|
4 | 4 | from datetime import datetime, timezone |
5 | 5 |
|
6 | 6 | from .listener import PowersensorListener |
| 7 | +from .xlatemsg import translate_raw_message |
7 | 8 |
|
8 | 9 | EXPIRY_CHECK_INTERVAL_S = 30 |
9 | 10 | EXPIRY_TIMEOUT_S = 5 * 60 |
@@ -55,75 +56,9 @@ async def yourcallback(event: dict) |
55 | 56 | { event: "device_lost", mac: "..." } |
56 | 57 |
|
57 | 58 |
|
| 59 | + Additionally, all events described in xlatemsg.translate_raw_message |
| 60 | + may be issued. The event name is inserted into the field 'event'. |
58 | 61 |
|
59 | | - The events below all have the following common fields: |
60 | | -
|
61 | | - { mac: "...", starttime_utc: X } |
62 | | - |
63 | | - and where applicable, also: |
64 | | -
|
65 | | - { via: "..." } |
66 | | -
|
67 | | - For brevity's sake they are not shown in the examples below, other |
68 | | - then simply as ... |
69 | | -
|
70 | | -
|
71 | | - battery_level: |
72 | | - The battery level of a sensor. |
73 | | -
|
74 | | - { ..., event: "battery_level", volts: X.Y } |
75 | | -
|
76 | | - voltage: |
77 | | - The mains voltage as detected by a plug. |
78 | | -
|
79 | | - { ..., event: "voltage", volts: X.Y } |
80 | | -
|
81 | | - average_power: |
82 | | - Reports the average power observed over the reporting duration. |
83 | | - May be negative for e.g. solar sensors and house sensors when |
84 | | - exporting solar to the grid. |
85 | | -
|
86 | | - The summation_joules field is a summation style register which |
87 | | - reports accumulated energy. This field is only useful for |
88 | | - calculating the delta of energy between two events. The counter |
89 | | - will reset to zero if the device is restarted, and is technically |
90 | | - subject to overflow, though that is unlikely to be reached. |
91 | | - The summation may be negative if solar export is present. The |
92 | | - summation may increment or decrement depending on whether energy |
93 | | - is being imported from or exported to the grid. |
94 | | -
|
95 | | - { ..., event: "average_power", |
96 | | - watts: X.Y, |
97 | | - durations_s: N.M, |
98 | | - summation_joules: J.K, |
99 | | - } |
100 | | -
|
101 | | - For reports from plugs, the following fields will also be present: |
102 | | -
|
103 | | - { |
104 | | - ..., |
105 | | - volts: X.Y, |
106 | | - current: C.D, |
107 | | - active_current: E.F, |
108 | | - reactive_current: G.H, |
109 | | - } |
110 | | -
|
111 | | - The (apparent) current, active_current and reactive_current fields |
112 | | - are all reported in a unit of Amperes. |
113 | | -
|
114 | | - uncalibrated_power: |
115 | | - Powersensors require calibrations of their readings before they |
116 | | - are able to be converted into a proper power reading. This event |
117 | | - is issued for sensor readings prior to such calibration completing. |
118 | | - The reported value has no inherent meaning beyond being an |
119 | | - indication of the strength of the signal seen by the sensor. It |
120 | | - is most definitely NOT in Watts. For most purposes, this event |
121 | | - can (and should be) ignored. |
122 | | -
|
123 | | - { ..., event: "uncalibrated_power", |
124 | | - value: Y.Z, |
125 | | - durations_s: N.M, |
126 | | - } |
127 | 62 |
|
128 | 63 | The start function returns the number of found gateway plugs. |
129 | 64 | Powersensor devices aren't found directly as they are typically not |
@@ -184,7 +119,8 @@ async def _on_msg(self, obj): |
184 | 119 | device.mark_active() |
185 | 120 |
|
186 | 121 | if self._event_cb and device.subscribed: |
187 | | - evs = self._mk_events(obj) |
| 122 | + relayer = obj.get('via') or mac |
| 123 | + evs = self._mk_events(obj, relayer) |
188 | 124 | if len(evs) > 0: |
189 | 125 | for ev in evs: |
190 | 126 | await self._event_cb(ev) |
@@ -217,85 +153,15 @@ async def _remove_device(self, mac): |
217 | 153 | } |
218 | 154 | await self._event_cb(ev) |
219 | 155 |
|
220 | | - ### Event formatting ### |
221 | | - |
222 | | - def _mk_events(self, obj): |
| 156 | + def _mk_events(self, obj, relayer): |
223 | 157 | evs = [] |
224 | | - typ = obj.get('type') |
225 | | - if typ == 'instant_power': |
226 | | - unit = obj.get('unit') |
227 | | - if unit == 'w' or unit == 'W': |
228 | | - evs.append(self._mk_average_power_event(obj)) |
229 | | - elif unit == 'l' or unit == 'L': |
230 | | - evs.append(self._mk_average_water_event(obj)) |
231 | | - elif unit == 'U': |
232 | | - evs.append(self._mk_uncalib_power_event(obj)) |
233 | | - elif unit == 'I': |
234 | | - pass # invalid data / sample failed |
235 | | - |
236 | | - if obj.get('voltage') is not None: |
237 | | - evs.append(self._mk_voltage_event(obj)) |
238 | | - |
239 | | - if obj.get('batteryMicrovolt') is not None: |
240 | | - evs.append(self._mk_battery_event(obj)) |
241 | | - else: |
242 | | - print(obj) |
243 | | - |
244 | | - for ev in evs: |
245 | | - ev['mac'] = obj.get('mac') |
246 | | - if obj.get('starttime'): |
247 | | - ev['starttime_utc'] = obj.get('starttime') |
248 | | - if obj.get('via'): |
249 | | - ev['via'] = obj.get('via') |
| 158 | + kvs = translate_raw_message(obj, relayer) |
| 159 | + for key, ev in kvs.items(): |
| 160 | + ev['event'] = key |
| 161 | + evs.append(ev) |
250 | 162 |
|
251 | 163 | return evs |
252 | 164 |
|
253 | | - def _mk_average_power_event(self, obj): |
254 | | - ev = { |
255 | | - 'event': 'average_power', |
256 | | - 'watts': obj.get('power'), |
257 | | - 'duration_s': obj.get('duration'), |
258 | | - 'summation_joules': obj.get('summation'), |
259 | | - } |
260 | | - if obj.get('device') == 'plug': |
261 | | - ev['volts'] = obj.get('voltage') |
262 | | - ev['current'] = obj.get('current') |
263 | | - ev['active_current'] = obj.get('active_current') |
264 | | - ev['reactive_current'] = obj.get('reactive_current') |
265 | | - if obj.get('role'): |
266 | | - ev['role'] = obj.get('role') |
267 | | - return ev |
268 | | - |
269 | | - def _mk_average_water_event(self, obj): |
270 | | - ev = { |
271 | | - 'event': 'average_water', |
272 | | - 'litres': obj.get('power'), |
273 | | - 'duration_s': obj.get('duration'), |
274 | | - 'summation_litres': obj.get('summation'), |
275 | | - } |
276 | | - return ev |
277 | | - |
278 | | - def _mk_uncalib_power_event(self, obj): |
279 | | - ev = { |
280 | | - 'event': 'uncalibrated_power', |
281 | | - 'value': obj.get('power'), |
282 | | - 'duration_s': obj.get('duration'), |
283 | | - } |
284 | | - return ev |
285 | | - |
286 | | - def _mk_voltage_event(self, obj): |
287 | | - return { |
288 | | - 'event': 'voltage', |
289 | | - 'volts': obj.get('voltage'), |
290 | | - } |
291 | | - |
292 | | - def _mk_battery_event(self, obj): |
293 | | - return { |
294 | | - 'event': 'battery_level', |
295 | | - 'volts': float(obj.get('batteryMicrovolt'))/1000000.0, |
296 | | - } |
297 | | - |
298 | | - |
299 | 165 | ### Supporting classes ### |
300 | 166 |
|
301 | 167 | class _Device: |
|
0 commit comments