Browse Source

Update Dark Sky plugin for new API

This one got a lot more verbose, but I like it more too.  The caching
and formatting methods were moved to a base Handler class, and
individual commands subclass this using its methods as needed.  The
"hoos your weather provider" command doesn't even update the cache
anymore, which is nice.
Clara Hobbs 5 years ago
parent
commit
654d386e45
1 changed files with 135 additions and 69 deletions
  1. 135
    69
      kayleevc/plugins/darksky.py

+ 135
- 69
kayleevc/plugins/darksky.py View File

@@ -30,7 +30,7 @@ import time
30 30
 
31 31
 import requests
32 32
 
33
-from .pluginbase import PluginBase
33
+from . import PluginBase, Handler
34 34
 
35 35
 
36 36
 class Plugin(PluginBase):
@@ -56,51 +56,73 @@ class Plugin(PluginBase):
56 56
             self._temp_precision = 0
57 57
 
58 58
         self.commands = {
59
-            'whats the temperature': self._temperature,
60
-            'whats todays low': self._todays_low,
61
-            'whats todays high': self._todays_high,
62
-            'whats tomorrows high': self._tomorrows_high,
63
-            'whats the humidity': self._relative_humidity,
64
-            'whats the weather': self._current_conditions,
65
-            'whens sunset': self._sunset_time,
66
-            'hoos your weather provider': self._provider_credits
59
+            'whats the temperature': DarkSkyTemperatureHandler,
60
+            'whats todays high': DarkSkyTodaysHighHandler,
61
+            'whats todays low': DarkSkyTodaysLowHandler,
62
+            'whats tomorrows high': DarkSkyTomorrowsHighHandler,
63
+            'whats the humidity': DarkSkyHumidityHandler,
64
+            'whats the weather': DarkSkyCurrentConditionsHandler,
65
+            'whens sunset': DarkSkySunsetHandler,
66
+            'hoos your weather provider': DarkSkyProviderHandler
67 67
         }
68 68
 
69 69
         self.corpus_strings.update(self.commands)
70 70
 
71
-    def _format_temperature(self, temperature, unit='Fahrenheit'):
72
-        """Format a temperature for the TTS system"""
73
-        return '{:.{prec}f} degrees {}'.format(temperature, unit,
74
-                prec=self._temp_precision)
71
+    def get_handler(self, text):
72
+        """Return a handler if a recognized command is heard"""
73
+        if text in self.corpus_strings:
74
+            return self.commands[text](1, self._cache_filename,
75
+                    self._weather_url, self._cache_max_age,
76
+                    self._temp_precision)
77
+        else:
78
+            return None
75 79
 
76
-    def _temperature(self):
77
-        return self._format_temperature(self.cache['currently']['temperature'])
78 80
 
79
-    def _todays_low(self):
80
-        return self._format_temperature(
81
-            self.cache['daily']['data'][0]['temperatureMin'])
81
+class DarkSkyHandler(Handler):
82
+    """Base class for Dark Sky plugin handlers"""
82 83
 
83
-    def _todays_high(self):
84
-        return self._format_temperature(
85
-            self.cache['daily']['data'][0]['temperatureMax'])
84
+    def __init__(self, confidence, cache_filename, url, cache_max_age,
85
+            temp_precision):
86
+        super().__init__(confidence)
86 87
 
87
-    def _tomorrows_high(self):
88
-        return self._format_temperature(
89
-            self.cache['daily']['data'][1]['temperatureMax'])
88
+        self._cache_filename = cache_filename
89
+        self._url = url
90
+        self._cache_max_age = cache_max_age
91
+        self._temp_precision = temp_precision
90 92
 
91
-    def _relative_humidity(self):
92
-        return '{:.0f} percent'.format(
93
-            100 * self.cache['currently']['humidity'])
93
+    def _update_cache_if_stale(self):
94
+        try:
95
+            st = os.stat(self._cache_filename)
96
+        except FileNotFoundError:
97
+            # If there's no cache file, we need to get one
98
+            self._update_cache()
99
+        else:
100
+            if time.time() - st.st_mtime > self._cache_max_age:
101
+                self._update_cache()
94 102
 
95
-    def _current_conditions(self):
96
-        return '{}, {}, relative humidity {}'.format(
97
-            self.cache['currently']['summary'],
98
-            self._temperature(),
99
-            self._relative_humidity())
103
+    def _update_cache(self):
104
+        r = requests.get(self._url, stream=True)
105
+        if r.status_code == 200:
106
+            with open(self._cache_filename, 'wb') as f:
107
+                for chunk in r:
108
+                    f.write(chunk)
100 109
 
101
-    def _sunset_time(self):
102
-        # Get the time in a more useful structure
103
-        t = time.localtime(self.cache['daily']['data'][0]['sunsetTime'])
110
+    def _read_cache(self):
111
+        with open(self._cache_filename, 'r') as f:
112
+            return json.load(f)
113
+
114
+    def _format_temperature(self, temperature, unit='Fahrenheit'):
115
+        """Format a temperature for the TTS system"""
116
+        return '{:.{prec}f} degrees {}'.format(temperature, unit,
117
+                prec=self._temp_precision)
118
+
119
+    def _format_percent(self, value):
120
+        """Format a percentage value for the TTS system"""
121
+        return '{:.0f} percent'.format(100 * value)
122
+
123
+    def _format_time(self, epoch_time):
124
+        """Format a time in seconds since the epoch for the TTS system"""
125
+        t = time.localtime(epoch_time)
104 126
 
