Somewhat fancy voice command recognition software
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. # This is part of Kaylee
  2. # -- this code is licensed GPLv3
  3. # Copyright 2015-2016 Clayton G. Hobbs
  4. # Portions Copyright 2013 Jezra
  5. """Dark Sky weather plugin for Kaylee
  6. This plugin provides weather information `powered by Dark Sky
  7. <https://darksky.net/poweredby/>`__. The user must provide a key for
  8. the Dark Sky API, as well as decimal latitude and longitude::
  9. ".darksky": {
  10. "api_key": "USER_API_KEY",
  11. "latitude": USER_LATITUDE,
  12. "longitude": USER_LONGITUDE
  13. }
  14. """
  15. import json
  16. import os
  17. import time
  18. import requests
  19. from .pluginbase import PluginBase
  20. class Plugin(PluginBase):
  21. """Main class for the Dark Sky weather plugin"""
  22. def __init__(self, config, name):
  23. """Initialize the Dark Sky weather plugin"""
  24. super().__init__(config, name)
  25. self._cache_filename = os.path.join(config.cache_dir, 'darksky.json')
  26. self._weather_url = 'https://api.darksky.net/forecast/{}/{},{}'.format(
  27. self.options['api_key'],
  28. self.options['latitude'],
  29. self.options['longitude']
  30. )
  31. self._cache_max_age = 1800
  32. self.commands = {
  33. 'whats the temperature': self._temperature,
  34. 'whats todays low': self._todays_low,
  35. 'whats todays high': self._todays_high,
  36. 'whats tomorrows high': self._tomorrows_high,
  37. 'whats the humidity': self._relative_humidity,
  38. 'whats the weather': self._current_conditions,
  39. 'whens sunset': self._sunset_time,
  40. 'hoos your weather provider': self._provider_credits
  41. }
  42. self.corpus_strings.update(self.commands)
  43. def _temperature(self):
  44. return '{:.1f} degrees Fahrenheit'.format(
  45. self.cache['currently']['temperature'])
  46. def _todays_low(self):
  47. return '{:.1f} degrees Fahrenheit'.format(
  48. self.cache['daily']['data'][0]['temperatureMin'])
  49. def _todays_high(self):
  50. return '{:.1f} degrees Fahrenheit'.format(
  51. self.cache['daily']['data'][0]['temperatureMax'])
  52. def _tomorrows_high(self):
  53. return '{:.1f} degrees Fahrenheit'.format(
  54. self.cache['daily']['data'][1]['temperatureMax'])
  55. def _relative_humidity(self):
  56. return '{:.0f} percent'.format(
  57. 100 * self.cache['currently']['humidity'])
  58. def _current_conditions(self):
  59. return '{}, {}, relative humidity {}'.format(
  60. self.cache['currently']['summary'],
  61. self._temperature(),
  62. self._relative_humidity())
  63. def _sunset_time(self):
  64. # Get the time in a more useful structure
  65. t = time.localtime(self.cache['daily']['data'][0]['sunsetTime'])
  66. # Format each part of the time to be pronounced nicely
  67. hour = time.strftime('%I', t)
  68. if hour[0] == '0':
  69. hour = hour[1]
  70. minute = time.strftime('%M', t)
  71. if minute[0] == '0':
  72. if minute[1] == '0':
  73. minute = "o'clock"
  74. else:
  75. minute = 'O' + minute[1]
  76. ante_post = time.strftime('%p', t)
  77. if ante_post[0] == 'A':
  78. ante_post = 'AE ' + ante_post[1]
  79. # Put the parts together
  80. return '{} {} {}'.format(hour, minute, ante_post)
  81. def _provider_credits(self):
  82. return 'Powered by Dark Sky, https://darksky.net/poweredby/.'
  83. def _update_cache_if_stale(self):
  84. try:
  85. st = os.stat(self._cache_filename)
  86. except FileNotFoundError:
  87. # If there's no cache file, we need to get one
  88. self._update_cache()
  89. else:
  90. if time.time() - st.st_mtime > self._cache_max_age:
  91. self._update_cache()
  92. def _update_cache(self):
  93. r = requests.get(self._weather_url, stream=True)
  94. if r.status_code == 200:
  95. with open(self._cache_filename, 'wb') as f:
  96. for chunk in r:
  97. f.write(chunk)
  98. def _read_cache(self):
  99. with open(self._cache_filename, 'r') as f:
  100. return json.load(f)
  101. def recognizer_finished(self, recognizer, text):
  102. """Speak reasonably up-to-date weather information"""
  103. # Is there a matching command?
  104. if text in self.corpus_strings:
  105. self.emit('processed', text)
  106. self._update_cache_if_stale()
  107. self.cache = self._read_cache()
  108. self.emit('tts', self.commands[text]())
  109. return True
  110. else:
  111. return False