105 127
         # Format each part of the time to be pronounced nicely
106 128
         hour = time.strftime('%I', t)
@@ -121,41 +143,85 @@ class Plugin(PluginBase):
121 143
         # Put the parts together
122 144
         return '{} {} {}'.format(hour, minute, ante_post)
123 145
 
124
-    def _provider_credits(self):
125
-        return 'Powered by Dark Sky, https:// dark sky .net/ powered by/.'
146
+    def __call__(self, tts):
147
+        """Update the cache if necessary and load it"""
148
+        self._update_cache_if_stale()
149
+        self.cache = self._read_cache()
126 150
 
127
-    def _update_cache_if_stale(self):
128
-        try:
129
-            st = os.stat(self._cache_filename)
130
-        except FileNotFoundError:
131
-            # If there's no cache file, we need to get one
132
-            self._update_cache()
133
-        else:
134
-            if time.time() - st.st_mtime > self._cache_max_age:
135
-                self._update_cache()
136 151
 
137
-    def _update_cache(self):
138
-        r = requests.get(self._weather_url, stream=True)
139
-        if r.status_code == 200:
140
-            with open(self._cache_filename, 'wb') as f:
141
-                for chunk in r:
142
-                    f.write(chunk)
152
+class DarkSkyTemperatureHandler(DarkSkyHandler):
153
+    """Handler to speak the current temperature"""
143 154
 
144
-    def _read_cache(self):
145
-        with open(self._cache_filename, 'r') as f:
146
-            return json.load(f)
155
+    def __call__(self, tts):
156
+        """Speak the current temperature"""
157
+        super().__call__(tts)
158
+        tts(self._format_temperature(self.cache['currently']['temperature']))
147 159
 
148
-    def confidence(self, text):
149
-        """Return whether or not the command can be handled"""
150
-        return 1 if text in self.corpus_strings else 0
151
-
152
-    def handle(self, text):
153
-        """Speak reasonably up-to-date weather information"""
154
-        # Is there a matching command?
155
-        if self.confidence(text):
156
-            self._update_cache_if_stale()
157
-            self.cache = self._read_cache()
158
-            self.emit('tts', self.commands[text]())
159
-            return True
160
-        else:
161
-            return False
160
+
161
+class DarkSkyTodaysHighHandler(DarkSkyHandler):
162
+    """Handler to speak today's high temperature"""
163
+
164
+    def __call__(self, tts):
165
+        """Speak today's high"""
166
+        super().__call__(tts)
167
+        tts(self._format_temperature(
168
+            self.cache['daily']['data'][0]['temperatureMax']))
169
+
170
+
171
+class DarkSkyTodaysLowHandler(DarkSkyHandler):
172
+    """Handler to speak today's low temperature"""
173
+
174
+    def __call__(self, tts):
175
+        """Speak today's low"""
176
+        super().__call__(tts)
177
+        tts(self._format_temperature(
178
+            self.cache['daily']['data'][0]['temperatureMin']))
179
+
180
+
181
+class DarkSkyTomorrowsHighHandler(DarkSkyHandler):
182
+    """Handler to speak tomorrow's high temperature"""
183
+
184
+    def __call__(self, tts):
185
+        """Speak tomorrow's high"""
186
+        super().__call__(tts)
187
+        tts(self._format_temperature(
188
+            self.cache['daily']['data'][1]['temperatureMax']))
189
+
190
+
191
+class DarkSkyHumidityHandler(DarkSkyHandler):
192
+    """Handler to speak the current relative humidity"""
193
+
194
+    def __call__(self, tts):
195
+        """Speak the current relative humidity"""
196
+        super().__call__(tts)
197
+        tts(self._format_percent(self.cache['currently']['humidity']))
198
+
199
+
200
+class DarkSkyCurrentConditionsHandler(DarkSkyHandler):
201
+    """Handler to speak the current weather conditions"""
202
+
203
+    def __call__(self, tts):
204
+        """Speak the current weather conditions"""
205
+        super().__call__(tts)
206
+        tts('{}, {}, relative humidity {}'.format(
207
+            self.cache['currently']['summary'],
208
+            self._format_temperature(self.cache['currently']['temperature']),
209
+            self._format_percent(self.cache['currently']['humidity'])))
210
+
211
+
212
+class DarkSkySunsetHandler(DarkSkyHandler):
213
+    """Handler to speak the time of sunset"""
214
+
215
+    def __call__(self, tts):
216
+        """Speak the time of sunset"""
217
+        super().__call__(tts)
218
+        tts(self._format_time(self.cache['daily']['data'][0]['sunsetTime']))
219
+
220
+
221
+class DarkSkyProviderHandler(DarkSkyHandler):
222
+    """Handler to speak information about the weather provider"""
223
+
224
+    def __call__(self, tts):
225
+        """Speak information about the weather provider"""
226
+        # No need for super since this doesn't need any weather information
227
+        tts('Powered by Dark Sky, https:// dark sky .net/ powered by/.')

Loading…
Cancel
Save