· 6 years ago · Nov 18, 2019, 09:58 AM
1import argparse
2import datetime
3import json
4import sys
5import threading
6import time
7import urllib
8from binascii import unhexlify
9from functools import partial
10from struct import unpack
11
12import requests
13
14import dateutil.parser
15import txaio
16import websocket
17import yaml
18from autobahn.twisted.websocket import (
19 WebSocketClientFactory,
20 WebSocketClientProtocol,
21 connectWS,
22)
23from dateutil import tz
24from twisted.internet import reactor, ssl, task
25from twisted.internet.protocol import ReconnectingClientFactory
26from twisted.python import log
27
28
29class TracknetInfoClientProtocol(WebSocketClientProtocol):
30 stats = {}
31 config = {}
32
33 def onConnect(self, response):
34 log.msg("Tracknet info Server connected: {0}".format(response.peer))
35
36 def onOpen(self):
37 log.msg("Tracknet Info WebSocket connection open.")
38 query = {}
39 query["owner"] = self.owner
40 message = json.dumps(query)
41 self.sendMessage(message.encode("utf8"))
42 log.msg("tracknet info message sent: {0}".format(message))
43
44 def onMessage(self, payload, isBinary):
45 if isBinary:
46 log.msg(
47 "tracknet info Binary message received: {0} bytes: {1}".format(
48 len(payload), payload
49 )
50 )
51 else:
52 # log.msg("Text message received: {0}".format(payload.decode('utf8')))
53 response = json.loads(payload.decode("utf8"))
54 log.msg("Tracknet info payload: {0}".format(response))
55 for item in response["appx_list"]:
56 factory = MyClientFactory(
57 url=item["uri"], useragent="SimpleComplexWebSocketClient"
58 )
59 factory.protocol = TracknetDataClientProtocol
60 factory.protocolconfig = self.factory.protocolconfig
61 # factory.protocol.stats = stats
62 # factory.protocol.statsfile = statsfile
63 factory.setProtocolOptions(autoPingInterval=5, autoPingTimeout=2)
64 connectWS(factory)
65
66
67class TracknetDataClientProtocol(WebSocketClientProtocol):
68 stats = {}
69 config = {}
70
71 def onConnect(self, response):
72 log.msg("Tracknet data Server connected: {0}".format(response.peer))
73
74 def onOpen(self):
75 log.msg("Tracknet data WebSocket connection open.")
76
77 def onMessage(self, payload, isBinary):
78 if isBinary:
79 log.msg(
80 "Tracknet data Binary message received: {0} bytes: {1}".format(
81 len(payload), payload
82 )
83 )
84 else:
85 # log.msg("Text message received: {0}".format(payload.decode('utf8')))
86 response = json.loads(payload.decode("utf8"))
87 log.msg("Tracknet data payload: {0}".format(response))
88 if response["msgtype"] == "updf":
89 EUI = response["DevEui"].replace("-", "")
90 frameport = response.get("FPort", 1)
91 messagedata = response["FRMPayload"]
92 timestamp = response["ArrTime"]
93 parser = ColibirdParser()
94 parser.parse(
95 EUI,
96 messagedata,
97 timestamp,
98 self.factory.protocolconfig,
99 self.stats,
100 frameport,
101 )
102 else:
103 log.msg("Tracknet data ignoring message {0}".format(response))
104
105
106class LoriotClientProtocol(WebSocketClientProtocol):
107 stats = {}
108 config = {}
109
110 def onConnect(self, response):
111 log.msg("Loriot Server connected: {0}".format(response.peer))
112
113 def onOpen(self):
114 log.msg("Loriot WebSocket connection open.")
115 self.factory.resetDelay()
116 self.request_loriot_cache()
117
118 def request_loriot_cache(self):
119 if "last_timestamp" not in self.stats:
120 self.stats["last_timestamp"] = 0
121 query = {}
122 query["cmd"] = "cq"
123 if args.debug:
124 query["filter"] = {"EUI": args.debug}
125 else:
126 query["filter"] = {"from": self.stats["last_timestamp"]}
127 message = json.dumps(query)
128 # log.msg(message)
129 self.sendMessage(message.encode("utf8"))
130 log.msg("Loriot message sent: {0}".format(message))
131
132 def onMessage(self, payload, isBinary):
133 if isBinary:
134 log.msg(
135 "Loriot Binary message received: {0} bytes: {1}".format(
136 len(payload), payload
137 )
138 )
139 else:
140 # log.msg("Text message received: {0}".format(payload.decode('utf8')))
141 response = json.loads(payload.decode("utf8"))
142 # log.msg(response)
143 self.handle_loriot_message(response)
144
145 def handle_loriot_message(self, message, acceptgw=False):
146 if "total_messages" not in self.stats:
147 self.stats["total_messages"] = 0
148 self.stats["total_messages"] += 1
149
150 if message["cmd"] == "cq":
151 # cache query -> handle the cached messages individually
152 log.msg("message type cq with {0} objects".format(len(message["cache"])))
153 # sort the messages in the cache by timestamp ascending so we get them and post them in their original order
154 for msg in sorted(message["cache"], key=lambda message: message["ts"]):
155 # after "cq" premium accounts get only the "gw" messages, we need to accept these now
156 self.handle_loriot_message(msg, True)
157 elif message["cmd"] == "rx" or (acceptgw and message["cmd"] == "gw"):
158 # "normal" packet received
159 # premium loriot.io accounts get all messages twice: first as "rx" and a few seconds later as "gw" with additional metadata, when getting messages from the cache only the "gw" messages are returned
160 # to not import everything twice we ignore the "gw" messages - except after "cq" cache query
161 if vars(args).get("debug", False) and message["EUI"] != args.debug:
162 # if we are debugging ignore other devices
163 return
164
165 parser = ColibirdParser()
166 parser.parse(
167 message["EUI"],
168 message["data"],
169 message["ts"] / 1000,
170 self.factory.protocolconfig,
171 self.stats,
172 )
173
174 # update the last timestamp seen to not repeatedly request stuff
175 # if not args.debug:
176 # self.stats["last_timestamp"] = max(
177 # self.stats["last_timestamp"], message["ts"]
178 # )
179 # self.statsfile.seek(0)
180 # self.statsfile.truncate(0)
181 # yaml.safe_dump(self.stats, self.statsfile, default_flow_style=False)
182 # self.statsfile.flush()
183 # else:
184 log.msg(
185 "Loriot received unknown message type {1}: {0}".format(
186 message, message["cmd"]
187 )
188 )
189
190 def onClose(self, wasClean, code, reason):
191 log.msg("Loriot WebSocket connection closed: {0}".format(reason))
192
193
194class ColibirdParser:
195 def parse(self, EUI, messagedata, timestamp, config, stats, frameport):
196 self.config = config
197 self.stats = stats
198 if EUI in config.get("meters", {}):
199 if not messagedata:
200 # empty data
201 log.msg("Colibird parser: Empty data ignored: {0}".format(EUI))
202 return
203
204 data = {}
205 data["EUI"] = EUI
206 data["data"] = messagedata
207 data["frameport"] = frameport
208 # log.msg("Frameport is still: {0}".format(frameport))
209
210 if isinstance(timestamp, datetime.datetime):
211 data["datetime"] = timestamp
212 else:
213 data["datetime"] = datetime.datetime.fromtimestamp(timestamp)
214 data.update(
215 self.config["meters"][data["EUI"]]
216 ) # add all the info we have from config
217
218 # normalize key names from colibird-deviceapi
219 if "DevId" in data:
220 data["DevID"] = data["DevId"]
221 if "SubDevId" in data:
222 data["SubDevID"] = data["SubDevId"]
223
224 # start filling in generic info to post to colibird
225 postdata = {}
226 postdata["Passwd"] = data["Passwd"].encode("iso-8859-1")
227 postdata["unit"] = data.get("unit", "").encode("iso-8859-1")
228 postdata["DevID"] = data.get("DevID", "")
229 postdata["SubDevID"] = data.get("SubDevID", "")
230 postdata["text"] = "LoRaWAN EUI={0} DevID={1} type={2}".format(
231 data["EUI"], data["DevID"], data["device-type"]
232 ).encode("iso-8859-1")
233 postdata["ident"] = data["EUI"].encode("iso-8859-1")
234 postdata["time"] = (
235 data["datetime"].strftime("%Y-%m-%d %H:%M:%S").encode("iso-8859-1")
236 )
237
238 # update device statistics
239 log.msg("got data: {0}".format(data))
240 if data["device-type"] not in self.stats:
241 self.stats[data["device-type"]] = 0
242 self.stats[data["device-type"]] += 1
243
244 # list of devices we know
245 if data["device-type"] == "arf8045":
246 # http://www.adeunis-rf.com/media/downloads/184/trad_file/eng/arf8045_adeunis_lorawan_sensors_user_guide_v1.1_fr_gb.pdf
247 arf8045_types = {
248 0: "switch",
249 1: "auto",
250 2: "TOR",
251 10: "temperature",
252 11: "4-20mA",
253 12: "0-10V",
254 }
255 data["code"] = int(data["data"][0:2], 16)
256 data["frame_count"] = int(data["data"][2:3], 16)
257 data["status"] = int(data["data"][3:4], 16)
258 data["config_done"] = bool(data["status"] & 1)
259 data["low_battery"] = bool(data["status"] & 2)
260 data["config_switch_error"] = bool(data["status"] & 4)
261 data["hardware_error"] = bool(data["status"] & 8)
262 if data["code"] == 1:
263 # meter data
264 data["channel1_type"] = arf8045_types[int(data["data"][4:6], 16)]
265 # the meter sends 3-byte (24bit) values, so we add a '00' MSB to let unpack parse it as 4-byte unsigned integer
266 data["channel1_value"] = unpack(
267 "<I", unhexlify(data["data"][6:12] + "00")
268 )[0]
269 data["channel2_type"] = arf8045_types[int(data["data"][12:14], 16)]
270 # the meter sends 3-byte (24bit) values, so we add a '00' MSB to let unpack parse it as 4-byte unsigned integer
271 data["channel2_value"] = unpack(
272 "<I", unhexlify(data["data"][14:20] + "00")
273 )[0]
274 elif data["code"] == 3:
275 # configuration
276 data["device_type"] = int(data["data"][4:6], 16)
277 data["transmit_period"] = int(data["data"][6:10], 16)
278 data["channels_enabled"] = int(data["data"][10:12], 16)
279 data["channel1_type"] = arf8045_types[int(data["data"][12:14], 16)]
280 data["channel2_type"] = arf8045_types[int(data["data"][14:16], 16)]
281 data["input_type"] = int(data["data"][16:18], 16)
282 data["memo_switch"] = data["data"][18:20]
283 else:
284 log.msg(
285 "unknown arf8045 message code {0} in {1}".format(
286 data["status"], data
287 )
288 )
289
290 elif data["device-type"] == "arf8046":
291 # spec: http://www.adeunis-rf.com/media/downloads/183/trad_file/eng/arf8046xx_adeunis_lorawan_pulse_user_guide_v1.1_fr_gb.pdf
292 arf8046_types = {
293 0: "undefined",
294 3: "auto",
295 4: "gas",
296 5: "electricity",
297 6: "water",
298 7: "water",
299 8: "water",
300 9: "temperature",
301 }
302
303 data["code"] = int(data["data"][0:2], 16)
304 data["frame_count"] = int(data["data"][2:3], 16)
305 data["status"] = int(data["data"][3:4], 16)
306 data["config_done"] = bool(data["status"] & 1)
307 data["low_battery"] = bool(data["status"] & 2)
308 data["config_switch_error"] = bool(data["status"] & 4)
309 data["hardware_error"] = bool(data["status"] & 8)
310 if data["code"] == 2:
311 # metering data
312 data["pulse1_type"] = arf8046_types[int(data["data"][4:6], 16)]
313 data["pulse1_count"] = unpack("<I", unhexlify(data["data"][6:14]))[
314 0
315 ]
316 data["pulse2_type"] = arf8046_types[int(data["data"][14:16], 16)]
317 data["pulse2_count"] = unpack("<I", unhexlify(data["data"][16:24]))[
318 0
319 ]
320
321 # post data to colibird
322 postdata[
323 "text"
324 ] = "LoRaWAN EUI={0} DevID={1} type={2} channel={3}".format(
325 data["EUI"], data["DevID"], data["device-type"], 1
326 ).encode(
327 "iso-8859-1"
328 )
329 if "factor" in data:
330 postdata["value"] = data["pulse1_count"] * data["factor"]
331 else:
332 postdata["value"] = data["pulse1_count"]
333 postdata["media"] = data["pulse1_type"]
334 if "media" in data:
335 postdata["media"] = data["media"]
336 self.colibird_post(postdata)
337 if "send2" in data and bool(data["send2"]):
338 postdata["DevID"] = int(postdata["DevID"]) + 1
339 postdata[
340 "text"
341 ] = "LoRaWAN EUI={0} DevID={1} type={2} channel={3}".format(
342 data["EUI"], postdata["DevID"], data["device-type"], 2
343 ).encode(
344 "iso-8859-1"
345 )
346 if "factor2" in data:
347 postdata["value"] = data["pulse2_count"] * data["factor2"]
348 else:
349 postdata["value"] = data["pulse2_count"]
350 postdata["media"] = data["pulse2_type"]
351 if "media2" in data:
352 postdata["media"] = data["media2"]
353 self.colibird_post(postdata)
354 elif data["code"] == 3:
355 # device info
356 data["device_type"] = int(data["data"][4:6], 16)
357 data["transmit_period"] = unpack(
358 "<H", unhexlify(data["data"][6:10])
359 )[0]
360 data["channels_enabled"] = int(data["data"][10:12], 16)
361 data["pulse1_type"] = arf8046_types[int(data["data"][12:14], 16)]
362 data["pulse2_type"] = arf8046_types[int(data["data"][14:16], 16)]
363 data["input_type"] = int(data["data"][16:18], 16)
364 data["memo_switch"] = data["data"][18:20]
365 elif data["code"] == 4:
366 # pulse1 info
367 data["pulse1_weight"] = unpack("<H", unhexlify(data["data"][4:8]))[
368 0
369 ]
370 data["pulse1_offset"] = unpack("<I", unhexlify(data["data"][8:16]))[
371 0
372 ]
373 # data['pulse1_memo'] = unpack("<L",unhexlify(data['data'][16:24]))[0]
374 # the memo bytes are not being sent with our devices -> ignore
375 elif data["code"] == 5:
376 # pulse2 info
377 data["pulse2_weight"] = unpack("<H", unhexlify(data["data"][4:8]))[
378 0
379 ]
380 data["pulse2_offset"] = unpack("<I", unhexlify(data["data"][8:16]))[
381 0
382 ]
383 # data['pulse2_memo'] = unpack("<L",unhexlify(data['data'][16:24]))[0]
384 # the memo bytes are not being sent with our devices -> ignore
385 else:
386 log.msg(
387 "unknown arf8046 message code {0} in {1}".format(
388 data["status"], data
389 )
390 )
391 elif data["device-type"] == "arf8084":
392 # http://www.adeunis-rf.com/media/downloads/185/trad_file/eng/ug_lorawan_demonstrator_user_v1.1.pdf
393 data["status"] = int(data["data"][0:2], 16)
394 offset = 2
395 data["temperature_present"] = bool(data["status"] & 128)
396 if data["temperature_present"]:
397 data["temperature"] = unpack(
398 "b", unhexlify(data["data"][offset : (offset + 2)])
399 )[0]
400 offset += 2
401 data["accelerometer_triggered"] = bool(data["status"] & 64)
402 data["button_triggered"] = bool(data["status"] & 32)
403 data["gps_present"] = bool(data["status"] & 16)
404 if data["gps_present"]:
405 data["latitude"] = int(
406 data["data"][offset : (offset + 2)]
407 ) # full degrees (2 digits)
408 data["latitude"] += (
409 int(data["data"][(offset + 2) : (offset + 7)]) / 1000 / 60
410 ) # convert 5 digits nn.nnn minutes to 0.x degrees and add
411 if bool(
412 int(data["data"][(offset + 7) : (offset + 8)], 16) & 1
413 ): # if the last bit is set the coordinates are south -> negative coordinates
414 data["latitude"] *= -1
415 data["longitude"] = int(
416 data["data"][(offset + 8) : (offset + 11)]
417 ) # full degrees (3 digits)
418 data["longitude"] += (
419 int(data["data"][(offset + 11) : (offset + 15)]) / 100 / 60
420 ) # convert 4 digits nn.nn minutes to 0.x degrees and add
421 if bool(
422 int(data["data"][(offset + 15) : (offset + 16)], 16) & 1
423 ): # if the last bit is set the coordinates are west -> negative coordinates
424 data["longitude"] *= -1
425 offset += 16
426 data["up_present"] = bool(data["status"] & 8)
427 if data["up_present"]:
428 data["uplink_frames"] = int(data["data"][offset : offset + 2], 16)
429 offset += 2
430 data["down_present"] = bool(data["status"] & 4)
431 if data["down_present"]:
432 data["downlink_frames"] = int(data["data"][offset : offset + 2], 16)
433 offset += 2
434 data["voltage_present"] = bool(data["status"] & 2)
435 if data["voltage_present"]:
436 data["battery_voltage"] = (
437 unpack(">H", unhexlify(data["data"][offset : (offset + 4)]))[0]
438 / 1000
439 )
440 offset += 4
441 data["signal_present"] = bool(data["status"] & 1)
442 if data["signal_present"]:
443 data["device_rssi"] = "{0}dBm".format(
444 int(data["data"][offset : offset + 2], 16) * -1
445 )
446 data["device_snr"] = "{0}dB".format(
447 unpack(
448 "b", unhexlify(data["data"][(offset + 2) : (offset + 4)])
449 )[0]
450 )
451
452 # submit test data for this test device
453 postdata["unit"] = "°c"
454 postdata["media"] = "tempR"
455 if "temperature" in data:
456 postdata["value"] = data["temperature"]
457 else:
458 postdata["value"] = 0
459 self.colibird_post(postdata)
460 postdata["DevID"] = int(postdata["DevID"]) + 1
461 if "battery_voltage" in data:
462 postdata["value"] = data["battery_voltage"] * 10
463 else:
464 postdata["value"] = 0
465 if (
466 postdata["value"] < 100
467 ): # sometimes the battery voltage is ffff = 65.535, filtering here if >10 for a 3.6V battery
468 self.colibird_post(postdata)
469
470 elif data["device-type"] == "ewr10000":
471 data["value"] = postdata["value"] = unpack(
472 "<f", unhexlify(data["data"])
473 )[0]
474 postdata["media"] = data["media"]
475 self.colibird_post(postdata)
476
477 elif data["device-type"] == "ewr10001":
478 data["value"] = postdata["value"] = unpack(
479 ">f", unhexlify(data["data"][8:16])
480 )[0]
481 postdata["media"] = data["media"]
482 postdata[
483 "text"
484 ] = "LoRaWAN EUI={0} DevID={1} type={2} channel={3}".format(
485 data["EUI"], data["DevID"], data["device-type"], 1
486 ).encode(
487 "iso-8859-1"
488 )
489 self.colibird_post(postdata)
490 data["value"] = postdata["value"] = unpack(
491 ">f", unhexlify(data["data"][16:24])
492 )[0]
493 postdata["media"] = data["media"]
494 postdata["DevID"] = postdata["DevID"] + 1
495 postdata[
496 "text"
497 ] = "LoRaWAN EUI={0} DevID={1} type={2} channel={3}".format(
498 data["EUI"], data["DevID"], data["device-type"], 2
499 ).encode(
500 "iso-8859-1"
501 )
502 self.colibird_post(postdata)
503
504 elif data["device-type"] == "ewr10002":
505 # first 4 bytes is timestamp
506 data["datetime"] = datetime.datetime.fromtimestamp(
507 unpack(">I", unhexlify(data["data"][0:8]))[0]
508 )
509 postdata["time"] = (
510 data["datetime"].strftime("%Y-%m-%d %H:%M:%S").encode("iso-8859-1")
511 )
512
513 # then 4*4 bytes registers
514 data["bezug1"] = postdata["value"] = (
515 unpack(">I", unhexlify(data["data"][8:16]))[0] / 1000
516 )
517 postdata["media"] = data["media"]
518 postdata[
519 "text"
520 ] = "LoRaWAN EUI={0} DevID={1} type={2} channel={3}".format(
521 data["EUI"], data["DevID"], data["device-type"], 1
522 ).encode(
523 "iso-8859-1"
524 )
525 self.colibird_post(postdata)
526
527 data["bezug2"] = postdata["value"] = (
528 unpack(">I", unhexlify(data["data"][16:24]))[0] / 1000
529 )
530 postdata["media"] = data["media"]
531 postdata["DevID"] = postdata["DevID"] + 1
532 postdata[
533 "text"
534 ] = "LoRaWAN EUI={0} DevID={1} type={2} channel={3}".format(
535 data["EUI"], data["DevID"], data["device-type"], 2
536 ).encode(
537 "iso-8859-1"
538 )
539 self.colibird_post(postdata)
540
541 if len(data["data"]) > 24: # winterthur only sends 2 registers...
542 data["lieferung1"] = postdata["value"] = (
543 unpack(">I", unhexlify(data["data"][24:32]))[0] / 1000
544 )
545 postdata["media"] = data["media"]
546 postdata["DevID"] = postdata["DevID"] + 1
547 postdata[
548 "text"
549 ] = "LoRaWAN EUI={0} DevID={1} type={2} channel={3}".format(
550 data["EUI"], data["DevID"], data["device-type"], 3
551 ).encode(
552 "iso-8859-1"
553 )
554 self.colibird_post(postdata)
555
556 data["lieferung2"] = postdata["value"] = (
557 unpack(">I", unhexlify(data["data"][32:40]))[0] / 1000
558 )
559 postdata["media"] = data["media"]
560 postdata["DevID"] = postdata["DevID"] + 1
561 postdata[
562 "text"
563 ] = "LoRaWAN EUI={0} DevID={1} type={2} channel={3}".format(
564 data["EUI"], data["DevID"], data["device-type"], 4
565 ).encode(
566 "iso-8859-1"
567 )
568 self.colibird_post(postdata)
569
570 data["spannung1"] = unpack(">B", unhexlify(data["data"][40:42]))[0]
571 data["spannung2"] = unpack(">B", unhexlify(data["data"][42:44]))[0]
572 data["spannung3"] = unpack(">B", unhexlify(data["data"][44:46]))[0]
573 data["wirkleistung1"] = (
574 unpack(">H", unhexlify(data["data"][46:50]))[0] / 100
575 )
576 data["wirkleistung2"] = (
577 unpack(">H", unhexlify(data["data"][50:54]))[0] / 100
578 )
579 data["wirkleistung3"] = (
580 unpack(">H", unhexlify(data["data"][54:58]))[0] / 100
581 )
582
583 elif data["device-type"] == "senlabt":
584 # https://drive.google.com/file/d/0B78f0_ga-9v-SVVGSWdzM3JHYVE/view
585 # 01fc8b1c9c10017b
586 data["code"] = int(data["data"][0:2], 16)
587 if data["code"] == 1:
588 data["battery_percentage"] = int(data["data"][2:4], 16) / 254
589 postdata["value"] = data["temperature"] = (
590 unpack(">h", unhexlify(data["data"][-4:]))[0] / 16
591 )
592 postdata["media"] = data["media"]
593 self.colibird_post(postdata)
594 else:
595 log.msg(
596 "unknown senlabt message code {0} in {1}".format(
597 data["code"], data
598 )
599 )
600
601 elif data["device-type"] == "senlabm":
602 # https://drive.google.com/file/d/0B78f0_ga-9v-SVVGSWdzM3JHYVE/view
603 # 02f7946e9c1000000018
604 data["code"] = int(data["data"][0:2], 16)
605 if data["code"] == 2:
606 data["battery_percentage"] = int(data["data"][2:4], 16) / 254
607 postdata["value"] = data["impulses"] = unpack(
608 ">H", unhexlify(data["data"][-4:])
609 )[0]
610 postdata["media"] = data["media"]
611 self.colibird_post(postdata)
612 else:
613 log.msg(
614 "unknown senlabm message code {0} in {1}".format(
615 data["code"], data
616 )
617 )
618
619 elif data["device-type"] == "tetraedre":
620 # http://www.tetraedre.com/publication.php?publication_id=169&book_id=7&chapter_id=12
621 data["header_main"] = int(data["data"][0:2], 16)
622 offset = 2
623 chunk = 0
624 data["chunk"] = []
625 while offset < len(data["data"]):
626 chunkdata = {}
627 chunkdata["header"] = int(data["data"][offset : offset + 2], 16)
628 offset += 2
629 if chunkdata["header"] > 0 and chunkdata["header"] < 128:
630 # Chunk type A: 2 bytes MSB first
631 chunkdata["data"] = unhexlify(data["data"][offset : offset + 4])
632 offset += 4
633 if chunkdata["header"] == 1:
634 # Temperature. signed integer 16bits. 1 LSB = 0.01°C: 0: 0°C; 1: +0.01°C; 0xFFFF : -0.01°C
635 chunkdata["type"] = "temperature"
636 chunkdata["unit"] = "°c"
637 chunkdata["value"] = (
638 unpack(">h", chunkdata["data"])[0] / 100
639 )
640 elif chunkdata["header"] == 2:
641 # Relative Humidity. unsigned integer 16bits. 1 LSB = 0.01°RH
642 chunkdata["type"] = "humidity"
643 chunkdata["unit"] = "RH"
644 chunkdata["value"] = (
645 unpack(">H", chunkdata["data"])[0] / 100
646 )
647 elif chunkdata["header"] == 3:
648 # Oxygen concentration. 1 LSB = 0.001%
649 chunkdata["type"] = "oxygen"
650 chunkdata["unit"] = "%"
651 chunkdata["value"] = (
652 unpack(">H", chunkdata["data"])[0] / 1000
653 )
654 elif chunkdata["header"] == 4:
655 # CO2 concentration. 1 LSB = 0.001%
656 chunkdata["type"] = "CO2"
657 chunkdata["unit"] = "%"
658 chunkdata["value"] = (
659 unpack(">H", chunkdata["data"])[0] / 1000
660 )
661 elif chunkdata["header"] == 5:
662 # Second Temperature. signed integer 16bits. 1 LSB = 0.01°C: 0: 0°C; 1: +0.01°C; 0xFFFF : -0.01°C
663 chunkdata["type"] = "temperature2"
664 chunkdata["unit"] = "°c"
665 chunkdata["value"] = (
666 unpack(">h", chunkdata["data"])[0] / 100
667 )
668 elif chunkdata["header"] == 6:
669 # Pressure. unsigned integer 16bits. 1 LSB = 0.5mbar
670 chunkdata["type"] = "pressure"
671 chunkdata["unit"] = "mbar"
672 chunkdata["value"] = unpack(">H", chunkdata["data"])[0] / 2
673 elif chunkdata["header"] in range(20, 24):
674 # DIF/VIF for MBUS data
675 chunkdata["type"] = "difvif"
676 chunkdata["unit"] = ""
677 difvif = unpack(">BB", chunkdata["data"])
678 chunkdata["dif"] = difvif[0]
679 chunkdata["vif"] = difvif[1]
680 self.mbus_difvif_parse(chunkdata)
681 else:
682 log.msg(
683 "unknown tetaedre chunk code {0} in chunk {1}: {2}".format(
684 chunkdata["header"], chunk, chunkdata["data"]
685 )
686 )
687 elif chunkdata["header"] >= 128 and chunkdata["header"] < 192:
688 # Chunk type B: 4 bytes MSB first
689 chunkdata["data"] = unhexlify(data["data"][offset : offset + 8])
690 offset += 8
691 if chunkdata["header"] == 128:
692 # timestamp of the measurement. unsigned integer 32 bits. UNIX timestamp
693 chunkdata["type"] = "timestamp"
694 chunkdata["unit"] = ""
695 chunkdata["value"] = datetime.datetime.fromtimestamp(
696 unpack(">I", chunkdata["data"])[0]
697 )
698 elif chunkdata["header"] == 129:
699 # Main energy index. IEEE754 floating point. Unit : kWh for electricty meters (register 1.8.0), m3 for water meter, m3 for uncorrected gaz meters
700 chunkdata["type"] = "energy"
701 chunkdata["unit"] = ""
702 chunkdata["value"] = unpack(">f", chunkdata["data"])[0]
703 elif chunkdata["header"] == 130:
704 # Serial number. unsigned integer 32bits. Serial number of the meter
705 chunkdata["type"] = "serial"
706 chunkdata["unit"] = ""
707 chunkdata["value"] = unpack(">I", chunkdata["data"])[0]
708 elif chunkdata["header"] == 131:
709 # First tariff energy index. IEEE754 floating point. Unit : kWh for electricty meters (register 1.8.1)
710 chunkdata["type"] = "energy1"
711 chunkdata["unit"] = ""
712 chunkdata["value"] = unpack(">f", chunkdata["data"])[0]
713 elif (
714 data.get("tetraedre-parse-override", False)
715 and chunkdata["header"] == 132
716 ):
717 # original tetraedre specification
718 # First tariff energy index. IEEE754 floating point. Unit : kWh for electricty meters (register 1.8.2)
719 postdata["type"] = chunkdata["type"] = "energy2"
720 postdata["unit"] = chunkdata["unit"] = data.get(
721 "unit", "kWh"
722 )
723 postdata["media"] = chunkdata["media"] = data.get(
724 "media", "electricity"
725 )
726 postdata["value"] = chunkdata["value"] = unpack(
727 ">I", chunkdata["data"]
728 )[0] * data.get("factor", 1)
729 postdata[
730 "text"
731 ] = "LoRaWAN EUI={0} DevID={1} type={2} register={3}".format(
732 data["EUI"],
733 data["DevID"],
734 data["device-type"],
735 postdata["type"],
736 ).encode(
737 "iso-8859-1"
738 )
739 self.colibird_post(postdata)
740 postdata["DevID"] += 1
741 elif chunkdata["header"] in range(132, 136):
742 # aquametro specification
743 if chunkdata["header"] == 132:
744 postdata["type"] = "heat"
745 elif chunkdata["header"] == 133:
746 postdata["type"] = "water"
747 elif chunkdata["header"] in range(134, 136):
748 postdata["type"] = "tempR"
749
750 # data chunk 0x84 (=132) -> depends on data chunk 0x14 (20) for parsing (difference in header=112)
751 infochunk = None
752 for c in data["chunk"]:
753 if c.get("header", 0) == (chunkdata["header"] - 112):
754 infochunk = c
755 break
756 if infochunk == None:
757 log.msg(
758 "no infochunk received with metadata about decoding"
759 )
760 log.msg(
761 "chunk:{0}, offset:{1}/{3}, data:{2}".format(
762 chunk + 1, offset, chunkdata, len(data["data"])
763 )
764 )
765 continue
766
767 postdata["unit"] = chunkdata["unit"] = infochunk.get(
768 "unit", data["unit"]
769 )
770 if infochunk["dif"] == 4:
771 postdata["value"] = chunkdata["value"] = (
772 unpack("<i", chunkdata["data"])[0]
773 * infochunk["factor"]
774 )
775 elif infochunk["dif"] == 5:
776 postdata["value"] = chunkdata["value"] = (
777 unpack("<f", chunkdata["data"])[0]
778 * infochunk["factor"]
779 )
780 else:
781 log.msg(
782 "unknown DIF {0} in data: {1}".format(
783 infochunk["dif"], data
784 )
785 )
786 postdata[
787 "text"
788 ] = "LoRaWAN EUI={0} DevID={1} type={2} register={3}".format(
789 data["EUI"],
790 data["DevID"],
791 data["device-type"],
792 postdata["type"],
793 ).encode(
794 "iso-8859-1"
795 )
796 self.colibird_post(postdata)
797 postdata["DevID"] += 1
798 else:
799 log.msg(
800 "unknown tetaedre chunk code {0} in chunk {1}: {2}".format(
801 chunkdata["header"], chunk, chunkdata["data"]
802 )
803 )
804 elif chunkdata["header"] >= 192 and chunkdata["header"] < 255:
805 # Chunk type C: variable size, first byte is size
806 datalength = int(data["data"][offset : offset + 2], 16)
807 offset += 2
808 chunkdata["data"] = unhexlify(
809 data["data"][offset : offset + (datalength * 2)]
810 ) # 2 chars per byte -> 4 byte = 8 chars need to be parsed
811 offset += datalength * 2
812 if chunkdata["header"] == 192:
813 # ZMD410 profil. First is timestamp (32bits unix) followed by either 1, 2 or 3 float16 values. The values definition and number are dependant of the ZMD410 configuration. Their encoding is float16 (see below). The first value represents to the first field of the profil, the second value (if any) represents the second field,...
814 log.msg(
815 "ignoring ZMD410 data for now: {0}".format(
816 chunkdata["data"]
817 )
818 )
819 elif chunkdata["header"] == 200:
820 # M-BUS data chunk.
821 log.msg(
822 "ignoring MBUS data for now: {0}".format(
823 chunkdata["data"]
824 )
825 )
826 elif chunkdata["header"] == 201:
827 # Avectris Tetraedre config: c9 04 85990900
828 # 0xc9: 201: Wert der kommt ist ein Volumen gemäss Beschreibung
829 # 0x04: Anzahl Byte für den Wert
830 # 0x85990900: MBus Decoding 629.125
831 postdata["value"] = chunkdata["value"] = (
832 unpack("<I", chunkdata["data"])[0]
833 * data.get("factor", 1)
834 / 1000
835 )
836 postdata[
837 "text"
838 ] = "LoRaWAN EUI={0} DevID={1} type={2}".format(
839 data["EUI"], data["DevID"], data["device-type"]
840 ).encode(
841 "iso-8859-1"
842 )
843 postdata["media"] = data["media"]
844 self.colibird_post(postdata)
845 postdata["DevID"] += 1
846 else:
847 log.msg(
848 "unknown tetaedre chunk code {0} in chunk {1}: {2}".format(
849 chunkdata["header"], chunk, chunkdata["data"]
850 )
851 )
852 else:
853 # 0x0 or 0xff are reserved. ignore
854 log.msg(
855 "unknown tetaedre chunk code {0} in chunk {1}: {2}".format(
856 chunkdata["header"], chunk, chunkdata["data"]
857 )
858 )
859
860 chunk += 1
861 data["chunk"].append(chunkdata)
862 log.msg(
863 "chunk:{0}, offset:{1}/{3}, data:{2}".format(
864 chunk, offset, chunkdata, len(data["data"])
865 )
866 )
867 # endwhile
868 elif data["device-type"] == "IRHead_Proto1":
869 fields = unhexlify(data["data"]).decode("utf-8").splitlines()
870 # -------------
871 # 1.8.1(0027603*kWh)
872 # 1.8.2(0013127*kWh)
873 # 1.8.0(0040730*kWh)
874 r = re.compile(r".*\((\d+)")
875 for i in range(1, 3): # items 1 and 2 only
876 postdata["value"] = data["value"] = int(r.match(fields[i]).group(1))
877 postdata["SubDevID"] = data["SubDevID"] = i - 1
878 self.colibird_post(postdata)
879
880 elif data["device-type"] == "IRH-AVECV2":
881 # 01234567 890123 456789 012345 678901 234567 890123 456789
882 # 70B3D5FFFE0E8F86 01dcf2e9 0054fd 0026e8 007be6 000000 000000 000000 000000
883 # byte char description
884 # 0-3 0-7 P1: Zählernummer (4 bytes unsigned int MSB)
885 # 4-6 8-13 P2: Hochtarif (3 bytes unsigned int MSB)
886 # 7-9 14-19 P3: Niedertarif (3 bytes unsigned int MSB)
887 # 10-12 20-25 P4: Summe Hochtarif / Niedertairf (3 bytes unsigned int MSB)
888 # 13-15 26-31 P5: Hochtarif Einspeisung (3 bytes unsigned int MSB)
889 # 16-18 32-37 P6: Niedertarif Einspeisung (3 bytes unsigned int MSB)
890 # 19-21 38-43 P7: Rückgestelltes Register Hochtarif (3 bytes unsigned int MSB)
891 # 22-24 44-49 P8: Rückgestelltes Register Niedertarif (3 bytes unsigned int MSB)
892
893 if len(data["data"]) < 8:
894 log.msg(
895 "IRH-AVECV2 {1} bytes received: {1}".format(
896 len(data["data"]), data
897 )
898 )
899 return
900
901 data["serial"] = unpack(">I", unhexlify(data["data"][0:8]))[0]
902 postdata["media"] = data.get("media", "")
903 postdata["DevID"] = data["DevID"]
904 postdata["SubDevID"] = data["SubDevID"]
905
906 if len(data["data"]) < 14:
907 log.msg(
908 "IRH-AVECV2 {1} bytes received: {1}".format(
909 len(data["data"]), data
910 )
911 )
912 return
913
914 data["bezug1"] = unpack(">I", unhexlify("00" + data["data"][8:14]))[0]
915 if "factor" in data:
916 data["bezug1"] = data["bezug1"] * data["factor"]
917 postdata["value"] = data["bezug1"]
918 postdata["name"] = "BezugHochtarif"
919 postdata["text"] = "{0} {1} (DevID={2} SubDevID={3} EUI={4})".format(
920 data["serial"],
921 postdata["name"],
922 postdata["DevID"],
923 postdata["SubDevID"],
924 data["EUI"],
925 ).encode("iso-8859-1")
926 if data.get("P2", False) == 1:
927 self.colibird_post(postdata)
928
929 if len(data["data"]) < 20:
930 log.msg(
931 "IRH-AVECV2 {1} bytes received: {1}".format(
932 len(data["data"]), data
933 )
934 )
935 return
936
937 postdata["DevID"] = postdata["DevID"] + 1
938 data["bezug2"] = unpack(">I", unhexlify("00" + data["data"][14:20]))[0]
939 if "factor" in data:
940 data["bezug2"] = data["bezug2"] * data["factor"]
941 postdata["value"] = data["bezug2"]
942 postdata["name"] = "BezugNiedertarif"
943 postdata["text"] = "{0} {1} (DevID={2} SubDevID={3} EUI={4})".format(
944 data["serial"],
945 postdata["name"],
946 postdata["DevID"],
947 postdata["SubDevID"],
948 data["EUI"],
949 ).encode("iso-8859-1")
950 if data.get("P3", False) == 1:
951 self.colibird_post(postdata)
952
953 if len(data["data"]) < 26:
954 log.msg(
955 "IRH-AVECV2 {1} bytes received: {1}".format(
956 len(data["data"]), data
957 )
958 )
959 return
960
961 postdata["DevID"] = postdata["DevID"] + 1
962 data["bezug3"] = unpack(">I", unhexlify("00" + data["data"][20:26]))[0]
963 if "factor" in data:
964 data["bezug3"] = data["bezug3"] * data["factor"]
965 postdata["value"] = data["bezug3"]
966 postdata["name"] = "BezugSumme"
967 postdata["text"] = "{0} {1} (DevID={2} SubDevID={3} EUI={4})".format(
968 data["serial"],
969 postdata["name"],
970 postdata["DevID"],
971 postdata["SubDevID"],
972 data["EUI"],
973 ).encode("iso-8859-1")
974 if data.get("P4", False) == 1:
975 self.colibird_post(postdata)
976
977 if len(data["data"]) < 32:
978 log.msg(
979 "IRH-AVECV2 {1} bytes received: {1}".format(
980 len(data["data"]), data
981 )
982 )
983 return
984
985 postdata["DevID"] = postdata["DevID"] + 1
986 data["lieferung1"] = unpack(
987 ">I", unhexlify("00" + data["data"][26:32])
988 )[0]
989 if "factor" in data:
990 data["lieferung1"] = data["lieferung1"] * data["factor"]
991 postdata["value"] = data["lieferung1"]
992 postdata["name"] = "EinspeisungHochtarif"
993 postdata["text"] = "{0} {1} (DevID={2} SubDevID={3} EUI={4})".format(
994 data["serial"],
995 postdata["name"],
996 postdata["DevID"],
997 postdata["SubDevID"],
998 data["EUI"],
999 ).encode("iso-8859-1")
1000 if data.get("P5", False) == 1:
1001 self.colibird_post(postdata)
1002
1003 if len(data["data"]) < 38:
1004 log.msg(
1005 "IRH-AVECV2 {1} bytes received: {1}".format(
1006 len(data["data"]), data
1007 )
1008 )
1009 return
1010
1011 postdata["DevID"] = postdata["DevID"] + 1
1012 data["lieferung2"] = unpack(
1013 ">I", unhexlify("00" + data["data"][32:38])
1014 )[0]
1015 if "factor" in data:
1016 data["lieferung2"] = data["lieferung2"] * data["factor"]
1017 postdata["value"] = data["lieferung2"]
1018 postdata["name"] = "EinspeisungNiedertarif"
1019 postdata["text"] = "{0} {1} (DevID={2} SubDevID={3} EUI={4})".format(
1020 data["serial"],
1021 postdata["name"],
1022 postdata["DevID"],
1023 postdata["SubDevID"],
1024 data["EUI"],
1025 ).encode("iso-8859-1")
1026 if data.get("P6", False) == 1:
1027 self.colibird_post(postdata)
1028
1029 if len(data["data"]) < 44:
1030 log.msg(
1031 "IRH-AVECV2 {1} bytes received: {1}".format(
1032 len(data["data"]), data
1033 )
1034 )
1035 return
1036
1037 postdata["DevID"] = postdata["DevID"] + 1
1038 data["rueckgestellt1"] = unpack(
1039 ">I", unhexlify("00" + data["data"][38:44])
1040 )[0]
1041 if "factor" in data:
1042 data["rueckgestellt1"] = data["rueckgestellt1"] * data["factor"]
1043 postdata["value"] = data["rueckgestellt1"]
1044 postdata["name"] = "RueckgestelltHochtarif"
1045 postdata["text"] = "{0} {1} (DevID={2} SubDevID={3} EUI={4})".format(
1046 data["serial"],
1047 postdata["name"],
1048 postdata["DevID"],
1049 postdata["SubDevID"],
1050 data["EUI"],
1051 ).encode("iso-8859-1")
1052 if data.get("P7", False) == 1:
1053 self.colibird_post(postdata)
1054
1055 if len(data["data"]) < 50:
1056 log.msg(
1057 "IRH-AVECV2 {1} bytes received: {1}".format(
1058 len(data["data"]), data
1059 )
1060 )
1061 return
1062
1063 postdata["DevID"] = postdata["DevID"] + 1
1064 data["rueckgestellt2"] = unpack(
1065 ">I", unhexlify("00" + data["data"][44:50])
1066 )[0]
1067 if "factor" in data:
1068 data["rueckgestellt2"] = data["rueckgestellt2"] * data["factor"]
1069 postdata["value"] = data["rueckgestellt2"]
1070 postdata["name"] = "RueckgestelltNiedertarif"
1071 postdata["text"] = "{0} {1} (DevID={2} SubDevID={3} EUI={4})".format(
1072 data["serial"],
1073 postdata["name"],
1074 postdata["DevID"],
1075 postdata["SubDevID"],
1076 data["EUI"],
1077 ).encode("iso-8859-1")
1078 if data.get("P8", False) == 1:
1079 self.colibird_post(postdata)
1080
1081 elif data["device-type"] == "E350Standard":
1082 # 01234567 890123 456789 012345 678901 234567 890123 456789
1083 # 70B3D5FFFE0E8F86 01dcf2e9 0054fd 0026e8 007be6 000000 000000 000000 000000
1084 # byte char description
1085 # 0-3 0-7 P1: Zeitstempel (4 bytes unsigned int MSB)
1086 # 4-7 8-15 P2: Hochtarif (4 bytes unsigned int MSB)
1087 # 8-11 16-25 P3: Niedertarif (4 bytes unsigned int MSB)
1088 # 12-15 24-31 P4: Summe Hochtarif / Niedertairf (4 bytes unsigned int MSB)
1089 # 16-19 32-39 P5: Hochtarif Einspeisung (4 bytes unsigned int MSB)
1090 # 20-23 40-47 P6: Niedertarif Einspeisung (4 bytes unsigned int MSB)
1091 # 24-27 48-55 P7: BezugBlindHochtarif (4 bytes unsigned int MSB)
1092 # 28-31 56-63 P8: BezugBlindNiedertarif (4 bytes unsigned int MSB)
1093 # 32-35 64-71 P9: EinspeisungBlindHochtarif (4 bytes unsigned int MSB)
1094 # 36-39 72-79 P10: EinspeisungBlindNiedertarif (4 bytes unsigned int MSB)
1095
1096 if len(data["data"]) < 79:
1097 log.msg(
1098 "E350Standard {1} bytes received of 79 minimum: {1}".format(
1099 len(data["data"]), data
1100 )
1101 )
1102 return
1103
1104 data["datetime"] = datetime.datetime.fromtimestamp(
1105 unpack(">I", unhexlify(data["data"][0:8]))[0]
1106 )
1107 postdata["time"] = (
1108 data["datetime"].strftime("%Y-%m-%d %H:%M:%S").encode("iso-8859-1")
1109 )
1110 postdata["media"] = data["media"]
1111 postdata["DevID"] = data["DevID"]
1112 postdata["SubDevID"] = data["SubDevID"]
1113 data["serial"] = 0
1114 data["bezug1"] = unpack(">I", unhexlify(data["data"][8:16]))[0]
1115 if "factor" in data:
1116 data["bezug1"] = data["bezug1"] * data["factor"]
1117 postdata["value"] = data["bezug1"]
1118 postdata["name"] = "BezugHochtarif"
1119 postdata["text"] = "{0} {1} (DevID={2} SubDevID={3} EUI={4})".format(
1120 data["serial"],
1121 postdata["name"],
1122 postdata["DevID"],
1123 postdata["SubDevID"],
1124 data["EUI"],
1125 ).encode("iso-8859-1")
1126 if data.get("P2", False) == 1:
1127 self.colibird_post(postdata)
1128
1129 postdata["DevID"] = postdata["DevID"] + 1
1130 data["bezug2"] = unpack(">I", unhexlify(data["data"][16:24]))[0]
1131 if "factor" in data:
1132 data["bezug2"] = data["bezug2"] * data["factor"]
1133 postdata["value"] = data["bezug2"]
1134 postdata["name"] = "BezugNiedertarif"
1135 postdata["text"] = "{0} {1} (DevID={2} SubDevID={3} EUI={4})".format(
1136 data["serial"],
1137 postdata["name"],
1138 postdata["DevID"],
1139 postdata["SubDevID"],
1140 data["EUI"],
1141 ).encode("iso-8859-1")
1142 if data.get("P3", False) == 1:
1143 self.colibird_post(postdata)
1144
1145 postdata["DevID"] = postdata["DevID"] + 1
1146 data["bezug3"] = postdata["value"] = unpack(
1147 ">I", unhexlify(data["data"][24:32])
1148 )[0]
1149 if "factor" in data:
1150 data["bezug3"] = data["bezug3"] * data["factor"]
1151 postdata["value"] = data["bezug3"]
1152 postdata["name"] = "BezugSumme"
1153 postdata["text"] = "{0} {1} (DevID={2} SubDevID={3} EUI={4})".format(
1154 data["serial"],
1155 postdata["name"],
1156 postdata["DevID"],
1157 postdata["SubDevID"],
1158 data["EUI"],
1159 ).encode("iso-8859-1")
1160 if data.get("P4", False) == 1:
1161 self.colibird_post(postdata)
1162
1163 postdata["DevID"] = postdata["DevID"] + 1
1164 data["lieferung1"] = postdata["value"] = unpack(
1165 ">I", unhexlify(data["data"][32:40])
1166 )[0]
1167 if "factor" in data:
1168 data["lieferung1"] = data["lieferung1"] * data["factor"]
1169 postdata["value"] = data["lieferung1"]
1170 postdata["name"] = "EinspeisungHochtarif"
1171 postdata["text"] = "{0} {1} (DevID={2} SubDevID={3} EUI={4})".format(
1172 data["serial"],
1173 postdata["name"],
1174 postdata["DevID"],
1175 postdata["SubDevID"],
1176 data["EUI"],
1177 ).encode("iso-8859-1")
1178 if data.get("P5", False) == 1:
1179 self.colibird_post(postdata)
1180
1181 postdata["DevID"] = postdata["DevID"] + 1
1182 data["lieferung2"] = postdata["value"] = unpack(
1183 ">I", unhexlify(data["data"][40:48])
1184 )[0]
1185 if "factor" in data:
1186 data["lieferung2"] = data["lieferung2"] * data["factor"]
1187 postdata["value"] = data["lieferung2"]
1188 postdata["name"] = "EinspeisungNiedertarif"
1189 postdata["text"] = "{0} {1} (DevID={2} SubDevID={3} EUI={4})".format(
1190 data["serial"],
1191 postdata["name"],
1192 postdata["DevID"],
1193 postdata["SubDevID"],
1194 data["EUI"],
1195 ).encode("iso-8859-1")
1196 if data.get("P6", False) == 1:
1197 self.colibird_post(postdata)
1198
1199 postdata["DevID"] = postdata["DevID"] + 1
1200 data["bezugblind1"] = postdata["value"] = unpack(
1201 ">I", unhexlify(data["data"][48:56])
1202 )[0]
1203 if "factor" in data:
1204 data["bezugblind1"] = data["bezugblind1"] * data["factor"]
1205 postdata["value"] = data["bezugblind1"]
1206 postdata["name"] = "BezugBlindHochtarif"
1207 postdata["text"] = "{0} {1} (DevID={2} SubDevID={3} EUI={4})".format(
1208 data["serial"],
1209 postdata["name"],
1210 postdata["DevID"],
1211 postdata["SubDevID"],
1212 data["EUI"],
1213 ).encode("iso-8859-1")
1214 if data.get("P7", False) == 1:
1215 self.colibird_post(postdata)
1216
1217 postdata["DevID"] = postdata["DevID"] + 1
1218 data["bezugblind2"] = postdata["value"] = unpack(
1219 ">I", unhexlify(data["data"][56:64])
1220 )[0]
1221 if "factor" in data:
1222 data["bezugblind2"] = data["bezugblind2"] * data["factor"]
1223 postdata["value"] = data["bezugblind2"]
1224 postdata["name"] = "BezugBlindNiedertarif"
1225 postdata["text"] = "{0} {1} (DevID={2} SubDevID={3} EUI={4})".format(
1226 data["serial"],
1227 postdata["name"],
1228 postdata["DevID"],
1229 postdata["SubDevID"],
1230 data["EUI"],
1231 ).encode("iso-8859-1")
1232 if data.get("P8", False) == 1:
1233 self.colibird_post(postdata)
1234
1235 postdata["DevID"] = postdata["DevID"] + 1
1236 data["lieferungblind1"] = postdata["value"] = unpack(
1237 ">I", unhexlify(data["data"][64:72])
1238 )[0]
1239 if "factor" in data:
1240 data["lieferungblind1"] = data["lieferungblind1"] * data["factor"]
1241 postdata["value"] = data["lieferungblind1"]
1242 postdata["name"] = "EinspeisungBlindHochtarif"
1243 postdata["text"] = "{0} {1} (DevID={2} SubDevID={3} EUI={4})".format(
1244 data["serial"],
1245 postdata["name"],
1246 postdata["DevID"],
1247 postdata["SubDevID"],
1248 data["EUI"],
1249 ).encode("iso-8859-1")
1250 if data.get("P9", False) == 1:
1251 self.colibird_post(postdata)
1252
1253 postdata["DevID"] = postdata["DevID"] + 1
1254 data["lieferungblind2"] = postdata["value"] = unpack(
1255 ">I", unhexlify(data["data"][72:80])
1256 )[0]
1257 if "factor" in data:
1258 data["lieferungblind2"] = data["lieferungblind2"] * data["factor"]
1259 postdata["value"] = data["lieferungblind2"]
1260 postdata["name"] = "EinspeisungBlindNiedertarif"
1261 postdata["text"] = "{0} {1} (DevID={2} SubDevID={3} EUI={4})".format(
1262 data["serial"],
1263 postdata["name"],
1264 postdata["DevID"],
1265 postdata["SubDevID"],
1266 data["EUI"],
1267 ).encode("iso-8859-1")
1268 if data.get("P10", False) == 1:
1269 self.colibird_post(postdata)
1270
1271 elif data["device-type"] == "E350Default":
1272 # 01234567 890123 456789 012345 678901 234567 890123 456789
1273 # 70B3D5FFFE0E8F86 01dcf2e9 0054fd 0026e8 007be6 000000 000000 000000 000000
1274 # byte char description
1275 # 0-3 0-7 P1: Zeitstempel (4 bytes unsigned int MSB)
1276 # 4-7 8-15 P2: 1.8.0 Summe Bezug Wirk Hoch/Nieder (4 bytes unsigned int MSB)
1277 # 8-11 16-25 P3: 1.8.1 Bezug Wirk Hochtarif (4 bytes unsigned int MSB)
1278 # 12-15 24-31 P4: 1.8.2 Bezug Wirk Niedertarif (4 bytes unsigned int MSB)
1279 # 16-19 32-39 P5: 2.8.0 Summe Einspeisung Wirk Hoch/Nieder (4 bytes unsigned int MSB)
1280 # 20-23 40-47 P6: 2.8.1 Einspeisung Wirk Hoch (4 bytes unsigned int MSB)
1281 # 24-27 48-55 P7: 2.8.2 Einspeisung Wirk Nieder (4 bytes unsigned int MSB)
1282 # 28-31 56-63 P8: 3.8.0 Bezug Blind (4 bytes unsigned int MSB)
1283 # 32-35 64-71 P9: 4.8.0 Einspeisung Blind (4 bytes unsigned int MSB)
1284
1285 if len(data["data"]) < 71:
1286 log.msg(
1287 "E350Default {1} bytes received of 71 minimum: {1}".format(
1288 len(data["data"]), data
1289 )
1290 )
1291 return
1292
1293 data["datetime"] = datetime.datetime.fromtimestamp(
1294 unpack(">I", unhexlify(data["data"][0:8]))[0]
1295 )
1296 postdata["time"] = (
1297 data["datetime"].strftime("%Y-%m-%d %H:%M:%S").encode("iso-8859-1")
1298 )
1299 postdata["media"] = data["media"]
1300 postdata["DevID"] = data["DevID"]
1301 postdata["SubDevID"] = data["SubDevID"]
1302 data["serial"] = 0
1303 data["bezug1"] = unpack(">I", unhexlify(data["data"][8:16]))[0]
1304 if "factor" in data:
1305 data["bezug1"] = data["bezug1"] * data["factor"]
1306 postdata["value"] = data["bezug1"]
1307 postdata["name"] = "BezugWirkSumme"
1308 postdata["text"] = "{0} {1} (DevID={2} SubDevID={3} EUI={4})".format(
1309 data["serial"],
1310 postdata["name"],
1311 postdata["DevID"],
1312 postdata["SubDevID"],
1313 data["EUI"],
1314 ).encode("iso-8859-1")
1315 if data.get("P2", False) == 1:
1316 self.colibird_post(postdata)
1317
1318 postdata["DevID"] = postdata["DevID"] + 1
1319 data["bezug2"] = unpack(">I", unhexlify(data["data"][16:24]))[0]
1320 if "factor" in data:
1321 data["bezug2"] = data["bezug2"] * data["factor"]
1322 postdata["value"] = data["bezug2"]
1323 postdata["name"] = "BezugWirkHoch"
1324 postdata["text"] = "{0} {1} (DevID={2} SubDevID={3} EUI={4})".format(
1325 data["serial"],
1326 postdata["name"],
1327 postdata["DevID"],
1328 postdata["SubDevID"],
1329 data["EUI"],
1330 ).encode("iso-8859-1")
1331 if data.get("P3", False) == 1:
1332 self.colibird_post(postdata)
1333
1334 postdata["DevID"] = postdata["DevID"] + 1
1335 data["bezug3"] = postdata["value"] = unpack(
1336 ">I", unhexlify(data["data"][24:32])
1337 )[0]
1338 if "factor" in data:
1339 data["bezug3"] = data["bezug3"] * data["factor"]
1340 postdata["value"] = data["bezug3"]
1341 postdata["name"] = "BezugWirkNieder"
1342 postdata["text"] = "{0} {1} (DevID={2} SubDevID={3} EUI={4})".format(
1343 data["serial"],
1344 postdata["name"],
1345 postdata["DevID"],
1346 postdata["SubDevID"],
1347 data["EUI"],
1348 ).encode("iso-8859-1")
1349 if data.get("P4", False) == 1:
1350 self.colibird_post(postdata)
1351
1352 postdata["DevID"] = postdata["DevID"] + 1
1353 data["lieferung1"] = postdata["value"] = unpack(
1354 ">I", unhexlify(data["data"][32:40])
1355 )[0]
1356 if "factor" in data:
1357 data["lieferung1"] = data["lieferung1"] * data["factor"]
1358 postdata["value"] = data["lieferung1"]
1359 postdata["name"] = "EinspeisungWirkSumme"
1360 postdata["text"] = "{0} {1} (DevID={2} SubDevID={3} EUI={4})".format(
1361 data["serial"],
1362 postdata["name"],
1363 postdata["DevID"],
1364 postdata["SubDevID"],
1365 data["EUI"],
1366 ).encode("iso-8859-1")
1367 if data.get("P5", False) == 1:
1368 self.colibird_post(postdata)
1369
1370 postdata["DevID"] = postdata["DevID"] + 1
1371 data["lieferung2"] = postdata["value"] = unpack(
1372 ">I", unhexlify(data["data"][40:48])
1373 )[0]
1374 if "factor" in data:
1375 data["lieferung2"] = data["lieferung2"] * data["factor"]
1376 postdata["value"] = data["lieferung2"]
1377 postdata["name"] = "EinspeisungWirkHoch"
1378 postdata["text"] = "{0} {1} (DevID={2} SubDevID={3} EUI={4})".format(
1379 data["serial"],
1380 postdata["name"],
1381 postdata["DevID"],
1382 postdata["SubDevID"],
1383 data["EUI"],
1384 ).encode("iso-8859-1")
1385 if data.get("P6", False) == 1:
1386 self.colibird_post(postdata)
1387
1388 postdata["DevID"] = postdata["DevID"] + 1
1389 data["lieferung3"] = postdata["value"] = unpack(
1390 ">I", unhexlify(data["data"][48:56])
1391 )[0]
1392 if "factor" in data:
1393 data["lieferung3"] = data["lieferung3"] * data["factor"]
1394 postdata["value"] = data["lieferung3"]
1395 postdata["name"] = "EinspeisungWirkNieder"
1396 postdata["text"] = "{0} {1} (DevID={2} SubDevID={3} EUI={4})".format(
1397 data["serial"],
1398 postdata["name"],
1399 postdata["DevID"],
1400 postdata["SubDevID"],
1401 data["EUI"],
1402 ).encode("iso-8859-1")
1403 if data.get("P7", False) == 1:
1404 self.colibird_post(postdata)
1405
1406 postdata["DevID"] = postdata["DevID"] + 1
1407 data["bezugblind"] = postdata["value"] = unpack(
1408 ">I", unhexlify(data["data"][56:64])
1409 )[0]
1410 if "factor" in data:
1411 data["bezugblind"] = data["bezugblind"] * data["factor"]
1412 postdata["value"] = data["bezugblind"]
1413 postdata["name"] = "BezugBlind"
1414 postdata["text"] = "{0} {1} (DevID={2} SubDevID={3} EUI={4})".format(
1415 data["serial"],
1416 postdata["name"],
1417 postdata["DevID"],
1418 postdata["SubDevID"],
1419 data["EUI"],
1420 ).encode("iso-8859-1")
1421 if data.get("P8", False) == 1:
1422 self.colibird_post(postdata)
1423
1424 postdata["DevID"] = postdata["DevID"] + 1
1425 data["lieferungblind"] = postdata["value"] = unpack(
1426 ">I", unhexlify(data["data"][64:72])
1427 )[0]
1428 if "factor" in data:
1429 data["lieferungblind"] = data["lieferungblind"] * data["factor"]
1430 postdata["value"] = data["lieferungblind"]
1431 postdata["name"] = "EinspeisungBlind"
1432 postdata["text"] = "{0} {1} (DevID={2} SubDevID={3} EUI={4})".format(
1433 data["serial"],
1434 postdata["name"],
1435 postdata["DevID"],
1436 postdata["SubDevID"],
1437 data["EUI"],
1438 ).encode("iso-8859-1")
1439 if data.get("P9", False) == 1:
1440 self.colibird_post(postdata)
1441
1442 elif data["device-type"] == "CalecIILoRa-prototyp":
1443 # MBus over LoRa...
1444 # 040602000000053be718af44055b0826a341055f7869a341046d1e2922220c7870365705
1445 # 040602000000 Energie 2000 Wh
1446 # 053be718af44 Durchflussmenge 1.400778 m3/h
1447 # 055b0826a341 Vorlauftemperatur 20.39357 C
1448 # 055f7869a341 Rücklauftemperatur 20.4265 C
1449 # 046d1e292222 Datum 2017-02-02 09:30
1450 # 0c7870365705 Fabrikationsnummer 5573670
1451 # looks like:
1452 # MMVVDDDDDDDD
1453 # MM = 1 byte medium aka DIF (M-Bus standard table 8.4.1)
1454 # VV = 1 byte VIF (value information field, unit and factor, M-Bus standard table 8.4.3)
1455 # DDDDDDDD = 4 byte data, float or int depending on DIF
1456 offset = 0
1457 data["chunk"] = []
1458 while offset < len(data["data"]):
1459 chunkdata = {}
1460 chunkdata["type"] = "difvif"
1461 chunkdata["unit"] = ""
1462 chunkdata["factor"] = 1
1463 chunkdata["dif"] = unpack(
1464 ">B", unhexlify(data["data"][offset + 0 : offset + 2])
1465 )[0]
1466 chunkdata["vif"] = unpack(
1467 ">B", unhexlify(data["data"][offset + 2 : offset + 4])
1468 )[0]
1469
1470 self.mbus_difvif_parse(chunkdata)
1471
1472 if chunkdata["dif"] == 4:
1473 raw = unpack(
1474 "<i", unhexlify(data["data"][offset + 4 : offset + 12])
1475 )[0]
1476 if chunkdata["type"] == "date":
1477 minutes = raw & 0x3F
1478 hours = (raw >> 8) & 0x1F
1479 days = (raw >> 16) & 0x1F
1480 months = (raw >> 24) & 0x0F
1481 years = (
1482 (((raw >> 16) & 0xE0) >> 5)
1483 | (((raw >> 24) & 0xF0) >> 1)
1484 ) + 2000
1485 dst = bool((raw >> 8) & 0x80)
1486 data["datetime"] = chunkdata["date"] = datetime.datetime(
1487 years, months, days, hours, minutes
1488 )
1489 # log.msg("m={0} h={1} d={2} m={3} y={4} dst={5} - {6}".format(minutes, hours, days, months, years, dst, chunkdata['date']))
1490 else:
1491 chunkdata["value"] = raw * chunkdata["factor"]
1492 elif chunkdata["dif"] == 5:
1493 chunkdata["value"] = (
1494 unpack(
1495 "<f", unhexlify(data["data"][offset + 4 : offset + 12])
1496 )[0]
1497 * chunkdata["factor"]
1498 )
1499 elif chunkdata["dif"] == 12:
1500 chunkdata["value"] = int(
1501 data["data"][offset + 10 : offset + 12]
1502 + data["data"][offset + 8 : offset + 10]
1503 + data["data"][offset + 6 : offset + 8]
1504 + data["data"][offset + 4 : offset + 6]
1505 )
1506 if chunkdata["type"] == "serial":
1507 data["serial"] = chunkdata["value"]
1508 else:
1509 log.msg(
1510 "unknown DIF {0} in data: {1}".format(
1511 chunkdata["dif"], data
1512 )
1513 )
1514
1515 data["chunk"].append(chunkdata)
1516 log.msg(
1517 "chunk:{0}, offset:{1}/{3}, data:{2}".format(
1518 data["data"][offset : offset + 12],
1519 offset,
1520 chunkdata,
1521 len(data["data"]),
1522 )
1523 )
1524 offset += 12
1525 # endwhile
1526
1527 postdata["DevID"] = data["DevID"]
1528 postdata["time"] = (
1529 data["datetime"].strftime("%Y-%m-%d %H:%M:%S").encode("iso-8859-1")
1530 )
1531 for chunkdata in data["chunk"]:
1532 if chunkdata["type"] == "difvif" and "media" in chunkdata:
1533 # actual meter data, not datetime or serial or stuff colibird doesn't know about
1534 postdata["media"] = chunkdata["media"]
1535 postdata["SubDevID"] = data["SubDevID"]
1536 postdata["unit"] = chunkdata["unit"]
1537 postdata["value"] = chunkdata["value"]
1538 postdata[
1539 "text"
1540 ] = "{0} {1} (DevID={2} SubDevID={3} EUI={4})".format(
1541 data["serial"],
1542 chunkdata["name"],
1543 postdata["DevID"],
1544 postdata["SubDevID"],
1545 data["EUI"],
1546 ).encode(
1547 "iso-8859-1"
1548 )
1549 self.colibird_post(postdata)
1550 postdata["DevID"] += 1
1551
1552 elif data["device-type"] == "CalecIILoRa":
1553 # https://drive.google.com/open?id=15LshFWzb7KuRPahp05QR2s9fZqfH826V
1554 # examples:
1555 # 26 44 b405 55781819 c0 04 7a b9 00 0000 04 06 284e0000 04 15 ec310000 04 6d 1f345c2c 0c 78 07495805
1556 # 32 44 b405 55781819 c0 04 7a ba 00 0000 05 2b 00000000 05 3b 00000000 05 5b 98a08b41 05 5f e88e8141 04 6d 1f345c2c 0c 78 07495805
1557
1558 # 1 byte data length
1559 data["datalength"] = unpack("B", unhexlify(data["data"][0:2]))[0]
1560 if data["datalength"] != int(len(data["data"]) / 2 - 1):
1561 log.msg(
1562 "mismatch: datalength {0}, packet length {1}".format(
1563 data["datalength"], int(len(data["data"]) / 2 - 1)
1564 )
1565 )
1566 # 1 byte 0x44 constant
1567 data["datatype"] = unpack("B", unhexlify(data["data"][2:4]))[0]
1568 if data["datatype"] != int("44", 16):
1569 log.err(
1570 "received CalecIILoRa telegram type {0} {0:x} != 0x44".format(
1571 data["datatype"]
1572 )
1573 )
1574 # vendor type 0x05b4 constant
1575 data["vendor"] = unpack("<H", unhexlify(data["data"][4:8]))[0]
1576 if data["vendor"] != int("05b4", 16):
1577 log.err(
1578 "received CalecIILoRa vendor type {0} {0:x} != 0x05b4".format(
1579 data["vendor"]
1580 )
1581 )
1582 data["secondaryaddress"] = unpack("<I", unhexlify(data["data"][8:16]))[
1583 0
1584 ]
1585 data["deviceversion"] = unpack("B", unhexlify(data["data"][16:18]))[0]
1586 data["mediatype"] = unpack("B", unhexlify(data["data"][18:20]))[0]
1587 if data["mediatype"] == 4:
1588 # 0x04 = Rücklauf (Wärmezähler)
1589 data["media"] = "heat"
1590 elif data["mediatype"] == int("c", 16):
1591 # 0x0c = Vorlauf
1592 data["media"] = "heat"
1593 elif data["mediatype"] == 7:
1594 # 0x07 = Wasser
1595 data["media"] = "water"
1596 # Short Header constant 0x7A
1597 data["shortheader"] = unpack("B", unhexlify(data["data"][20:22]))[0]
1598 if data["shortheader"] != int("7a", 16):
1599 log.err(
1600 "received CalecIILoRa short header {0} {0:x} != 0x7a".format(
1601 data["shortheader"]
1602 )
1603 )
1604 # Zugriffszähler, Inkrement pro Auslesung
1605 data["accesscounter"] = unpack("B", unhexlify(data["data"][22:24]))[0]
1606 # Status entsprechend EN13757
1607 data["devicestatus"] = unpack("B", unhexlify(data["data"][24:26]))[0]
1608 # Signatur (not used constant 0x0000)
1609 data["signature"] = unpack("<H", unhexlify(data["data"][26:30]))[0]
1610
1611 offset = 30
1612 data["chunks"] = []
1613 while offset < len(data["data"]):
1614 chunkdata = {}
1615 chunkdata["dif"] = unpack(
1616 "B", unhexlify(data["data"][offset : offset + 2])
1617 )[0]
1618 chunkdata["vif"] = unpack(
1619 "B", unhexlify(data["data"][offset + 2 : offset + 4])
1620 )[0]
1621 offset += 4
1622 if chunkdata["dif"] == int("0c", 16) and chunkdata["vif"] == int(
1623 "78", 16
1624 ):
1625 # 4 byte BCD8 - Fabrikations-Nummer
1626 data["serial"] = int(data["data"][offset : offset + 8])
1627 offset += 8
1628 elif chunkdata["dif"] == 4 or chunkdata["dif"] == 5:
1629 self.mbus_difvif_parse(chunkdata)
1630 if chunkdata["dif"] == 4:
1631 chunkdata["value"] = unpack(
1632 "<I", unhexlify(data["data"][offset : offset + 8])
1633 )[0] * chunkdata.get("factor", 1)
1634 elif chunkdata["dif"] == 5:
1635 chunkdata["value"] = unpack(
1636 "<f", unhexlify(data["data"][offset : offset + 8])
1637 )[0] * chunkdata.get("factor", 1)
1638
1639 if chunkdata["vif"] == int("6d", 16):
1640 # 4 byte INT4 - Aktuelles Datum + Uhrzeit (Timestamp)
1641 # data["timestamp"] = chunkdata['value'] = unpack(">I", unhexlify(data["data"][offset:offset+8]))[0]
1642 # unfortunately I get 1986 with big-endian and 1993 with little endian parsing -> unusable
1643 log.msg(
1644 "CalecIILoRa timestamp {0}: {1}".format(
1645 chunkdata["value"],
1646 datetime.datetime.fromtimestamp(chunkdata["value"]),
1647 )
1648 )
1649
1650 offset += 8
1651 log.msg(chunkdata)
1652 data["chunks"].append(chunkdata)
1653 else:
1654 log.err("unknown CalecIILoRa dif/vif {0}".format(chunkdata))
1655 chunkoffset = 0
1656 # start to increment the DevID at this offset from data['DevID']
1657 # since one message type includes heat/water and the other tempR/tempR we'll offset the temps not to overlap with the former
1658 typeoffset = {"tempR": 5}
1659 for chunkdata in data["chunks"]:
1660 if chunkdata.get("media", False):
1661 postdata["unit"] = chunkdata["unit"]
1662 postdata["media"] = chunkdata["media"]
1663 postdata["value"] = chunkdata["value"]
1664 postdata["DevID"] = (
1665 data["DevID"]
1666 + typeoffset.get(postdata["media"], 0)
1667 + chunkoffset
1668 )
1669 postdata[
1670 "text"
1671 ] = "LoRaWAN EUI={0} DevID={1} type={2} secondaryaddress={3}, serial={4}, register={5}".format(
1672 data["EUI"],
1673 data["DevID"],
1674 data["device-type"],
1675 data["secondaryaddress"],
1676 data["serial"],
1677 chunkdata["name"],
1678 ).encode(
1679 "iso-8859-1"
1680 )
1681 self.colibird_post(postdata)
1682 chunkoffset += 1
1683
1684 elif data["device-type"] == "mirotemp":
1685 # https://github.com/alexnanzer/simple-complex/files/895332/Miromico.-.Temperatur.Sensor.pdf
1686 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
1687 "",
1688 "Temperatur",
1689 postdata["DevID"],
1690 postdata["SubDevID"],
1691 data["EUI"],
1692 ).encode("iso-8859-1")
1693 postdata["value"] = data["temperature"] = (
1694 int(data["data"][4:8], 16) / 10
1695 )
1696 postdata["media"] = "tempx"
1697 postdata["unit"] = "°C"
1698 self.colibird_post(postdata)
1699
1700 elif data["device-type"] == "tabshealthy1":
1701 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
1702 "",
1703 "Temperatur",
1704 postdata["DevID"],
1705 postdata["SubDevID"],
1706 data["EUI"],
1707 ).encode("iso-8859-1")
1708 postdata["value"] = int(data["data"][4:6], 16) - 32
1709 postdata["media"] = data["media"]
1710 self.colibird_post(postdata)
1711
1712 elif data["device-type"] == "tabsdoorwindow":
1713 if len(data["data"]) == 16:
1714 postdata["text"] = "Status, tabs Door/Window (EUI={0})".format(
1715 data["EUI"]
1716 ).encode("iso-8859-1")
1717
1718 postdata["value"] = int(data["data"][0:2], 16)
1719 postdata["media"] = "door"
1720 postdata["unit"] = ""
1721 self.colibird_post(postdata)
1722
1723 postdata["DevID"] = postdata["DevID"] + 1
1724 postdata[
1725 "text"
1726 ] = "Batterylevel V, tabs Door/Window (EUI={0})".format(
1727 data["EUI"]
1728 ).encode(
1729 "iso-8859-1"
1730 )
1731
1732 data["voltages"] = int(data["data"][2:4], 16)
1733 postdata["value"] = data["battery_voltage"] = (
1734 (data["voltages"] & 15) + 25
1735 ) / 10
1736 postdata["media"] = "batlevelv"
1737 postdata["unit"] = "V"
1738 self.colibird_post(postdata)
1739
1740 postdata["DevID"] = postdata["DevID"] + 1
1741 postdata[
1742 "text"
1743 ] = "Batterylevel %, tabs Door/Window (EUI={0})".format(
1744 data["EUI"]
1745 ).encode(
1746 "iso-8859-1"
1747 )
1748 data["voltages"] = int(data["data"][2:4], 16)
1749 postdata["value"] = data["battery_capacity"] = (
1750 ((data["voltages"] & (15 << 4)) >> 4) / 15 * 100
1751 )
1752 postdata["media"] = "batlevel"
1753 postdata["unit"] = "%"
1754 self.colibird_post(postdata)
1755
1756 elif data["device-type"] == "tabsmotion":
1757 if len(data["data"]) == 16:
1758 postdata["text"] = "Motions, tabs Motions (EUI={0})".format(
1759 data["EUI"]
1760 ).encode("iso-8859-1")
1761 postdata["value"] = int(data["data"][0:2], 16)
1762 postdata["media"] = "motion"
1763 postdata["unit"] = ""
1764 self.colibird_post(postdata)
1765
1766 postdata["DevID"] = postdata["DevID"] + 1
1767 postdata["text"] = "Batterylevel V, tabs Motions (EUI={0})".format(
1768 data["EUI"]
1769 ).encode("iso-8859-1")
1770 data["voltages"] = int(data["data"][2:4], 16)
1771 postdata["value"] = data["battery_voltage"] = (
1772 (data["voltages"] & 15) + 25
1773 ) / 10
1774 postdata["media"] = "batlevelv"
1775 postdata["unit"] = "V"
1776 self.colibird_post(postdata)
1777
1778 postdata["DevID"] = postdata["DevID"] + 1
1779 postdata["text"] = "Batterylevel %, tabs Motions (EUI={0})".format(
1780 data["EUI"]
1781 ).encode("iso-8859-1")
1782 data["voltages"] = int(data["data"][2:4], 16)
1783 postdata["value"] = data["battery_capacity"] = (
1784 ((data["voltages"] & (15 << 4)) >> 4) / 15 * 100
1785 )
1786 postdata["media"] = "batlevel"
1787 postdata["unit"] = "%"
1788 self.colibird_post(postdata)
1789
1790 elif data["device-type"] == "yabbygps":
1791
1792 if len(data["data"]) == 22 and data["frameport"] == 1:
1793 postdata["text"] = "Position, Yabby GPS (EUI={0})".format(
1794 data["EUI"]
1795 ).encode("iso-8859-1")
1796 postdata["lat"] = data["lat"] = (
1797 unpack("<i", unhexlify(data["data"][0:8]))[0] / 10000000
1798 )
1799 postdata["lng"] = data["lng"] = (
1800 unpack("<i", unhexlify(data["data"][8:16]))[0] / 10000000
1801 )
1802 postdata["value"] = ""
1803 postdata["media"] = "position"
1804 postdata["unit"] = ""
1805 postdata["dashboard_tile"] = "tracking_od"
1806
1807 if not postdata["lng"] == 0:
1808 self.colibird_post(postdata)
1809
1810 postdata["DevID"] = postdata["DevID"] + 1
1811 postdata["text"] = "Batterylevel V, Yabby GPS (EUI={0})".format(
1812 data["EUI"]
1813 ).encode("iso-8859-1")
1814 postdata["value"] = data["battery_voltage"] = (
1815 int(data["data"][20:22], 16) * 25 / 1000
1816 )
1817 postdata["media"] = "batlevelv"
1818 postdata["unit"] = "V"
1819 if "outdoor" in data:
1820 postdata["dashboard_tile"] = ""
1821
1822 self.colibird_post(postdata)
1823
1824 elif data["device-type"] == "tabslocator":
1825
1826 if len(data["data"]) == 22:
1827
1828 data["status"] = int(data["data"][0:2], 16)
1829 gnssfix = (data["status"] >> 3) & 1
1830
1831 if gnssfix == 0:
1832
1833 postdata[
1834 "text"
1835 ] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
1836 "",
1837 "Tabs Locator",
1838 postdata["DevID"],
1839 postdata["SubDevID"],
1840 data["EUI"],
1841 ).encode(
1842 "iso-8859-1"
1843 )
1844 data["lat"] = unpack("<i", unhexlify(data["data"][6:14]))[
1845 0
1846 ] # int(data['data'][6:14],16) richtig so?
1847 postdata["lat"] = data["locator_lat"] = (
1848 (data["lat"] & 268435455)
1849 ) / 1000000 # die ersten 28 bit
1850 data["lng"] = unpack("<i", unhexlify(data["data"][14:22]))[
1851 0
1852 ] # int(data['data'][14:22],16) richtig so?
1853 postdata["lng"] = data["locator_lng"] = (
1854 (data["lng"] & 536870911)
1855 ) / 1000000 # die ersten 29 bit
1856 postdata["value"] = 1 # to be sure its not empty
1857 postdata["media"] = "position"
1858 postdata["unit"] = ""
1859 postdata["dashboard_tile"] = "tracking_od"
1860
1861 if not postdata["lng"] == 0:
1862 self.colibird_post(postdata)
1863
1864 postdata["DevID"] = postdata["DevID"] + 1
1865 postdata[
1866 "text"
1867 ] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
1868 "",
1869 "Batteriestand V",
1870 postdata["DevID"],
1871 postdata["SubDevID"],
1872 data["EUI"],
1873 ).encode(
1874 "iso-8859-1"
1875 )
1876 data["voltages"] = int(data["data"][2:4], 16)
1877 postdata["value"] = data["battery_voltage"] = (
1878 (data["voltages"] & 15) + 25
1879 ) / 10
1880 postdata["media"] = "batlevelv"
1881 postdata["unit"] = "V"
1882 self.colibird_post(postdata)
1883
1884 postdata["DevID"] = postdata["DevID"] + 1
1885 postdata[
1886 "text"
1887 ] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
1888 "",
1889 "Batteriestand %",
1890 postdata["DevID"],
1891 postdata["SubDevID"],
1892 data["EUI"],
1893 ).encode(
1894 "iso-8859-1"
1895 )
1896 data["voltages"] = int(data["data"][2:4], 16)
1897 postdata["value"] = data["battery_capacity"] = (
1898 ((data["voltages"] & (15 << 4)) >> 4) / 15 * 100
1899 )
1900 postdata["media"] = "batlevel"
1901 postdata["unit"] = "%"
1902 self.colibird_post(postdata)
1903
1904 elif data["device-type"] == "tabshealthy":
1905
1906 if len(data["data"]) == 16:
1907 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
1908 "",
1909 "Temperatur",
1910 postdata["DevID"],
1911 postdata["SubDevID"],
1912 data["EUI"],
1913 ).encode("iso-8859-1")
1914 postdata["value"] = int(data["data"][4:6], 16) - 32
1915 postdata["media"] = "tempx"
1916 postdata["unit"] = "°c"
1917 postdata["dashboard_tile"] = "temp_id"
1918 self.colibird_post(postdata)
1919
1920 postdata["DevID"] = postdata["DevID"] + 1
1921 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
1922 "",
1923 "Feuchtigkeit",
1924 postdata["DevID"],
1925 postdata["SubDevID"],
1926 data["EUI"],
1927 ).encode("iso-8859-1")
1928 postdata["value"] = int(data["data"][6:8], 16)
1929 postdata["media"] = "Humidity"
1930 postdata["unit"] = "%rH"
1931 postdata["dashboard_tile"] = "humidity_id"
1932 self.colibird_post(postdata)
1933
1934 postdata["DevID"] = postdata["DevID"] + 1
1935 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
1936 "", "CO2", postdata["DevID"], postdata["SubDevID"], data["EUI"]
1937 ).encode("iso-8859-1")
1938 postdata["value"] = unpack("<H", unhexlify(data["data"][8:12]))[0]
1939 postdata["media"] = "CO2"
1940 postdata["unit"] = "ppm"
1941 if not postdata["value"] == 65535:
1942 postdata["dashboard_tile"] = "co2_id"
1943 self.colibird_post(postdata)
1944
1945 postdata["DevID"] = postdata["DevID"] + 1
1946 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
1947 "", "VOC", postdata["DevID"], postdata["SubDevID"], data["EUI"]
1948 ).encode("iso-8859-1")
1949 postdata["value"] = unpack("<H", unhexlify(data["data"][12:16]))[0]
1950 postdata["media"] = "voc"
1951 postdata["unit"] = "ppb"
1952 if not postdata["value"] == 65535:
1953 self.colibird_post(postdata)
1954
1955 postdata["DevID"] = postdata["DevID"] + 1
1956 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
1957 "",
1958 "Batteriestand V",
1959 postdata["DevID"],
1960 postdata["SubDevID"],
1961 data["EUI"],
1962 ).encode("iso-8859-1")
1963 data["voltages"] = int(data["data"][2:4], 16)
1964 postdata["value"] = data["battery_voltage"] = (
1965 (data["voltages"] & 15) + 25
1966 ) / 10
1967 postdata["media"] = "batlevelv"
1968 postdata["unit"] = "V"
1969 postdata["dashboard_tile"] = ""
1970 self.colibird_post(postdata)
1971
1972 postdata["DevID"] = postdata["DevID"] + 1
1973 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
1974 "",
1975 "Batteriestand %",
1976 postdata["DevID"],
1977 postdata["SubDevID"],
1978 data["EUI"],
1979 ).encode("iso-8859-1")
1980 data["voltages"] = int(data["data"][2:4], 16)
1981 postdata["value"] = data["battery_capacity"] = (
1982 ((data["voltages"] & (15 << 4)) >> 4) / 15 * 100
1983 )
1984 postdata["media"] = "batlevel"
1985 postdata["unit"] = "%"
1986 postdata["dashboard_tile"] = ""
1987 self.colibird_post(postdata)
1988
1989 elif data["device-type"] == "tabsbutton":
1990
1991 if len(data["data"]) == 22:
1992 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
1993 "",
1994 "Taster 0",
1995 postdata["DevID"],
1996 postdata["SubDevID"],
1997 data["EUI"],
1998 ).encode("iso-8859-1")
1999 data["buttonpushed"] = int(data["data"][0:2], 16)
2000 postdata["value"] = data["button0pushed"] = data["buttonpushed"] & 1
2001 postdata["media"] = "button"
2002 postdata["unit"] = ""
2003 if postdata["value"] == 1:
2004 self.colibird_post(postdata)
2005
2006 postdata["DevID"] = postdata["DevID"] + 1
2007 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
2008 "",
2009 "Taster 1",
2010 postdata["DevID"],
2011 postdata["SubDevID"],
2012 data["EUI"],
2013 ).encode("iso-8859-1")
2014 postdata["value"] = data["button1pushed"] = (
2015 data["buttonpushed"] & (3 << 1)
2016 ) >> 1
2017 postdata["media"] = "button"
2018 postdata["unit"] = ""
2019 if data["button1pushed"] == 1:
2020 self.colibird_post(postdata)
2021
2022 postdata["DevID"] = postdata["DevID"] + 1
2023 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
2024 "",
2025 "Batteriestand V",
2026 postdata["DevID"],
2027 postdata["SubDevID"],
2028 data["EUI"],
2029 ).encode("iso-8859-1")
2030 data["voltages"] = int(data["data"][2:4], 16)
2031 postdata["value"] = data["battery_voltage"] = (
2032 (data["voltages"] & 15) + 25
2033 ) / 10
2034 postdata["media"] = "batlevelv"
2035 postdata["unit"] = "V"
2036 self.colibird_post(postdata)
2037
2038 postdata["DevID"] = postdata["DevID"] + 1
2039 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
2040 "",
2041 "Batteriestand %",
2042 postdata["DevID"],
2043 postdata["SubDevID"],
2044 data["EUI"],
2045 ).encode("iso-8859-1")
2046 data["voltages"] = int(data["data"][2:4], 16)
2047 postdata["value"] = data["battery_capacity"] = (
2048 ((data["voltages"] & (15 << 4)) >> 4) / 15 * 100
2049 )
2050 postdata["media"] = "batlevel"
2051 postdata["unit"] = "%"
2052 self.colibird_post(postdata)
2053
2054 elif data["device-type"] == "GWFWater":
2055 data["vif"] = unpack("<B", unhexlify(data["data"][22:24]))[0]
2056 # 0x16=22: 10^0, 0x15=21: 10^-1, 0x14=20: 10^-2 etc until 0x10=16: 10^-6
2057 data["factor"] = 10 ** (data["vif"] - 22)
2058 postdata["value"] = data["value"] = (
2059 unpack("<I", unhexlify(data["data"][24:32]))[0] * data["factor"]
2060 )
2061 postdata["media"] = "water"
2062 postdata["unit"] = "m3"
2063 self.colibird_post(postdata)
2064
2065 elif data["device-type"] == "ComtacCM1":
2066 # DevSpezifikation folgt
2067 if len(data["data"]) == 26:
2068 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
2069 "",
2070 "Temperatur",
2071 postdata["DevID"],
2072 postdata["SubDevID"],
2073 data["EUI"],
2074 ).encode("iso-8859-1")
2075 postdata["value"] = data["temperature"] = (
2076 unpack(">h", unhexlify(data["data"][18:22]))[0] / 100
2077 )
2078 postdata["media"] = data["media"] = "tempx"
2079 postdata["unit"] = "°C"
2080 self.colibird_post(postdata)
2081
2082 postdata["DevID"] += 1
2083 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
2084 "",
2085 "Feuchte %",
2086 postdata["DevID"],
2087 postdata["SubDevID"],
2088 data["EUI"],
2089 ).encode("iso-8859-1")
2090 postdata["value"] = data["humidity_percentage"] = (
2091 unpack(">h", unhexlify(data["data"][22:26]))[0] / 100
2092 )
2093 postdata["media"] = "Humidity"
2094 postdata["unit"] = "%rH"
2095 self.colibird_post(postdata)
2096
2097 postdata["DevID"] += 1
2098 postdata["value"] = data["Batterylevel_V"] = (
2099 unpack(">H", unhexlify(data["data"][14:18]))[0] / 1000
2100 )
2101 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
2102 "",
2103 "Batteriestand V",
2104 postdata["DevID"],
2105 postdata["SubDevID"],
2106 data["EUI"],
2107 ).encode("iso-8859-1")
2108 postdata["media"] = "batlevelV"
2109 postdata["unit"] = "V"
2110 self.colibird_post(postdata)
2111
2112 elif data["device-type"] == "ComtacMS3":
2113 # DevSpezifikation folgt
2114 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
2115 "",
2116 "Temperatur",
2117 postdata["DevID"],
2118 postdata["SubDevID"],
2119 data["EUI"],
2120 ).encode("iso-8859-1")
2121 postdata["value"] = data["temperature"] = (
2122 unpack(">h", unhexlify(data["data"][10:14]))[0] / 10
2123 )
2124 postdata["media"] = data["media"] = "tempx"
2125 postdata["unit"] = "°C"
2126 self.colibird_post(postdata)
2127
2128 postdata["DevID"] += 1
2129 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
2130 "",
2131 "Feuchte %",
2132 postdata["DevID"],
2133 postdata["SubDevID"],
2134 data["EUI"],
2135 ).encode("iso-8859-1")
2136 postdata["value"] = data["humidity_percentage"] = int(
2137 data["data"][8:10], 16
2138 )
2139 postdata["media"] = "Humidity"
2140 postdata["unit"] = "%rH"
2141 self.colibird_post(postdata)
2142
2143 postdata["DevID"] += 1
2144 postdata["value"] = data["Batterylevel_V"] = (
2145 1 + int(data["data"][4:6], 16) * 0.01
2146 )
2147 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
2148 "",
2149 "Batteriestand V",
2150 postdata["DevID"],
2151 postdata["SubDevID"],
2152 data["EUI"],
2153 ).encode("iso-8859-1")
2154 postdata["media"] = "batlevelV"
2155 postdata["unit"] = "V"
2156 self.colibird_post(postdata)
2157
2158 postdata["DevID"] += 1
2159 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
2160 "",
2161 "MS3 Locator",
2162 postdata["DevID"],
2163 postdata["SubDevID"],
2164 data["EUI"],
2165 ).encode("iso-8859-1")
2166 postdata["lat"] = data["locator_lat"] = (
2167 unpack(">i", unhexlify(data["data"][50:58]))[0] / 1000000
2168 )
2169 postdata["lng"] = data["locator_lng"] = (
2170 unpack(">i", unhexlify(data["data"][58:66]))[0] / 1000000
2171 )
2172 postdata["value"] = 1 # to be sure its not empty
2173 postdata["media"] = "position"
2174 postdata["unit"] = ""
2175 if not postdata["lng"] == 2147.483647:
2176 self.colibird_post(postdata)
2177
2178 postdata["DevID"] += 1
2179 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
2180 "",
2181 "Helligkeit",
2182 postdata["DevID"],
2183 postdata["SubDevID"],
2184 data["EUI"],
2185 ).encode("iso-8859-1")
2186 postdata["value"] = data["brightness"] = int(data["data"][6:8], 16) * 10
2187 postdata["media"] = "Brightness"
2188 postdata["unit"] = "lx"
2189 self.colibird_post(postdata)
2190
2191 elif data["device-type"] == "conbeeL300":
2192 # DevSpezifikation folgt
2193 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
2194 "",
2195 "Temperatur",
2196 postdata["DevID"],
2197 postdata["SubDevID"],
2198 data["EUI"],
2199 ).encode("iso-8859-1")
2200 postdata["value"] = data["temperature"] = unpack(
2201 ">f", unhexlify(data["data"][16:24])
2202 )[0]
2203 postdata["media"] = data["media"] = "tempx"
2204 postdata["unit"] = "°C"
2205 self.colibird_post(postdata)
2206
2207 postdata["DevID"] += 1
2208 postdata["value"] = data["Batterylevel_V"] = (
2209 unpack("<B", unhexlify(data["data"][30:32]))[0]
2210 ) / 10
2211 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
2212 "",
2213 "Batteriestand V",
2214 postdata["DevID"],
2215 postdata["SubDevID"],
2216 data["EUI"],
2217 ).encode("iso-8859-1")
2218 postdata["media"] = "batlevelV"
2219 postdata["unit"] = "V"
2220 self.colibird_post(postdata)
2221
2222 postdata["DevID"] += 1
2223 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
2224 "",
2225 "Helligkeit",
2226 postdata["DevID"],
2227 postdata["SubDevID"],
2228 data["EUI"],
2229 ).encode("iso-8859-1")
2230 postdata["value"] = data["brightness"] = (
2231 unpack("<H", unhexlify(data["data"][6:10]))[0] / 256
2232 )
2233 postdata["media"] = "Brightness"
2234 postdata["unit"] = "lx"
2235 self.colibird_post(postdata)
2236
2237 elif data["device-type"] == "KlaxV1":
2238 # DevSpezifikation folgt
2239 postdata["text"] = "{1} (EUI={2})".format(
2240 "", "Strom 1-8-0", data["EUI"]
2241 ).encode("iso-8859-1")
2242 postdata["value"] = data["1-8-0"] = unpack(
2243 ">i", unhexlify(data["data"][36:44])
2244 )[0]
2245 postdata["media"] = data["media"] = "electricity"
2246 postdata["unit"] = "wh"
2247 self.colibird_post(postdata)
2248
2249 postdata["DevID"] += 1
2250
2251 postdata["text"] = "{1} (EUI={2})".format(
2252 "", "Strom 2-8-0", data["EUI"]
2253 ).encode("iso-8859-1")
2254 postdata["value"] = data["2-8-0"] = unpack(
2255 ">i", unhexlify(data["data"][68:76])
2256 )[0]
2257 postdata["media"] = data["media"] = "electricity"
2258 postdata["unit"] = "wh"
2259 self.colibird_post(postdata)
2260
2261 elif data["device-type"] == "DecentlabIndoorClimate":
2262 # DevSpezifikation folgt
2263 if len(data["data"]) == 54:
2264 postdata["text"] = "{1} (EUI={2})".format(
2265 "", "Temperatur", data["EUI"]
2266 ).encode("iso-8859-1")
2267 postdata["value"] = data["temperature"] = (
2268 int(data["data"][14:18], 16) / 65535 * 175 - 45
2269 )
2270 postdata["media"] = data["media"] = "tempx"
2271 postdata["unit"] = "°C"
2272 postdata["dashboard_tile"] = "temp_id"
2273 self.colibird_post(postdata)
2274
2275 postdata["DevID"] += 1
2276 postdata["text"] = "{1} (EUI={2})".format(
2277 "", "Feuchte %", data["EUI"]
2278 ).encode("iso-8859-1")
2279 postdata["value"] = data["humidity_percentage"] = (
2280 int(data["data"][18:22], 16) / 65535 * 100
2281 )
2282 postdata["media"] = "Humidity"
2283 postdata["unit"] = "%rH"
2284 postdata["dashboard_tile"] = "humidity_id"
2285 self.colibird_post(postdata)
2286
2287 postdata["DevID"] += 1
2288 postdata["text"] = "{1} EUI={2})".format(
2289 "", "CO2", data["EUI"]
2290 ).encode("iso-8859-1")
2291 postdata["value"] = data["CO2"] = (
2292 int(data["data"][34:38], 16) - 32768
2293 )
2294 postdata["media"] = "CO2"
2295 postdata["unit"] = "ppm"
2296 postdata["dashboard_tile"] = "co2_id"
2297 self.colibird_post(postdata)
2298
2299 postdata["DevID"] += 1
2300 postdata["text"] = "{1} (EUI={2})".format(
2301 "", "Batteriestand V", data["EUI"]
2302 ).encode("iso-8859-1")
2303 postdata["value"] = data["Battery_Voltage"] = (
2304 int(data["data"][10:14], 16) / 1000
2305 )
2306 postdata["media"] = "batlevelV"
2307 postdata["unit"] = "V"
2308 postdata["dashboard_tile"] = ""
2309 self.colibird_post(postdata)
2310
2311 postdata["DevID"] += 1
2312 postdata["text"] = "{1} (EUI={2})".format(
2313 "", "Luftdruck Pa", data["EUI"]
2314 ).encode("iso-8859-1")
2315 postdata["value"] = data["Air_Pressure"] = (
2316 int(data["data"][22:26], 16) * 2
2317 )
2318 postdata["media"] = "airpressure"
2319 postdata["unit"] = "pa"
2320 postdata["dashboard_tile"] = "airpressure_id"
2321 self.colibird_post(postdata)
2322
2323 postdata["DevID"] += 1
2324 postdata["text"] = "{1} (EUI={2})".format(
2325 "", "Helligkeit", data["EUI"]
2326 ).encode("iso-8859-1")
2327 data["ch0"] = int(data["data"][26:30], 16)
2328 data["ch1"] = int(data["data"][30:34], 16)
2329 postdata["value"] = data["Illuminance"] = (
2330 max(
2331 max(
2332 1.0 * data["ch0"] - 1.64 * data["ch1"],
2333 0.59 * data["ch0"] - 0.86 * data["ch1"],
2334 ),
2335 0,
2336 )
2337 * 1.5504
2338 )
2339 postdata["media"] = "brightness"
2340 postdata["unit"] = "lx"
2341 postdata["dashboard_tile"] = ""
2342 self.colibird_post(postdata)
2343
2344 postdata["DevID"] += 1
2345 postdata["text"] = "{1} (EUI={2})".format(
2346 "", "Bewegungen", data["EUI"]
2347 ).encode("iso-8859-1")
2348 postdata["value"] = data["motions"] = int(data["data"][46:50], 16)
2349 postdata["media"] = "motion"
2350 postdata["unit"] = ""
2351 self.colibird_post(postdata)
2352
2353 postdata["DevID"] += 1
2354 postdata["text"] = "{1} (EUI={2})".format(
2355 "", "VOC", data["EUI"]
2356 ).encode("iso-8859-1")
2357 postdata["value"] = data["voc"] = int(data["data"][50:54], 16)
2358 postdata["media"] = "voc"
2359 postdata["unit"] = "ppb"
2360 self.colibird_post(postdata)
2361
2362 elif data["device-type"] == "DecentlabClimate":
2363 if len(data["data"]) == 62:
2364 # DevSpezifikation folgt
2365 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
2366 "",
2367 "Temperatur",
2368 postdata["DevID"],
2369 postdata["SubDevID"],
2370 data["EUI"],
2371 ).encode("iso-8859-1")
2372 postdata["value"] = data["temperature"] = (
2373 int(data["data"][10:14], 16) / 65536 * 175.72 - 46.85
2374 )
2375 postdata["media"] = data["media"] = "tempx"
2376 postdata["unit"] = "°C"
2377 if "outdoor" in data:
2378 if data["outdoor"] == 1:
2379 postdata["dashboard_tile"] = "temp_od"
2380 else:
2381 postdata["dashboard_tile"] = "temp_id"
2382 self.colibird_post(postdata)
2383
2384 postdata["DevID"] += 1
2385 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
2386 "",
2387 "Feuchte %",
2388 postdata["DevID"],
2389 postdata["SubDevID"],
2390 data["EUI"],
2391 ).encode("iso-8859-1")
2392 postdata["value"] = data["humidity_percentage"] = (
2393 int(data["data"][14:18], 16) / 65536 * 125 - 6
2394 )
2395 postdata["media"] = "Humidity"
2396 postdata["unit"] = "%rH"
2397 if "outdoor" in data:
2398 if data["outdoor"] == 1:
2399 postdata["dashboard_tile"] = "humidity_od"
2400 else:
2401 postdata["dashboard_tile"] = "humidity_id"
2402 self.colibird_post(postdata)
2403
2404 postdata["DevID"] += 1
2405 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
2406 "", "CO2", postdata["DevID"], postdata["SubDevID"], data["EUI"]
2407 ).encode("iso-8859-1")
2408 postdata["value"] = data["CO2"] = (
2409 int(data["data"][26:30], 16) - 32768
2410 )
2411 postdata["media"] = "CO2"
2412 postdata["unit"] = "ppm"
2413 if "outdoor" in data:
2414 if data["outdoor"] == 1:
2415 postdata["dashboard_tile"] = "co2_od"
2416 else:
2417 postdata["dashboard_tile"] = "co2_id"
2418 self.colibird_post(postdata)
2419
2420 postdata["DevID"] += 1
2421 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
2422 "",
2423 "Batteriestand V",
2424 postdata["DevID"],
2425 postdata["SubDevID"],
2426 data["EUI"],
2427 ).encode("iso-8859-1")
2428 postdata["value"] = data["Battery_Voltage"] = (
2429 int(data["data"][58:62], 16) / 1000
2430 )
2431 postdata["media"] = "batlevelV"
2432 postdata["unit"] = "V"
2433 postdata["dashboard_tile"] = ""
2434 self.colibird_post(postdata)
2435
2436 postdata["DevID"] += 1
2437 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
2438 "",
2439 "Luftdruck Pa",
2440 postdata["DevID"],
2441 postdata["SubDevID"],
2442 data["EUI"],
2443 ).encode("iso-8859-1")
2444 postdata["value"] = data["Air_Pressure"] = (
2445 int(data["data"][22:26], 16) * 2
2446 )
2447 postdata["media"] = "airpressure"
2448 postdata["unit"] = "pa"
2449 if "outdoor" in data:
2450 if data["outdoor"] == 1:
2451 postdata["dashboard_tile"] = "airpressure_od"
2452 else:
2453 postdata["dashboard_tile"] = "airpressure_id"
2454 self.colibird_post(postdata)
2455
2456 elif data["device-type"] == "DecentlabLevel":
2457 if len(data["data"]) == 22:
2458 # DevSpezifikation folgt
2459 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
2460 "",
2461 "Distanz",
2462 postdata["DevID"],
2463 postdata["SubDevID"],
2464 data["EUI"],
2465 ).encode("iso-8859-1")
2466 postdata["value"] = data["distance"] = int(data["data"][10:14], 16)
2467 postdata["media"] = data["media"] = "distance"
2468 postdata["unit"] = "mm"
2469 self.colibird_post(postdata)
2470
2471 postdata["DevID"] += 1
2472 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
2473 "",
2474 "Batteriestand V",
2475 postdata["DevID"],
2476 postdata["SubDevID"],
2477 data["EUI"],
2478 ).encode("iso-8859-1")
2479 postdata["value"] = data["Battery_Voltage"] = (
2480 int(data["data"][18:22], 16) / 1000
2481 )
2482 postdata["media"] = "batlevelV"
2483 postdata["unit"] = "V"
2484 self.colibird_post(postdata)
2485
2486 if "levelempty" in data:
2487 postdata["DevID"] += 1
2488 postdata[
2489 "text"
2490 ] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
2491 "",
2492 "Füllstand %",
2493 postdata["DevID"],
2494 postdata["SubDevID"],
2495 data["EUI"],
2496 ).encode(
2497 "iso-8859-1"
2498 )
2499 postdata["value"] = data["Level_%"] = (
2500 100
2501 / (data["levelempty"] - data["levelfull"])
2502 * (data["levelempty"] - data["distance"])
2503 )
2504 postdata["media"] = "level"
2505 postdata["unit"] = "%"
2506 self.colibird_post(postdata)
2507
2508 elif data["device-type"] == "Tekelek766":
2509 # DevSpezifikation folgt
2510 if len(data["data"]) == 40:
2511 data["status"] = int(data["data"][0:2], 16)
2512 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
2513 "",
2514 "Distanz",
2515 postdata["DevID"],
2516 postdata["SubDevID"],
2517 data["EUI"],
2518 ).encode("iso-8859-1")
2519
2520 if data["status"] == 16:
2521 if len(data["data"]) == 40:
2522 postdata["value"] = data["distance"] = (
2523 int(data["data"][8:12], 16) * 10
2524 )
2525 postdata["media"] = data["media"] = "distance"
2526 postdata["unit"] = "mm"
2527 self.colibird_post(postdata)
2528
2529 postdata["DevID"] += 1
2530 postdata[
2531 "text"
2532 ] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
2533 "",
2534 "Temperatur",
2535 postdata["DevID"],
2536 postdata["SubDevID"],
2537 data["EUI"],
2538 ).encode(
2539 "iso-8859-1"
2540 )
2541 postdata["value"] = data["temperature"] = int(
2542 data["data"][12:14], 16
2543 )
2544 postdata["media"] = "tempx"
2545 postdata["unit"] = "°C"
2546 self.colibird_post(postdata)
2547
2548 if "levelempty" in data:
2549 postdata["DevID"] += 1
2550 postdata[
2551 "text"
2552 ] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
2553 "",
2554 "Füllstand %",
2555 postdata["DevID"],
2556 postdata["SubDevID"],
2557 data["EUI"],
2558 ).encode(
2559 "iso-8859-1"
2560 )
2561 postdata["value"] = data["Level_%"] = (
2562 100
2563 / (data["levelempty"] - data["levelfull"])
2564 * (data["levelempty"] - data["distance"])
2565 )
2566 postdata["media"] = "level"
2567 postdata["unit"] = "%"
2568 self.colibird_post(postdata)
2569
2570 if data["status"] == 48:
2571 if len(data["data"]) == 36:
2572 postdata["value"] = data["distance"] = (
2573 int(data["data"][28:32], 16) * 10
2574 )
2575 postdata["media"] = data["media"] = "distance"
2576 postdata["unit"] = "mm"
2577 self.colibird_post(postdata)
2578
2579 postdata["DevID"] += 1
2580 postdata[
2581 "text"
2582 ] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
2583 "",
2584 "Temperatur",
2585 postdata["DevID"],
2586 postdata["SubDevID"],
2587 data["EUI"],
2588 ).encode(
2589 "iso-8859-1"
2590 )
2591 postdata["value"] = data["temperature"] = int(
2592 data["data"][32:34], 16
2593 )
2594 postdata["media"] = "tempx"
2595 postdata["unit"] = "°C"
2596 self.colibird_post(postdata)
2597
2598 if "levelempty" in data:
2599 postdata["DevID"] += 1
2600 postdata[
2601 "text"
2602 ] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
2603 "",
2604 "Füllstand %",
2605 postdata["DevID"],
2606 postdata["SubDevID"],
2607 data["EUI"],
2608 ).encode(
2609 "iso-8859-1"
2610 )
2611 postdata["value"] = data["Level_%"] = (
2612 100
2613 / (data["levelempty"] - data["levelfull"])
2614 * (data["levelempty"] - data["distance"])
2615 )
2616 postdata["media"] = "level"
2617 postdata["unit"] = "%"
2618 self.colibird_post(postdata)
2619
2620 elif data["device-type"] == "BoschParking":
2621 # DevSpezifikation folgt
2622 data["status"] = int(data["data"][0:2], 16)
2623 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
2624 "", "Parking", postdata["DevID"], postdata["SubDevID"], data["EUI"]
2625 ).encode("iso-8859-1")
2626 postdata["media"] = data["media"] = "parking"
2627 postdata["unit"] = ""
2628 postdata["dashboard_tile"] = "parking_od"
2629 if data["status"] == 1:
2630 if len(data["data"]) == 16:
2631 postdata["value"] = data["parking_State"] = int(
2632 data["data"][4:6], 16
2633 )
2634 self.colibird_post(postdata)
2635 if data["status"] == 2:
2636 if len(data["data"]) == 10:
2637 postdata["value"] = data["parking_State"] = int(
2638 data["data"][8:10], 16
2639 )
2640 self.colibird_post(postdata)
2641
2642 elif data["device-type"] == "BoschParkingV2":
2643 # DevSpezifikation folgt
2644 if len(data["data"]) == 2:
2645 postdata["text"] = "{1} (EUI={2})".format(
2646 "", "Parking", data["EUI"]
2647 ).encode("iso-8859-1")
2648 postdata["media"] = data["media"] = "parking"
2649 postdata["unit"] = ""
2650 postdata["dashboard_tile"] = "parking_od"
2651 postdata["value"] = data["parking_State"] = int(
2652 data["data"][0:2], 16
2653 )
2654 self.colibird_post(postdata)
2655
2656 elif data["device-type"] == "AdeunisPulse2":
2657 data["code"] = int(data["data"][0:2], 16)
2658 if data["code"] == 70: # Daten kommen mit 46, 46 in dezimal sind 70
2659 data["pulse1_count"] = unpack(">I", unhexlify(data["data"][4:12]))[
2660 0
2661 ]
2662 data["pulse2_count"] = unpack(">I", unhexlify(data["data"][12:20]))[
2663 0
2664 ]
2665 postdata[
2666 "text"
2667 ] = "LoRaWAN EUI={0} DevID={1} type={2} channel={3}".format(
2668 data["EUI"], data["DevID"], data["device-type"], 1
2669 ).encode(
2670 "iso-8859-1"
2671 )
2672 if "factor" in data:
2673 postdata["value"] = data["pulse1_count"] * data["factor"]
2674 else:
2675 postdata["value"] = data["pulse1_count"]
2676 if "media" in data:
2677 postdata["media"] = data["media"]
2678 self.colibird_post(postdata)
2679 if "send2" in data and bool(data["send2"]):
2680 postdata["DevID"] = int(postdata["DevID"]) + 1
2681 postdata[
2682 "text"
2683 ] = "LoRaWAN EUI={0} DevID={1} type={2} channel={3}".format(
2684 data["EUI"], postdata["DevID"], data["device-type"], 2
2685 ).encode(
2686 "iso-8859-1"
2687 )
2688 if "factor2" in data:
2689 postdata["value"] = data["pulse2_count"] * data["factor2"]
2690 else:
2691 postdata["value"] = data["pulse2_count"]
2692 if "media2" in data:
2693 postdata["media"] = data["media2"]
2694 self.colibird_post(postdata)
2695
2696 elif data["device-type"] == "AdeunisFTD":
2697 if len(data["data"]) == 34 or len(data["data"]) == 16:
2698 postdata["text"] = "{1} (EUI={2})".format(
2699 "", "Feldtestgerät", data["EUI"]
2700 ).encode("iso-8859-1")
2701 postdata["value"] = unpack("<B", unhexlify(data["data"][2:4]))[0]
2702 postdata["media"] = data["media"] = "tempx"
2703 postdata["unit"] = "°C"
2704 self.colibird_post(postdata)
2705
2706 elif data["device-type"] == "AdeunisExTemp":
2707 data["code"] = int(data["data"][0:2], 16)
2708 if data["code"] == 67: # Daten kommen mit 43, 43 in dezimal sind 67
2709 data["ext_temp"] = unpack(">H", unhexlify(data["data"][12:16]))[0]
2710 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
2711 "",
2712 "Temperatur",
2713 postdata["DevID"],
2714 postdata["SubDevID"],
2715 data["EUI"],
2716 ).encode("iso-8859-1")
2717 postdata["value"] = data["ext_temp"] / 10
2718 postdata["media"] = data["media"] = "tempx"
2719 postdata["unit"] = "°C"
2720 self.colibird_post(postdata)
2721
2722 elif data["device-type"] == "Holley":
2723 if len(data["data"]) == 8:
2724 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
2725 "",
2726 "Strom",
2727 postdata["DevID"],
2728 postdata["SubDevID"],
2729 data["EUI"],
2730 ).encode("iso-8859-1")
2731 postdata["value"] = int(data["data"][2:8], 16)
2732 postdata["media"] = data["media"] = "electricity"
2733 postdata["unit"] = "kwh"
2734 self.colibird_post(postdata)
2735
2736 elif data["device-type"] == "ZENNERWater":
2737 data["datatype"] = int(data["data"][0:1], 16)
2738 # flips data bytes
2739 # input:Hexadecimal string
2740 # output:flipped Hexadecimal string
2741 def flipBytes(data):
2742 return "".join(map(str.__add__, data[-2::-2], data[-1::-2]))
2743
2744 # creates timestamp from binary in given format
2745 # input:binary
2746 # output:timestamp
2747 def binaryToTimestampD1(data):
2748 year = str(int(data[0:4] + "" + data[8:11], 2))
2749 month = str(int(data[4:8], 2))
2750 day = str(int(data[11:16], 2))
2751
2752 if len(data) > 17:
2753 hour = str(int(data[19:24], 2))
2754 minute = str(int(data[26:32], 2))
2755 return datetime.datetime.strptime(
2756 year + "-" + month + "-" + day + "-" + hour + "-" + minute,
2757 "%y-%m-%d-%H-%M",
2758 )
2759 else:
2760 return datetime.datetime.strptime(
2761 year + "-" + month + "-" + day, "%y-%m-%d"
2762 )
2763
2764 # creates timestamp from binary in given format and set day to 1
2765 # input:binary
2766 # output:timestamp
2767 def binaryToTimestampD1(data):
2768 year = str(int(data[0:4] + "" + data[8:11], 2))
2769 month = str(int(data[4:8], 2))
2770 # day = str(int(data[11:16],2))
2771 day = str(1)
2772
2773 if len(data) > 17:
2774 hour = str(int(data[19:24], 2))
2775 minute = str(int(data[26:32], 2))
2776 return datetime.datetime.strptime(
2777 year + "-" + month + "-" + day + "-" + hour + "-" + minute,
2778 "%y-%m-%d-%H-%M",
2779 )
2780 else:
2781 return datetime.datetime.strptime(
2782 year + "-" + month + "-" + day, "%y-%m-%d"
2783 )
2784
2785 # creates timestamp from binary in given format and set day to 15
2786 # input:binary
2787 # output:timestamp
2788 def binaryToTimestampD15(data):
2789 year = str(int(data[0:4] + "" + data[8:11], 2))
2790 month = str(int(data[4:8], 2))
2791 # day = str(int(data[11:16],2))
2792 day = str(15)
2793
2794 if len(data) > 17:
2795 hour = str(int(data[19:24], 2))
2796 minute = str(int(data[26:32], 2))
2797 return datetime.datetime.strptime(
2798 year + "-" + month + "-" + day + "-" + hour + "-" + minute,
2799 "%y-%m-%d-%H-%M",
2800 )
2801 else:
2802 return datetime.datetime.strptime(
2803 year + "-" + month + "-" + day, "%y-%m-%d"
2804 )
2805
2806 # creates Text in post text format
2807 # input:
2808 # output:string encoded in iso-8859-1
2809 # def postText(devId,subDevId,dataEUI):
2810 # return "{1} (DevID={2} SubDevID={3} EUI={4})".format(
2811 # "", "Wasserzähler", devId, subDevId, dataEUI).encode("iso-8859-1")
2812
2813 if data["datatype"] == 1:
2814 data["subtype"] = int(data["data"][1:2], 16)
2815 if data["subtype"] == 1 or data["subtype"] == 2:
2816 postdata["text"] = "Consumption, Watermeter (EUI={0})".format(
2817 data["EUI"]
2818 ).encode("iso-8859-1")
2819 postdata["value"] = data["watervalue"] = unpack(
2820 "<I", unhexlify(data["data"][2:10])
2821 )[0]
2822 # postdata["value"] = int(flipBytes(data[2:10]),16)
2823 postdata["media"] = data["media"] = "water"
2824 postdata["unit"] = "l"
2825 self.colibird_post(postdata)
2826
2827 if data["datatype"] == 2:
2828 data["subtype"] = int(data["data"][1:2], 16)
2829 if data["subtype"] == 1 or data["subtype"] == 2:
2830 postdata["text"] = "Consumption, Watermeter (EUI={0})".format(
2831 data["EUI"]
2832 ).encode("iso-8859-1")
2833 postdata["time"] = binaryToTimestampD1(
2834 bin(int(flipBytes(data["data"][2:10]), 16)).replace(
2835 "0b", "00"
2836 )
2837 )
2838 postdata["value"] = int(flipBytes(data["data"][10:18]), 16)
2839 postdata["media"] = data["media"] = "water"
2840 postdata["unit"] = "l"
2841 self.colibird_post(postdata)
2842
2843 if data["datatype"] == 3:
2844 data["subtype"] = int(data["data"][1:2], 16)
2845 if data["subtype"] == 1 or data["subtype"] == 2:
2846 postdata["text"] = "Consumption, Watermeter (EUI={0})".format(
2847 data["EUI"]
2848 ).encode("iso-8859-1")
2849 postdata["time"] = binaryToTimestampD1(
2850 bin(int(flipBytes(data["data"][2:6]), 16)).replace(
2851 "0b", "00"
2852 )
2853 )
2854 postdata["value"] = int(flipBytes(data["data"][6:14]), 16)
2855 postdata["media"] = data["media"] = "water"
2856 postdata["unit"] = "l"
2857 self.colibird_post(postdata)
2858
2859 postdata["time"] = binaryToTimestampD15(
2860 bin(int(flipBytes(data["data"][2:6]), 16)).replace(
2861 "0b", "00"
2862 )
2863 )
2864 postdata["value"] = int(flipBytes(data["data"][14:22]), 16)
2865 postdata["media"] = data["media"] = "water"
2866 postdata["unit"] = "l"
2867 self.colibird_post(postdata)
2868
2869 if data["datatype"] == 4:
2870 data["subtype"] = int(data["data"][1:2], 16)
2871 if data["subtype"] == 1 or data["subtype"] == 2:
2872 postdata["text"] = "Consumption, Watermeter (EUI={0})".format(
2873 data["EUI"]
2874 ).encode("iso-8859-1")
2875 postdata["time"] = binaryToTimestamp(
2876 bin(int(flipBytes(data["data"][2:6]), 16)).replace(
2877 "0b", "00"
2878 )
2879 )
2880 postdata["value"] = int(flipBytes(data["data"][6:14]), 16)
2881 postdata["media"] = data["media"] = "water"
2882 postdata["unit"] = "l"
2883 self.colibird_post(postdata)
2884
2885 elif data["device-type"] == "ZENNERHKV":
2886 data["datatype"] = int(data["data"][0:1], 16)
2887 # flips data bytes
2888 # input:Hexadecimal string
2889 # output:flipped Hexadecimal string
2890 def flipBytes(data):
2891 return "".join(map(str.__add__, data[-2::-2], data[-1::-2]))
2892
2893 # creates timestamp from binary in given format
2894 # input:binary
2895 # output:timestamp
2896 def binaryToTimestampD1(data):
2897 year = str(int(data[0:4] + "" + data[8:11], 2))
2898 month = str(int(data[4:8], 2))
2899 day = str(int(data[11:16], 2))
2900
2901 if len(data) > 17:
2902 hour = str(int(data[19:24], 2))
2903 minute = str(int(data[26:32], 2))
2904 return datetime.datetime.strptime(
2905 year + "-" + month + "-" + day + "-" + hour + "-" + minute,
2906 "%y-%m-%d-%H-%M",
2907 )
2908 else:
2909 return datetime.datetime.strptime(
2910 year + "-" + month + "-" + day, "%y-%m-%d"
2911 )
2912
2913 # creates timestamp from binary in given format and set day to 1
2914 # input:binary
2915 # output:timestamp
2916 def binaryToTimestampD1(data):
2917 year = str(int(data[0:4] + "" + data[8:11], 2))
2918 month = str(int(data[4:8], 2))
2919 # day = str(int(data[11:16],2))
2920 day = str(1)
2921
2922 if len(data) > 17:
2923 hour = str(int(data[19:24], 2))
2924 minute = str(int(data[26:32], 2))
2925 return datetime.datetime.strptime(
2926 year + "-" + month + "-" + day + "-" + hour + "-" + minute,
2927 "%y-%m-%d-%H-%M",
2928 )
2929 else:
2930 return datetime.datetime.strptime(
2931 year + "-" + month + "-" + day, "%y-%m-%d"
2932 )
2933
2934 # creates timestamp from binary in given format and set day to 15
2935 # input:binary
2936 # output:timestamp
2937 def binaryToTimestampD15(data):
2938 year = str(int(data[0:4] + "" + data[8:11], 2))
2939 month = str(int(data[4:8], 2))
2940 # day = str(int(data[11:16],2))
2941 day = str(15)
2942
2943 if len(data) > 17:
2944 hour = str(int(data[19:24], 2))
2945 minute = str(int(data[26:32], 2))
2946 return datetime.datetime.strptime(
2947 year + "-" + month + "-" + day + "-" + hour + "-" + minute,
2948 "%y-%m-%d-%H-%M",
2949 )
2950 else:
2951 return datetime.datetime.strptime(
2952 year + "-" + month + "-" + day, "%y-%m-%d"
2953 )
2954
2955 # creates Text in post text format
2956 # input:
2957 # output:string encoded in iso-8859-1
2958 # def postText(devId,subDevId,dataEUI):
2959 # return "{1} (DevID={2} SubDevID={3} EUI={4})".format(
2960 # "", "Heizkostenverteiler", devId, subDevId, dataEUI).encode("iso-8859-1")
2961
2962 if data["datatype"] == 1:
2963 data["subtype"] = int(data["data"][1:2], 16)
2964 if data["subtype"] == 1 or data["subtype"] == 2:
2965 postdata[
2966 "text"
2967 ] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
2968 "",
2969 "Heizkostenverteiler",
2970 postdata["DevID"],
2971 postdata["SubDevID"],
2972 data["EUI"],
2973 ).encode(
2974 "iso-8859-1"
2975 )
2976 postdata["value"] = data["heatvalue"] = unpack(
2977 "<I", unhexlify(data["data"][2:10])
2978 )[0]
2979 # postdata["value"] = int(flipBytes(data["data"][2:10]),16)
2980 postdata["media"] = data["media"] = "heat"
2981 postdata["unit"] = "Wh"
2982 self.colibird_post(postdata)
2983
2984 if data["datatype"] == 2:
2985 data["subtype"] = int(data["data"][1:2], 16)
2986 if data["subtype"] == 1 or data["subtype"] == 2:
2987 postdata[
2988 "text"
2989 ] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
2990 "",
2991 "Heizkostenverteiler",
2992 postdata["DevID"],
2993 postdata["SubDevID"],
2994 data["EUI"],
2995 ).encode(
2996 "iso-8859-1"
2997 )
2998 postdata["time"] = binaryToTimestampD1(
2999 bin(int(flipBytes(data["data"][2:10]), 16)).replace(
3000 "0b", "00"
3001 )
3002 )
3003 postdata["value"] = int(flipBytes(data["data"][10:18]), 16)
3004 postdata["media"] = data["media"] = "heat"
3005 postdata["unit"] = "Wh"
3006 self.colibird_post(postdata)
3007
3008 if data["datatype"] == 3:
3009 data["subtype"] = int(data["data"][1:2], 16)
3010 if data["subtype"] == 1 or data["subtype"] == 2:
3011 postdata[
3012 "text"
3013 ] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
3014 "",
3015 "Heizkostenverteiler",
3016 postdata["DevID"],
3017 postdata["SubDevID"],
3018 data["EUI"],
3019 ).encode(
3020 "iso-8859-1"
3021 )
3022 postdata["time"] = binaryToTimestampD1(
3023 bin(int(flipBytes(data["data"][2:6]), 16)).replace(
3024 "0b", "00"
3025 )
3026 )
3027 postdata["value"] = int(flipBytes(data["data"][6:14]), 16)
3028 postdata["media"] = data["media"] = "heat"
3029 postdata["unit"] = "Wh"
3030 self.colibird_post(postdata)
3031
3032 postdata["time"] = binaryToTimestampD15(
3033 bin(int(flipBytes(data["data"][2:6]), 16)).replace(
3034 "0b", "00"
3035 )
3036 )
3037 postdata["value"] = int(flipBytes(data["data"][14:22]), 16)
3038 postdata["media"] = data["media"] = "heat"
3039 postdata["unit"] = "Wh"
3040 self.colibird_post(postdata)
3041
3042 if data["datatype"] == 4:
3043 data["subtype"] = int(data["data"][1:2], 16)
3044 if data["subtype"] == 1 or data["subtype"] == 2:
3045 postdata[
3046 "text"
3047 ] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
3048 "",
3049 "Heizkostenverteiler",
3050 postdata["DevID"],
3051 postdata["SubDevID"],
3052 data["EUI"],
3053 ).encode(
3054 "iso-8859-1"
3055 )
3056 postdata["time"] = binaryToTimestamp(
3057 bin(int(flipBytes(data["data"][2:6]), 16)).replace(
3058 "0b", "00"
3059 )
3060 )
3061 postdata["value"] = int(flipBytes(data["data"][6:14]), 16)
3062 postdata["media"] = data["media"] = "heat"
3063 postdata["unit"] = "Wh"
3064 self.colibird_post(postdata)
3065
3066 elif data["device-type"] == "ComtacTeaser":
3067 # DevSpezifikation folgt
3068 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
3069 "",
3070 "Temperatur",
3071 postdata["DevID"],
3072 postdata["SubDevID"],
3073 data["EUI"],
3074 ).encode("iso-8859-1")
3075 postdata["value"] = data["temperature"] = unpack(
3076 ">B", unhexlify(data["data"][12:14])
3077 )[0]
3078 postdata["media"] = data["media"]
3079 self.colibird_post(postdata)
3080 postdata["DevID"] += 1
3081 postdata["value"] = data["humidity_percentage"] = unpack(
3082 ">B", unhexlify(data["data"][14:16])
3083 )[0]
3084 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
3085 "",
3086 "Feuchte %",
3087 postdata["DevID"],
3088 postdata["SubDevID"],
3089 data["EUI"],
3090 ).encode("iso-8859-1")
3091 self.colibird_post(postdata)
3092
3093 elif data["device-type"] == "netvoxR718G":
3094 if len(data["data"]) == 22:
3095 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
3096 "",
3097 "Helligkeit",
3098 postdata["DevID"],
3099 postdata["SubDevID"],
3100 data["EUI"],
3101 ).encode("iso-8859-1")
3102 postdata["value"] = int(data["data"][8:16], 16)
3103 postdata["media"] = "brightness"
3104 postdata["unit"] = "lx"
3105 self.colibird_post(postdata)
3106
3107 postdata["DevID"] += 1
3108 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
3109 "",
3110 "Batteriestand V",
3111 postdata["DevID"],
3112 postdata["SubDevID"],
3113 data["EUI"],
3114 ).encode("iso-8859-1")
3115 postdata["value"] = data["Battery_Voltage"] = (
3116 int(data["data"][6:8], 16) / 10
3117 )
3118 postdata["media"] = "batlevelV"
3119 postdata["unit"] = "V"
3120 self.colibird_post(postdata)
3121
3122 elif data["device-type"] == "NetvoxR311W":
3123 if len(data["data"]) == 22:
3124 data["type"] = int(data["data"][2:4], 16)
3125 data["reporttype"] = int(data["data"][4:6], 16)
3126 if data["type"] == 6 and data["reporttype"] == 1:
3127 postdata["dashboard_tile"] = ""
3128 postdata["media"] = "leckage"
3129 postdata["unit"] = ""
3130 postdata["text"] = "{1} (EUI={2})".format(
3131 "", "Wasserleckage 1", data["EUI"]
3132 ).encode("iso-8859-1")
3133 postdata["value"] = int(data["data"][8:10], 16)
3134 self.colibird_post(postdata)
3135
3136 postdata["DevID"] += 1
3137 postdata["text"] = "{1} (EUI={2})".format(
3138 "", "Wasserleckage 2", data["EUI"]
3139 ).encode("iso-8859-1")
3140 postdata["value"] = int(data["data"][10:12], 16)
3141 self.colibird_post(postdata)
3142
3143 postdata["DevID"] += 1
3144 data["battery"] = int(data["data"][6:8], 16)
3145 postdata["value"] = (data["battery"] & 127) / 10
3146 postdata["text"] = "{1} (EUI={2})".format(
3147 "", "Batteriestand V", data["EUI"]
3148 ).encode("iso-8859-1")
3149 postdata["media"] = "batlevelv"
3150 postdata["unit"] = "V"
3151 self.colibird_post(postdata)
3152
3153 elif data["device-type"] == "NetvoxRB02I":
3154 if len(data["data"]) == 22 and data["frameport"] == 6:
3155 data["type"] = int(data["data"][2:4], 16)
3156 data["reporttype"] = int(data["data"][4:6], 16)
3157 if (data["type"] in (16, 49, 77, 85)) and data["reporttype"] == 1:
3158 postdata["dashboard_tile"] = ""
3159 postdata["media"] = "button"
3160 postdata["unit"] = ""
3161 postdata[
3162 "text"
3163 ] = "Status, Netvox Push Button (EUI={0})".format(
3164 data["EUI"]
3165 ).encode(
3166 "iso-8859-1"
3167 )
3168 postdata["value"] = int(data["data"][8:10], 16)
3169 self.colibird_post(postdata)
3170
3171 postdata["DevID"] += 1
3172 data["battery"] = int(data["data"][6:8], 16)
3173 postdata["value"] = (data["battery"] & 127) / 10
3174 postdata[
3175 "text"
3176 ] = "Batterylevel V, Netvox Push Button (EUI={0})".format(
3177 data["EUI"]
3178 ).encode(
3179 "iso-8859-1"
3180 )
3181 postdata["media"] = "batlevelv"
3182 postdata["unit"] = "V"
3183 self.colibird_post(postdata)
3184
3185 elif data["device-type"] == "NetvoxR718MBC":
3186 if len(data["data"]) == 22:
3187 data["type"] = int(data["data"][2:4], 16)
3188 data["reporttype"] = int(data["data"][4:6], 16)
3189 if data["type"] == 44 and data["reporttype"] == 1:
3190 postdata["dashboard_tile"] = ""
3191 postdata["media"] = "activity"
3192 postdata["unit"] = "s"
3193 postdata["text"] = "{1} (EUI={2})".format(
3194 "", "Aktivitätsmesser", data["EUI"]
3195 ).encode("iso-8859-1")
3196 postdata["value"] = unpack(">I", unhexlify(data["data"][8:16]))[
3197 0
3198 ]
3199 self.colibird_post(postdata)
3200
3201 postdata["DevID"] += 1
3202 postdata["value"] = 1
3203 postdata["text"] = "{1} (EUI={2})".format(
3204 "", "Bewegungen", data["EUI"]
3205 ).encode("iso-8859-1")
3206 postdata["media"] = "motion"
3207 postdata["unit"] = ""
3208 self.colibird_post(postdata)
3209
3210 postdata["DevID"] += 1
3211 data["battery"] = int(data["data"][6:8], 16)
3212 postdata["value"] = (data["battery"] & 127) / 10
3213 postdata["text"] = "{1} (EUI={2})".format(
3214 "", "Batteriestand V", data["EUI"]
3215 ).encode("iso-8859-1")
3216 postdata["media"] = "batlevelv"
3217 postdata["unit"] = "V"
3218 self.colibird_post(postdata)
3219
3220 elif data["device-type"] == "NetvoxRA02A":
3221 if len(data["data"]) == 22:
3222 data["type"] = int(data["data"][2:4], 16)
3223 data["reporttype"] = int(data["data"][4:6], 16)
3224 if data["type"] == 10 and data["reporttype"] == 1:
3225 postdata["dashboard_tile"] = ""
3226 postdata["media"] = "smoke"
3227 postdata["unit"] = ""
3228 postdata["text"] = "{1} (EUI={2})".format(
3229 "", "Rauchwarnmelder", data["EUI"]
3230 ).encode("iso-8859-1")
3231 postdata["value"] = int(data["data"][8:10], 16)
3232 self.colibird_post(postdata)
3233
3234 postdata["DevID"] += 1
3235 data["battery"] = int(data["data"][6:8], 16)
3236 postdata["value"] = (data["battery"] & 127) / 10
3237 postdata["text"] = "{1} (EUI={2})".format(
3238 "", "Batteriestand V", data["EUI"]
3239 ).encode("iso-8859-1")
3240 postdata["media"] = "batlevelv"
3241 postdata["unit"] = "V"
3242 self.colibird_post(postdata)
3243
3244 elif data["device-type"] == "netvoxR311G":
3245 if len(data["data"]) == 22:
3246 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
3247 "",
3248 "Helligkeit",
3249 postdata["DevID"],
3250 postdata["SubDevID"],
3251 data["EUI"],
3252 ).encode("iso-8859-1")
3253 postdata["value"] = int(data["data"][8:12], 16)
3254 postdata["media"] = "brightness"
3255 postdata["unit"] = "lx"
3256 self.colibird_post(postdata)
3257
3258 postdata["DevID"] += 1
3259 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
3260 "",
3261 "Batteriestand V",
3262 postdata["DevID"],
3263 postdata["SubDevID"],
3264 data["EUI"],
3265 ).encode("iso-8859-1")
3266 postdata["value"] = data["Battery_Voltage"] = (
3267 int(data["data"][6:8], 16) / 10
3268 )
3269 postdata["media"] = "batlevelV"
3270 postdata["unit"] = "V"
3271 self.colibird_post(postdata)
3272
3273 elif data["device-type"] == "elvacoCMa11L":
3274 if len(data["data"]) == 8 and data["frameport"] == 2:
3275 postdata[
3276 "text"
3277 ] = "Temperature °C, Elvaco CMa11L (EUI={0})".format(
3278 data["EUI"]
3279 ).encode(
3280 "iso-8859-1"
3281 )
3282 postdata["value"] = (
3283 unpack("<h", unhexlify(data["data"][2:6]))[0] / 100
3284 )
3285 postdata["media"] = "tempx"
3286 postdata["unit"] = "°C"
3287 postdata["dashboard_tile"] = "temp_id"
3288 self.colibird_post(postdata)
3289
3290 postdata["DevID"] += 1
3291 postdata[
3292 "text"
3293 ] = "Humidity %, Elvaco CMa11L (EUI={0})".format(
3294 data["EUI"]
3295 ).encode(
3296 "iso-8859-1"
3297 )
3298 postdata["value"] = (
3299 unpack("<b", unhexlify(data["data"][6:8]))[0]
3300 )
3301 postdata["media"] = "humidity"
3302 postdata["unit"] = "%rH"
3303 postdata["dashboard_tile"] = "humidity_id"
3304 self.colibird_post(postdata)
3305
3306
3307 elif data["device-type"] == "nemeuslevel":
3308 if len(data["data"]) == 16:
3309 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
3310 "",
3311 "Füllstand",
3312 postdata["DevID"],
3313 postdata["SubDevID"],
3314 data["EUI"],
3315 ).encode("iso-8859-1")
3316 postdata["value"] = int(data["data"][2:6], 16)
3317 postdata["media"] = "distance"
3318 postdata["unit"] = "cm"
3319 self.colibird_post(postdata)
3320
3321 postdata["DevID"] += 1
3322 postdata["text"] = "{1} (DevID={2} SubDevID={3} EUI={4})".format(
3323 "",
3324 "Temperatur",
3325 postdata["DevID"],
3326 postdata["SubDevID"],
3327 data["EUI"],
3328 ).encode("iso-8859-1")
3329 postdata["value"] = data["Temperature"] = unpack(
3330 ">b", unhexlify(data["data"][6:8])
3331 )[0]
3332 postdata["media"] = "tempx"
3333 postdata["unit"] = "°c"
3334 self.colibird_post(postdata)
3335
3336 elif data["device-type"] == "ElsysV8":
3337 editableData = data["data"]
3338 """
3339 returns given bytes as per datatype
3340 input:int datatype
3341 output:int length of bytes to read
3342 """
3343
3344 def getLengthFromType(dataType):
3345 if dataType == 1 or dataType == 4 or dataType == 7 or dataType == 6:
3346 return 4
3347 elif dataType == 2 or dataType == 5:
3348 return 2
3349
3350 """
3351 adds data to post data array
3352 input:int datatype,int data to be sent
3353 """
3354
3355 def addData(dataType, value, data, devIdIni):
3356 if dataType == 1:
3357 postdata["DevID"] = devIdIni
3358 postdata["media"] = "tempx"
3359 postdata["value"] = value / 10
3360 postdata["unit"] = "°c"
3361 postdata["text"] = postText(
3362 "Temperatur",
3363 postdata["DevID"],
3364 postdata["SubDevID"],
3365 data["EUI"],
3366 )
3367 elif dataType == 2:
3368 postdata["DevID"] = devIdIni + 1
3369 postdata["media"] = "humidity"
3370 postdata["value"] = value
3371 postdata["unit"] = "%rH"
3372 postdata["text"] = postText(
3373 "Feuchtigkeit",
3374 postdata["DevID"],
3375 postdata["SubDevID"],
3376 data["EUI"],
3377 )
3378 elif dataType == 4:
3379 postdata["DevID"] = devIdIni + 2
3380 postdata["media"] = "brightness"
3381 postdata["value"] = value
3382 postdata["unit"] = "lx"
3383 postdata["text"] = postText(
3384 "Helligkeit",
3385 postdata["DevID"],
3386 postdata["SubDevID"],
3387 data["EUI"],
3388 )
3389 elif dataType == 6:
3390 postdata["DevID"] = devIdIni + 3
3391 postdata["media"] = "co2"
3392 postdata["value"] = value
3393 postdata["unit"] = "ppm"
3394 postdata["text"] = postText(
3395 "CO2", postdata["DevID"], postdata["SubDevID"], data["EUI"]
3396 )
3397 elif dataType == 5:
3398 postdata["DevID"] = devIdIni + 4
3399 postdata["media"] = "motion"
3400 postdata["value"] = value
3401 postdata["unit"] = ""
3402 postdata["text"] = postText(
3403 "Bewegunen",
3404 postdata["DevID"],
3405 postdata["SubDevID"],
3406 data["EUI"],
3407 )
3408 elif dataType == 7:
3409 postdata["DevID"] = devIdIni + 5
3410 postdata["media"] = "batlevelv"
3411 postdata["value"] = value / 1000
3412 postdata["unit"] = "V"
3413 postdata["text"] = postText(
3414 "Batteriestand V",
3415 postdata["DevID"],
3416 postdata["SubDevID"],
3417 data["EUI"],
3418 )
3419
3420 """
3421 creates Text in post text format
3422 input:
3423 output:String encoded in iso-8859-1
3424 """
3425
3426 def postText(varName, devId, subDevId, dataEUI):
3427 return "{1} (DevID={2} SubDevID={3} EUI={4})".format(
3428 "", varName, devId, subDevId, dataEUI
3429 ).encode("iso-8859-1")
3430
3431 devIdIni = postdata["DevID"]
3432 while True:
3433 if len(editableData) == 0:
3434 break
3435 dataType = int(editableData[0:2], 16)
3436 dataLength = getLengthFromType(dataType)
3437 value = int(editableData[2 : (dataLength + 2)], 16)
3438 editableData = editableData[(dataLength + 2) : len(editableData)]
3439 if (
3440 dataType == 1
3441 or dataType == 2
3442 or dataType == 4
3443 or dataType == 5
3444 or dataType == 6
3445 or dataType == 7
3446 ):
3447 addData(dataType, value, data, devIdIni)
3448 self.colibird_post(postdata)
3449
3450 elif data["device-type"] == "SensativeStrips":
3451 editableData = data["data"]
3452 """
3453 returns given bytes as per datatype
3454 input:int datatype
3455 output:int length of bytes to read
3456 """
3457
3458 def getLengthFromType(dataType):
3459 if dataType == 2 or dataType == 130:
3460 return 4
3461 elif (
3462 dataType == 1
3463 or dataType == 129
3464 or dataType == 6
3465 or dataType == 136
3466 or dataType == 9
3467 or dataType == 137
3468 or dataType == 11
3469 or dataType == 139
3470 ):
3471 return 2
3472
3473 """
3474 adds data to post data array
3475 input:int datatype,int data to be sent
3476 """
3477
3478 def addData(dataType, value, data, devIdIni):
3479 if dataType == 1 or dataType == 129:
3480 postdata["DevID"] = devIdIni
3481 postdata["media"] = "batlevel"
3482 postdata["value"] = value
3483 postdata["unit"] = "%"
3484 postdata[
3485 "text"
3486 ] = "Battery Level %, Sensative Strips (EUI={0})".format(
3487 data["EUI"]
3488 ).encode(
3489 "iso-8859-1"
3490 )
3491 elif dataType == 2 or dataType == 130:
3492 postdata["DevID"] = devIdIni + 1
3493 postdata["media"] = "tempx"
3494 postdata["dashboard_tile"] = "temp_id"
3495 postdata["value"] = value / 10
3496 postdata["unit"] = "°C"
3497 postdata[
3498 "text"
3499 ] = "Temperature °C, Sensative Strips (EUI={0})".format(
3500 data["EUI"]
3501 ).encode(
3502 "iso-8859-1"
3503 )
3504 elif dataType == 6 or dataType == 136:
3505 postdata["DevID"] = devIdIni + 2
3506 postdata["media"] = "humidity"
3507 postdata["dashboard_tile"] = "humidity_id"
3508 postdata["value"] = value / 2
3509 postdata["unit"] = "%rH"
3510 postdata[
3511 "text"
3512 ] = "Humidity %, Sensative Strips (EUI={0})".format(
3513 data["EUI"]
3514 ).encode(
3515 "iso-8859-1"
3516 )
3517 elif dataType == 9 or dataType == 137:
3518 postdata["DevID"] = devIdIni + 3
3519 postdata["media"] = "door"
3520 postdata["value"] = value
3521 postdata["unit"] = "ppm"
3522 postdata[
3523 "text"
3524 ] = "Door Status, Sensative Strips (EUI={0})".format(
3525 data["EUI"]
3526 ).encode(
3527 "iso-8859-1"
3528 )
3529 elif dataType == 11 or dataType == 139:
3530 postdata["DevID"] = devIdIni + 4
3531 postdata["media"] = "door"
3532 postdata["value"] = value
3533 postdata["unit"] = ""
3534 postdata[
3535 "text"
3536 ] = "Door Status, Sensative Strips (EUI={0})".format(
3537 data["EUI"]
3538 ).encode(
3539 "iso-8859-1"
3540 )
3541
3542 """
3543 creates Text in post text format
3544 input:
3545 output:String encoded in iso-8859-1
3546 """
3547
3548 def postText(varName, devId, subDevId, dataEUI):
3549 return "{1} (DevID={2} SubDevID={3} EUI={4})".format(
3550 "", varName, devId, subDevId, dataEUI
3551 ).encode("iso-8859-1")
3552
3553 devIdIni = postdata["DevID"]
3554 firstround = 1
3555 while True:
3556 if len(editableData) == 0:
3557 break
3558 if firstround == 1:
3559 dataType = int(editableData[4:6], 16)
3560 dataLength = getLengthFromType(dataType)
3561 value = int(editableData[6 : (dataLength + 6)], 16)
3562 editableData = editableData[
3563 (dataLength + 6) : len(editableData)
3564 ]
3565 firstround = 0
3566 else:
3567 dataType = int(editableData[0:2], 16)
3568 dataLength = getLengthFromType(dataType)
3569 value = int(editableData[2 : (dataLength + 2)], 16)
3570 editableData = editableData[
3571 (dataLength + 2) : len(editableData)
3572 ]
3573
3574 if (
3575 dataType == 2
3576 or dataType == 130
3577 or dataType == 6
3578 or dataType == 136
3579 or dataType == 9
3580 or dataType == 137
3581 or dataType == 1
3582 or dataType == 129
3583 ):
3584 addData(dataType, value, data, devIdIni)
3585 self.colibird_post(postdata)
3586
3587 elif data["device-type"] == "UrsalinkUC11T1":
3588 """
3589 Payload example: FRame N= 01 67 13 01 02 68 73
3590 Frame N+1: 03 75 5a
3591 """
3592
3593 editableData = data["data"]
3594 """
3595 returns given bytes as per datatype
3596 input:int datatype
3597 output:int length of bytes to read
3598 """
3599
3600 def getLengthFromType(channel):
3601 if channel == 1:
3602 return 4
3603 elif (
3604 channel == 2
3605 or channel == 3
3606 ):
3607 return 2
3608
3609 """
3610 adds data to post data array
3611 input:int channel,int data to be sent
3612 """
3613
3614 def addData(channel, value, data, devIdIni, channeltype):
3615 if channel == 3 and channeltype == 117:
3616 postdata["DevID"] = devIdIni + 2
3617 postdata["media"] = "batlevel"
3618 postdata["value"] = value
3619 postdata["unit"] = "%"
3620 postdata[
3621 "text"
3622 ] = "Battery Level %, Ursalink UC11-T1 (EUI={0})".format(
3623 data["EUI"]
3624 ).encode(
3625 "iso-8859-1"
3626 )
3627 elif channel == 1 and channeltype == 103:
3628 postdata["DevID"] = devIdIni
3629 postdata["media"] = "tempx"
3630 postdata["value"] = value / 10
3631 postdata["unit"] = "°C"
3632 postdata[
3633 "text"
3634 ] = "Temperature °C, Ursalink UC11-T1 (EUI={0})".format(
3635 data["EUI"]
3636 ).encode(
3637 "iso-8859-1"
3638 )
3639 elif channel == 2 and channeltype == 104:
3640 postdata["DevID"] = devIdIni + 1
3641 postdata["media"] = "humidity"
3642 postdata["value"] = value / 2
3643 postdata["unit"] = "%rH"
3644 postdata[
3645 "text"
3646 ] = "Humidity %, Ursalink UC11-T1 (EUI={0})".format(
3647 data["EUI"]
3648 ).encode(
3649 "iso-8859-1"
3650 )
3651
3652 devIdIni = postdata["DevID"]
3653
3654 while True:
3655 if len(editableData) == 0:
3656 break
3657 channel = int(editableData[0:2], 16)
3658 channeltype = int(editableData[2:4], 16)
3659 dataLength = getLengthFromType(channel)
3660 if channel == 1:
3661 value = (
3662 unpack("<h", unhexlify(editableData[4 : (dataLength + 4)]))[0]
3663 )
3664 elif (channel == 2 or channel == 3):
3665 value = int(editableData[4 : (dataLength + 4)], 16)
3666 editableData = editableData[(dataLength + 4) : len(editableData)]
3667 if (
3668 channel == 1
3669 or channel == 2
3670 or channel == 3
3671 ):
3672 addData(channel, value, data, devIdIni, channeltype)
3673 self.colibird_post(postdata)
3674
3675
3676 elif data["device-type"] == "Ascoel868door":
3677 import binascii
3678 import struct
3679
3680 """
3681 Converts hex string into device info dictionary
3682 Input : String Hex data
3683 Output: Data dictionary containing value for batterywarning, tamper and door
3684 """
3685
3686 def getDeviceInfo(data):
3687 binData = format(int(data[0:2], 16), "0>8b")
3688 dataDict = {}
3689 dataDict["state"] = binData[7:8]
3690 return dataDict
3691
3692 """
3693 Converts hex string into litte indian float
3694 Input : String Hex data
3695 Output: Float value of given data
3696 """
3697
3698 def hexToLittleIndianFloat(data):
3699 return struct.unpack("<f", binascii.unhexlify(data))[0]
3700
3701 """
3702 creates Text in post text format
3703 input:
3704 output:string encoded in iso-8859-1
3705 """
3706
3707 def postText(varName, devId, subDevId, dataEUI):
3708 return "{1} (DevID={2} SubDevID={3} EUI={4})".format(
3709 "", varName, devId, subDevId, dataEUI
3710 ).encode("iso-8859-1")
3711
3712 hexData = data["data"]
3713 postDataMedias = ["tempx", "humidity", "batlevel", "door"]
3714 postDataText = [
3715 "Temperatur",
3716 "Feuchtigkeit",
3717 "Batteriestand",
3718 "Zustand Türe",
3719 ]
3720 postDataUnits = ["°c", "%", "%", ""]
3721 postDataDevIdInc = [0, 1, 2, 3]
3722
3723 if len(hexData) == 20:
3724 deviceInfo = getDeviceInfo(hexData[2:4])
3725 door = int(deviceInfo["state"])
3726 temperature = round(hexToLittleIndianFloat(hexData[4:12]), 2)
3727 humidity = round(hexToLittleIndianFloat(hexData[12:20]), 2)
3728 battery = int(hexData[0:2], 16)
3729 postDataValues = [temperature, humidity, battery, door]
3730
3731 elif len(hexData) == 22:
3732 deviceInfo = getDeviceInfo(hexData[0:2])
3733 door = int(deviceInfo["state"])
3734 temperature = round(hexToLittleIndianFloat(hexData[6:14]), 2)
3735 humidity = round(hexToLittleIndianFloat(hexData[14:22]), 2)
3736 # counter = int(data[2:6], 16)
3737 postDataValues = [temperature, humidity, door]
3738 postDataMedias.remove(postDataMedias[2])
3739 postDataUnits.remove(postDataUnits[2])
3740 postDataText.remove(postDataText[2])
3741 postDataDevIdInc.remove(postDataDevIdInc[2])
3742
3743 devIdIni = postdata["DevID"]
3744 for i in range(len(postDataValues)):
3745 postdata["DevID"] = devIdIni + postDataDevIdInc[i]
3746 postdata["text"] = postText(
3747 postDataText[i],
3748 postdata["DevID"],
3749 postdata["SubDevID"],
3750 data["EUI"],
3751 )
3752 postdata["media"] = postDataMedias[i]
3753 postdata["value"] = postDataValues[i]
3754 postdata["unit"] = postDataUnits[i]
3755 self.colibird_post(postdata)
3756
3757 elif data["device-type"] == "LGUH50" or data["device-type"] == "SUH50":
3758 POST_DEVID_NAME = "DevID"
3759 POST_SUBDEVID_NAME = "SubDevID"
3760 POST_UNIT_NAME = "unit"
3761 POST_VALUE_NAME = "value"
3762 POST_MEDIA_NAME = "media"
3763 POST_TEXT_NAME = "text"
3764
3765 """
3766 usage : to send only data which has this media value
3767 input : String postMedia
3768 output : Bool to send or not
3769 """
3770
3771 def checkPostData(postMedia):
3772 if (
3773 postMedia == "heat"
3774 or postMedia == "water"
3775 or postMedia == "tempr"
3776 ):
3777 return True
3778 else:
3779 return False
3780
3781 """
3782 usage : converts hexadecimal alphabetic string to Decimal string
3783 input : string Hexadecimal alphabets
3784 output : string Decimal based numbers
3785 """
3786
3787 def convertHexToDecStr(data):
3788 hexDic = {
3789 "A": "10",
3790 "B": "11",
3791 "C": "12",
3792 "D": "13",
3793 "E": "14",
3794 "F": "15",
3795 }
3796 for hexKey, strDecVal in hexDic.items():
3797 data = data.replace(hexKey, strDecVal)
3798 return data
3799
3800 """
3801 usage : to process unit as per given factor
3802 input : dictionary Factor based data
3803 output : dictionary data without factor and unit update
3804 """
3805
3806 def factorUpdate(data):
3807 if "factor" in data:
3808 data = unitUpdate(data)
3809 del data["factor"]
3810 return data
3811
3812 """
3813 usage : To flip hexadecimal string and convert it to integer
3814 input : string hexadecimal alphabets
3815 output : int flipped int
3816 """
3817
3818 def flipToInt(data):
3819 return int(
3820 convertHexToDecStr(data[6:8])
3821 + convertHexToDecStr(data[4:6])
3822 + convertHexToDecStr(data[2:4])
3823 + convertHexToDecStr(data[0:2])
3824 )
3825
3826 """
3827 usage : creates postdata as per vif
3828 input : int vif
3829 output : dictionary post data based on vif
3830 """
3831
3832 def getDataFromVif(vif):
3833 output = {}
3834 if vif & 120 == 0:
3835 output[POST_UNIT_NAME] = "Wh"
3836 output["factor"] = 10 ** ((vif & 7) - 3)
3837 output[POST_MEDIA_NAME] = "heat"
3838 output["textName"] = "Wärme"
3839 elif vif & 120 == 1 << 3:
3840 output[POST_MEDIA_NAME] = "heat"
3841 output["textName"] = "Wärme"
3842 output[POST_UNIT_NAME] = "J"
3843 output["factor"] = 10 ** (vif & 7)
3844 elif vif & 120 == 2 << 3:
3845 output[POST_UNIT_NAME] = "m3"
3846 output["factor"] = 10 ** ((vif & 7) - 6)
3847 output[POST_MEDIA_NAME] = "water"
3848 output["textName"] = "Durchflussmenge"
3849 elif vif & 120 == 3 << 3:
3850 output[POST_UNIT_NAME] = "kg"
3851 output["factor"] = 10 ** ((vif & 7) - 3)
3852 # ignore vif=4<<3 ontime
3853 elif vif & 120 == 5 << 3:
3854 output[POST_MEDIA_NAME] = "Power"
3855 output["textName"] = "Power"
3856 output[POST_UNIT_NAME] = "W"
3857 output["factor"] = 10 ** ((vif & 7) - 3)
3858 elif vif & 120 == 6 << 3:
3859 output[POST_UNIT_NAME] = "J/h"
3860 output["factor"] = 10 ** ((vif & 7))
3861 elif vif & 120 == 7 << 3:
3862 output[POST_MEDIA_NAME] = "Flow"
3863 output["textName"] = "Flow"
3864 output[POST_UNIT_NAME] = "m3/h"
3865 output["factor"] = 10 ** ((vif & 7) - 6)
3866 elif vif & 120 == 8 << 3:
3867 output[POST_UNIT_NAME] = "m3/min"
3868 output["factor"] = 10 ** ((vif & 7) - 7)
3869 elif vif & 120 == 9 << 3:
3870 output[POST_UNIT_NAME] = "m3/s"
3871 output["factor"] = 10 ** ((vif & 7) - 9)
3872 elif vif & 120 == 10 << 3:
3873 output[POST_UNIT_NAME] = "kg/h"
3874 output["factor"] = 10 ** ((vif & 7) - 3)
3875 elif vif & 120 == 11 << 3:
3876 # absolute temperatures
3877 output[POST_UNIT_NAME] = "°c"
3878 output["factor"] = 10 ** ((vif & 3) - 3)
3879 output[POST_MEDIA_NAME] = "tempr"
3880 output["textName"] = "Vorlauf-Temperatur"
3881 if bool(vif & 4):
3882 output["textName"] = "Rücklauf-Temperatur"
3883 else:
3884 output["textName"] = "Vorlauf-Temperatur"
3885 elif vif & 120 == 12 << 3:
3886 # temperature difference
3887 output[POST_UNIT_NAME] = "°c"
3888 output["factor"] = 10 ** ((vif & 3) - 3)
3889 elif vif & 120 == 13 << 3:
3890 # date (& time)
3891 output["type"] = "date"
3892 # ignore vif=14<<3: averaging duration
3893 elif vif & 120 == 15 << 3:
3894 # fabrication number = serial number
3895 output[POST_UNIT_NAME] = "serial"
3896 output["textName"] = "serial"
3897 output[POST_MEDIA_NAME] = "Zahlernummer"
3898 else:
3899 print("unknown VIF {0}".format(vif))
3900 return output
3901
3902 """
3903 usage : to get post data from process string and its limit and which part based on vif and valueEnd
3904 input : String String to process data from | int vif | int valueEnd | int DevId | int SubDevId | int EUI
3905 output : dictionary postdata as per string,vif
3906 """
3907
3908 def getPostData(processString, vif, valueEnd, DevId, SubDivId, EUI):
3909 postData = {}
3910 postData = getDataFromVif(vif)
3911 postData[POST_VALUE_NAME] = flipToInt(processString[0:valueEnd])
3912 postData = factorUpdate(postData)
3913 postData[POST_VALUE_NAME] = round(postData[POST_VALUE_NAME], 2)
3914 # postData[POST_TEXT_NAME] = postText(postData["textName"],postdata[POST_DEVID_NAME],data["EUI"])
3915 # del postData["textName"]
3916 return postData
3917
3918 """
3919 usage : get increment for string based on loop
3920 input : int loop turn
3921 output : int increment
3922 """
3923
3924 def getValueIncrement(loopTurn):
3925 if loopTurn == 1 or loopTurn == 2 or loopTurn == 7:
3926 return 8
3927 elif loopTurn == 3 or loopTurn == 4:
3928 return 6
3929 elif loopTurn == 5 or loopTurn == 6 or loopTurn == 8:
3930 return 4
3931
3932 """
3933 usage : merge 2 dictionary
3934 input : dictionary 1
3935 output : dictionary 2
3936 """
3937
3938 def merge(dict1, dict2):
3939 return dict2.update(dict1)
3940
3941 """
3942 usage : creates Text in post text format
3943 input : string varName | int devId | int subDevId | int dataEUI
3944 output : string encoded in iso-8859-1
3945 """
3946
3947 def postText(varName, serial, dataEUI):
3948 return "{1} ({2}, EUI={3})".format(
3949 "", varName, serial, dataEUI
3950 ).encode("iso-8859-1")
3951
3952 """
3953 usage : sends postdata to colibird
3954 input : list postDataList
3955 output :
3956 """
3957
3958 def sendPostData(postDataList):
3959 serial = next(
3960 (
3961 postData
3962 for postData in postDataList
3963 if postData[POST_MEDIA_NAME] == "Zahlernummer"
3964 )
3965 )[POST_VALUE_NAME]
3966 i = 0
3967 for postData in postDataList:
3968 postData[POST_TEXT_NAME] = postText(
3969 postData["textName"], serial, data["EUI"]
3970 )
3971 del postData["textName"]
3972 if checkPostData(postData[POST_MEDIA_NAME]):
3973 postData[POST_DEVID_NAME] = postData[POST_DEVID_NAME] + i
3974 self.colibird_post(postData)
3975 print(postData)
3976 i = i + 1
3977
3978 """
3979 usage : updating unit to metric system with use of factor
3980 input : dictionary old unit based postdata
3981 output : dictionary new unit based postdata
3982 """
3983
3984 def unitUpdate(data):
3985 data[POST_VALUE_NAME] = data[POST_VALUE_NAME] * data["factor"]
3986 if data[POST_UNIT_NAME] == "Wh":
3987 if data["factor"] >= 1000:
3988 data[POST_VALUE_NAME] = data[POST_VALUE_NAME] / 10 ** 3
3989 data[POST_UNIT_NAME] = "kWh"
3990 elif data[POST_UNIT_NAME] == "J":
3991 data[POST_VALUE_NAME] = data[POST_VALUE_NAME] / 10 ** 9
3992 data[POST_UNIT_NAME] = "Gj"
3993 elif data[POST_UNIT_NAME] == "W":
3994 data[POST_VALUE_NAME] = data[POST_VALUE_NAME] / 10 ** 3
3995 data[POST_UNIT_NAME] = "kW"
3996 return data
3997
3998 """
3999 usage : removes process string characters as per they are processed
4000 input : string processString | string processType | int valueIncrement
4001 output : string processString after removel
4002 """
4003
4004 def updateProcessData(processData, processType, valueIncrement=0):
4005 length = len(processData)
4006 if (
4007 processData[0:2] == "00" or processData[0:2] == "01"
4008 ) and processType == "initial":
4009 return processData[2:length]
4010 elif processData[0:2] != "00" and processType == "initial":
4011 return processData
4012 elif processType == "vif" or processType == "dif":
4013 return processData[2:length]
4014 elif processType == "value":
4015 return processData[valueIncrement:length]
4016
4017 processData = data["data"]
4018 processData = updateProcessData(processData, "initial")
4019 loopTurn = 1
4020 postDataList = []
4021 while len(processData) != 10:
4022 # dif = unpack(">B", unhexlify(processData[0:2]))[0]
4023 processData = updateProcessData(processData, "dif")
4024 vif = unpack(">B", unhexlify(processData[0:2]))[0]
4025 processData = updateProcessData(processData, "vif")
4026 valueEnd = getValueIncrement(loopTurn)
4027 tempPostData = getPostData(
4028 processData,
4029 vif,
4030 valueEnd,
4031 postdata[POST_DEVID_NAME],
4032 postdata[POST_SUBDEVID_NAME],
4033 data["EUI"],
4034 )
4035 merge(tempPostData, postdata)
4036 processData = updateProcessData(processData, "value", valueEnd)
4037 # print(postdata)
4038 postDataList.append(postdata.copy())
4039 loopTurn = loopTurn + 1
4040
4041 sendPostData(postDataList)
4042
4043 elif data["device-type"] == "SensoneoQS":
4044 if len(data["data"]) == 66:
4045 data["code"] = int(data["data"][16:18], 16)
4046 if data["code"] == 1:
4047 postdata["media"] = "distance"
4048 postdata["unit"] = "cm"
4049 postdata["text"] = "{1} (EUI={2})".format(
4050 "", "Distanz 1 Sensoneo Q", data["EUI"]
4051 ).encode("iso-8859-1")
4052 sonar1 = int(data["data"][18:20], 16) * 2
4053 postdata["value"] = sonar1
4054 self.colibird_post(postdata)
4055
4056 postdata["DevID"] += 1
4057 postdata["text"] = "{1} (EUI={2})".format(
4058 "", "Distanz 2 Sensoneo Q", data["EUI"]
4059 ).encode("iso-8859-1")
4060 sonar2 = int(data["data"][20:22], 16) * 2
4061 postdata["value"] = sonar2
4062 self.colibird_post(postdata)
4063
4064 postdata["DevID"] += 1
4065 postdata["text"] = "{1} (EUI={2})".format(
4066 "", "Distanz 3 Sensoneo Q", data["EUI"]
4067 ).encode("iso-8859-1")
4068 sonar3 = int(data["data"][22:24], 16) * 2
4069 postdata["value"] = sonar3
4070 self.colibird_post(postdata)
4071
4072 postdata["DevID"] += 1
4073 postdata["text"] = "{1} (EUI={2})".format(
4074 "", "Distanz 4 Sensoneo Q", data["EUI"]
4075 ).encode("iso-8859-1")
4076 sonar4 = int(data["data"][24:26], 16) * 2
4077 postdata["value"] = sonar4
4078 self.colibird_post(postdata)
4079
4080 postdata["DevID"] += 1
4081 postdata["text"] = "{1} (EUI={2})".format(
4082 "", "Distanz Durchschnitt Sensoneo Q", data["EUI"]
4083 ).encode("iso-8859-1")
4084 AverageDistance = (sonar1 + sonar2 + sonar3 + sonar4) / 4
4085 postdata["value"] = AverageDistance
4086 self.colibird_post(postdata)
4087
4088 if "levelempty" in data:
4089 postdata["DevID"] += 1
4090 postdata["text"] = "{1} (EUI={2})".format(
4091 "", "Füllstand Sensoneo Q", data["EUI"]
4092 ).encode("iso-8859-1")
4093 postdata["dashboard_tile"] = "wastebin"
4094 filllevel = (
4095 100
4096 / (data["levelempty"] - data["levelfull"])
4097 * (data["levelempty"] - AverageDistance)
4098 )
4099 postdata["value"] = round(filllevel, 2)
4100 postdata["media"] = "level"
4101 postdata["unit"] = "%"
4102 self.colibird_post(postdata)
4103
4104 postdata["DevID"] += 1
4105 postdata["text"] = "{1} (EUI={2})".format(
4106 "", "Temperatur", data["EUI"]
4107 ).encode("iso-8859-1")
4108 postdata["value"] = data["temperature"] = int(
4109 data["data"][28:30], 16
4110 )
4111 postdata["media"] = "tempx"
4112 postdata["unit"] = "°C"
4113 postdata["dashboard_tile"] = "temp_od"
4114 self.colibird_post(postdata)
4115
4116 postdata["DevID"] += 1
4117 postdata["text"] = "{1} (EUI={2})".format(
4118 "", "Position", data["EUI"]
4119 ).encode("iso-8859-1")
4120 postdata["value"] = data["battery_voltage"] = (
4121 int(data["data"][26:28], 16) * 10 + 2500
4122 ) / 1000
4123 postdata["dashboard_tile"] = ""
4124 self.colibird_post(postdata)
4125
4126 postdata["DevID"] += 1
4127 postdata["lat"] = data["lat"] = unpack(
4128 "<f", unhexlify(data["data"][40:48])
4129 )[0]
4130 postdata["lng"] = data["lng"] = unpack(
4131 "<f", unhexlify(data["data"][50:58])
4132 )[0]
4133 postdata["value"] = ""
4134 postdata["media"] = "position"
4135 postdata["unit"] = ""
4136
4137 if not postdata["lng"] == 0:
4138 self.colibird_post(postdata)
4139
4140 elif data["device-type"] == "SensoneoSV3":
4141 if len(data["data"]) == 34 and data["frameport"] == 2:
4142 data["code"] = int(data["data"][16:18], 16)
4143 if data["code"] == 1:
4144 postdata["media"] = "distance"
4145 postdata["unit"] = "cm"
4146 postdata["text"] = "{1} (EUI={2})".format(
4147 "", "Distanz 1 Sensoneo Q", data["EUI"]
4148 ).encode("iso-8859-1")
4149 sonar1 = int(data["data"][18:20], 16) * 2
4150 postdata["value"] = sonar1
4151 self.colibird_post(postdata)
4152
4153 postdata["DevID"] += 1
4154 postdata["text"] = "{1} (EUI={2})".format(
4155 "", "Distanz 2 Sensoneo Q", data["EUI"]
4156 ).encode("iso-8859-1")
4157 sonar2 = int(data["data"][20:22], 16) * 2
4158 postdata["value"] = sonar2
4159 self.colibird_post(postdata)
4160
4161 postdata["DevID"] += 1
4162 postdata["text"] = "{1} (EUI={2})".format(
4163 "", "Distanz 3 Sensoneo Q", data["EUI"]
4164 ).encode("iso-8859-1")
4165 sonar3 = int(data["data"][22:24], 16) * 2
4166 postdata["value"] = sonar3
4167 self.colibird_post(postdata)
4168
4169 postdata["DevID"] += 1
4170 postdata["text"] = "{1} (EUI={2})".format(
4171 "", "Distanz 4 Sensoneo Q", data["EUI"]
4172 ).encode("iso-8859-1")
4173 sonar4 = int(data["data"][24:26], 16) * 2
4174 postdata["value"] = sonar4
4175 self.colibird_post(postdata)
4176
4177 postdata["DevID"] += 1
4178 postdata["text"] = "{1} (EUI={2})".format(
4179 "", "Distanz Durchschnitt Sensoneo Q", data["EUI"]
4180 ).encode("iso-8859-1")
4181 AverageDistance = (sonar1 + sonar2 + sonar3 + sonar4) / 4
4182 postdata["value"] = AverageDistance
4183 self.colibird_post(postdata)
4184
4185 if "levelempty" in data:
4186 postdata["DevID"] += 1
4187 postdata["text"] = "{1} (EUI={2})".format(
4188 "", "Füllstand Sensoneo Q", data["EUI"]
4189 ).encode("iso-8859-1")
4190 postdata["dashboard_tile"] = "wastebin"
4191 filllevel = (
4192 100
4193 / (data["levelempty"] - data["levelfull"])
4194 * (data["levelempty"] - AverageDistance)
4195 )
4196 postdata["value"] = round(filllevel, 2)
4197 postdata["media"] = "level"
4198 postdata["unit"] = "%"
4199 self.colibird_post(postdata)
4200
4201 postdata["DevID"] += 1
4202 postdata["text"] = "{1} (EUI={2})".format(
4203 "", "Temperatur", data["EUI"]
4204 ).encode("iso-8859-1")
4205 postdata["value"] = data["temperature"] = int(
4206 data["data"][28:30], 16
4207 )
4208 postdata["media"] = "tempx"
4209 postdata["unit"] = "°C"
4210 postdata["dashboard_tile"] = "temp_od"
4211 self.colibird_post(postdata)
4212
4213 postdata["DevID"] += 1
4214 postdata["text"] = "{1} (EUI={2})".format(
4215 "", "Position", data["EUI"]
4216 ).encode("iso-8859-1")
4217 postdata["value"] = data["battery_voltage"] = (
4218 int(data["data"][26:28], 16) * 10 + 2500
4219 ) / 1000
4220 postdata["dashboard_tile"] = ""
4221 self.colibird_post(postdata)
4222
4223 #This will come with GPS with V4
4224 #postdata["DevID"] += 1
4225 #postdata["lat"] = data["lat"] = unpack(
4226 # "<f", unhexlify(data["data"][40:48])
4227 #)[0]
4228 #postdata["lng"] = data["lng"] = unpack(
4229 # "<f", unhexlify(data["data"][50:58])
4230 #)[0]
4231 #postdata["value"] = ""
4232 #postdata["media"] = "position"
4233 #postdata["unit"] = ""
4234
4235 #if not postdata["lng"] == 0:
4236 # self.colibird_post(postdata)
4237
4238 elif data["device-type"] == "SensoneoSS":
4239 if len(data["data"]) == 34:
4240 postdata["media"] = "distance"
4241 postdata["unit"] = "cm"
4242 postdata["text"] = "{1} (EUI={2})".format(
4243 "", "Distanz Sensoneo S", data["EUI"]
4244 ).encode("iso-8859-1")
4245 sonar = data["data"][22:28]
4246 data["distance"] = int(bytearray.fromhex(sonar).decode())
4247 postdata["value"] = data["distance"]
4248
4249 if "levelempty" in data:
4250 if data["distance"] > data["levelempty"]:
4251 postdata["DevID"] += 1
4252 pass
4253 else:
4254 self.colibird_post(postdata)
4255
4256 postdata["DevID"] += 1
4257 postdata["text"] = "{1} (EUI={2})".format(
4258 "", "Füllstand Sensoneo S", data["EUI"]
4259 ).encode("iso-8859-1")
4260 postdata["dashboard_tile"] = "wastebin"
4261 filllevel = (
4262 100
4263 / (data["levelempty"] - data["levelfull"])
4264 * (data["levelempty"] - data["distance"])
4265 )
4266 postdata["value"] = round(filllevel, 2)
4267 postdata["media"] = "level"
4268 postdata["unit"] = "%"
4269 self.colibird_post(postdata)
4270 else:
4271 if data["distance"] > 250:
4272 postdata["DevID"] += 1
4273 pass
4274 else:
4275 self.colibird_post(postdata)
4276
4277 postdata["DevID"] += 1
4278 postdata["text"] = "{1} (EUI={2})".format(
4279 "", "Temperatur", data["EUI"]
4280 ).encode("iso-8859-1")
4281 temperature = data["data"][14:20]
4282 postdata["value"] = data["temperature"] = int(
4283 bytearray.fromhex(temperature).decode()
4284 )
4285 postdata["media"] = "tempx"
4286 postdata["unit"] = "°C"
4287 postdata["dashboard_tile"] = "temp_od"
4288 self.colibird_post(postdata)
4289
4290 postdata["DevID"] += 1
4291 postdata["text"] = "{1} (EUI={2})".format(
4292 "", "Batteriestand V", data["EUI"]
4293 ).encode("iso-8859-1")
4294 batteryV = data["data"][4:12]
4295 postdata["value"] = data["battery"] = float(
4296 bytearray.fromhex(batteryV).decode()
4297 )
4298 postdata["media"] = "batlevelv"
4299 postdata["unit"] = "V"
4300 postdata["dashboard_tile"] = ""
4301 self.colibird_post(postdata)
4302
4303 elif data["device-type"] == "Parametric2":
4304 if len(data["data"]) == 18:
4305 data["keycounter1"] = int(data["data"][0:2], 16)
4306 data["keycounter2"] = int(data["data"][6:8], 16)
4307 if data["keycounter1"] == 10:
4308 postdata["dashboard_tile"] = ""
4309 postdata["media"] = "motion"
4310 postdata["unit"] = ""
4311 postdata["text"] = "{1} (EUI={2})".format(
4312 "", "Zähler Li nach Re", data["EUI"]
4313 ).encode("iso-8859-1")
4314 postdata["value"] = int(data["data"][2:6], 16)
4315 self.colibird_post(postdata)
4316
4317 postdata["DevID"] += 1
4318 if data["keycounter2"] == 22:
4319 postdata["dashboard_tile"] = ""
4320 postdata["media"] = "motion"
4321 postdata["unit"] = ""
4322 postdata["text"] = "{1} (EUI={2})".format(
4323 "", "Zähler Re nach Li", data["EUI"]
4324 ).encode("iso-8859-1")
4325 postdata["value"] = int(data["data"][8:12], 16)
4326 self.colibird_post(postdata)
4327
4328 else:
4329 log.msg(
4330 "unknown meter type {0} for device {1} in config".format(
4331 data["device-type"], data["EUI"]
4332 )
4333 )
4334
4335 log.msg("all decoded: {0}".format(data))
4336 else:
4337 log.msg("Colibird parser: unknown device {1}: {0}".format(messagedata, EUI))
4338
4339 def colibird_post(self, postdata):
4340 if "value" in postdata:
4341 if type(self.config["colibird-url"]) != type([]):
4342 urls = [self.config["colibird-url"]]
4343 else:
4344 urls = self.config["colibird-url"]
4345
4346 if not postdata.get("unit", False):
4347 log.msg(
4348 "Colibird post: warning: no unit specified in {0}".format(postdata)
4349 )
4350
4351 headers = {"User-Agent": "SimpleComplexWebSocketClient/0.1"}
4352
4353 for url in urls:
4354 log.msg("Colibird post: {0} {1}".format(url, postdata))
4355 if not args.noop:
4356 req = requests.post(url, data=postdata, headers=headers)
4357 log.msg(
4358 "{0} {1}: {2}".format(req.status_code, req.reason, req.text)
4359 )
4360 else:
4361 log.msg(
4362 "Colibird post: don't actually send anything because of noop flag set"
4363 )
4364
4365 def mbus_difvif_parse(self, chunkdata):
4366 # VIF decoding according to MBUS-spec: http://www.m-bus.com/mbusdoc/md8.php 8.4.3 Codes for Value Information Field (VIF)
4367 # the bitmask for the VIF is 01111000b = 120
4368 # the VIF codes (in the mask above) are 0<<3=0 to 15<<3
4369 # the bitmask for the exponent is 00000111b = 7
4370 if chunkdata["vif"] & 120 == 0:
4371 chunkdata["unit"] = "Wh"
4372 chunkdata["factor"] = 10 ** ((chunkdata["vif"] & 7) - 3)
4373 chunkdata["media"] = "heat"
4374 chunkdata["name"] = "Energy"
4375 elif chunkdata["vif"] & 120 == 1 << 3:
4376 chunkdata["unit"] = "J"
4377 chunkdata["factor"] = 10 ** (chunkdata["vif"] & 7)
4378 elif chunkdata["vif"] & 120 == 2 << 3:
4379 chunkdata["unit"] = "m3"
4380 chunkdata["factor"] = 10 ** ((chunkdata["vif"] & 7) - 6)
4381 chunkdata["media"] = "water"
4382 chunkdata["name"] = "Volume"
4383 elif chunkdata["vif"] & 120 == 3 << 3:
4384 chunkdata["unit"] = "kg"
4385 chunkdata["factor"] = 10 ** ((chunkdata["vif"] & 7) - 3)
4386 # ignore vif=4<<3 ontime
4387 elif chunkdata["vif"] & 120 == 5 << 3:
4388 chunkdata["unit"] = "W"
4389 chunkdata["factor"] = 10 ** ((chunkdata["vif"] & 7) - 3)
4390 elif chunkdata["vif"] & 120 == 6 << 3:
4391 chunkdata["unit"] = "J/h"
4392 chunkdata["factor"] = 10 ** ((chunkdata["vif"] & 7))
4393 elif chunkdata["vif"] & 120 == 7 << 3:
4394 chunkdata["unit"] = "m3/h"
4395 chunkdata["factor"] = 10 ** ((chunkdata["vif"] & 7) - 6)
4396 elif chunkdata["vif"] & 120 == 8 << 3:
4397 chunkdata["unit"] = "m3/min"
4398 chunkdata["factor"] = 10 ** ((chunkdata["vif"] & 7) - 7)
4399 elif chunkdata["vif"] & 120 == 9 << 3:
4400 chunkdata["unit"] = "m3/s"
4401 chunkdata["factor"] = 10 ** ((chunkdata["vif"] & 7) - 9)
4402 elif chunkdata["vif"] & 120 == 10 << 3:
4403 chunkdata["unit"] = "kg/h"
4404 chunkdata["factor"] = 10 ** ((chunkdata["vif"] & 7) - 3)
4405 elif chunkdata["vif"] & 120 == 11 << 3:
4406 # absolute temperatures
4407 chunkdata["unit"] = "°c"
4408 chunkdata["factor"] = 10 ** ((chunkdata["vif"] & 3) - 3)
4409 chunkdata["media"] = "tempR"
4410 if bool(chunkdata["vif"] & 4):
4411 chunkdata["name"] = "ReturnTemperature"
4412 else:
4413 chunkdata["name"] = "FlowTemperature"
4414 elif chunkdata["vif"] & 120 == 12 << 3:
4415 # temperature difference
4416 chunkdata["unit"] = "°c"
4417 chunkdata["factor"] = 10 ** ((chunkdata["vif"] & 3) - 3)
4418 elif chunkdata["vif"] & 120 == 13 << 3:
4419 # date (& time)
4420 chunkdata["type"] = "date"
4421 # ignore vif=14<<3: averaging duration
4422 elif chunkdata["vif"] & 120 == 15 << 3:
4423 # fabrication number = serial number
4424 chunkdata["type"] = "serial"
4425 else:
4426 log.msg("unknown VIF {0} in chunk: {1}".format(chunkdata["vif"], chunkdata))
4427
4428
4429class MyClientFactory(ReconnectingClientFactory, WebSocketClientFactory):
4430 def startedConnecting(self, connector):
4431 log.msg("Started to connect.")
4432
4433 def clientConnectionLost(self, connector, reason):
4434 log.msg("Lost connection. Reason: {}".format(reason))
4435 ReconnectingClientFactory.clientConnectionLost(self, connector, reason)
4436
4437 def clientConnectionFailed(self, connector, reason):
4438 log.msg("Connection failed. Reason: {}".format(reason))
4439 ReconnectingClientFactory.clientConnectionFailed(self, connector, reason)
4440
4441
4442wselement_connections = []
4443wsThread_connections = []
4444
4445def connect_ws_element(url, factory):
4446 # websocket-client based connection due to issue in receiving data from some element urls
4447 func_message = partial(WSElementClientProtocol.on_message, factory)
4448 # websocket.enableTrace(True)
4449 ws = websocket.WebSocketApp(
4450 url,
4451 on_message=func_message,
4452 on_error=WSElementClientProtocol.on_error,
4453 on_close=WSElementClientProtocol.on_close,
4454 )
4455 ws.on_open = WSElementClientProtocol.on_open
4456 wselement_connections.append(ws)
4457 wst = threading.Thread(target=ws.run_forever, kwargs={"ping_interval": int(args.ping_interval)})
4458 wsThread_connections.append(wst)
4459 wst.daemon = True
4460 wst.start()
4461 log.msg("Thread started to URL: {0}".format(url))
4462 return wst
4463
4464class WSElementClientProtocol: # websocket based element client protocol
4465 stats = {}
4466 config = {}
4467
4468 @staticmethod
4469 def on_connect(ws, response):
4470 log.msg("WSElement Server connected: {0}".format(response.peer))
4471
4472 @staticmethod
4473 def on_open(ws):
4474 log.msg("WSElement WebSocket connection open.")
4475
4476
4477 # def run(*args):
4478 # for i in range(3):
4479 # time.sleep(1)
4480 # ws.send("%d" % i)
4481 # time.sleep(1)
4482 # # ws.close()
4483 # # print("thread terminating...")
4484 #
4485 # reactor.callInThread(run)
4486
4487 @staticmethod
4488 def on_error(ws, error):
4489 log.msg("WSElement WebSocket connection Error.")
4490 log.msg(error)
4491
4492 @staticmethod
4493 def on_close(ws):
4494 log.msg("WSElement WebSocket connection close.")
4495
4496 @staticmethod
4497 def on_message(factory, ws, payload):
4498 # log.msg("WSElement Text message received: {0}".format(payload))
4499 responses = json.loads(payload)
4500 for response in responses:
4501 if (
4502 response.get("event", False) == "packet_added"
4503 and response.get("body", False)
4504 and response["body"].get("packet_type", False) == "up"
4505 ):
4506 #log.msg("WSElement payload: {0}".format(response))
4507 url = urllib.parse.urlsplit(factory.protocolconfig["element-url"])
4508 if url[0] == "wss":
4509 method = "https"
4510 else:
4511 method = "http"
4512 dev_metadata = requests.get(
4513 "{0}://{1}/api/v1/devices/{2}?{3}".format(
4514 method, url[1], response["body"]["device_id"], url[3]
4515 ),
4516 headers={
4517 "Accept": "application/json",
4518 "Content-Type": "application/json",
4519 },
4520 ).json()
4521 #log.msg("WSElement metadata: {0}".format(dev_metadata))
4522 EUI = None
4523 for interface in dev_metadata["body"]["interfaces"]:
4524 if interface["opts"].get("device_eui", False):
4525 # print(interface["opts"].get("device_eui", False))
4526 EUI = interface["opts"]["device_eui"]
4527 # log.msg("EUI check 1")
4528 # log.msg(EUI)
4529 break
4530 if EUI is None:
4531 log.msg(
4532 "WSElement EUI not found in metadata for {0}".format(
4533 response["body"]["device_id"]
4534 )
4535 )
4536 continue
4537 # don't process this payload
4538
4539 frameport = response["body"]["meta"].get("frame_port", 1)
4540 log.msg("Frameport is: {0}".format(frameport))
4541
4542 messagedata = response["body"]["payload"]
4543 timestamp_original = dateutil.parser.parse(
4544 response["body"]["inserted_at"]
4545 )
4546 zurich_tz = tz.gettz("Europe/Zurich")
4547 timestamp = timestamp_original.astimezone(zurich_tz)
4548 parser = ColibirdParser()
4549 # log.msg("EUI check 2")
4550 # log.msg(EUI)
4551 # log.msg("protocol config : {0}".format(factory.protocolconfig))
4552 # log.msg("stats config : {0}".format(WSElementClientProtocol.stats))
4553 parser.parse(
4554 EUI,
4555 messagedata,
4556 timestamp,
4557 factory.protocolconfig,
4558 WSElementClientProtocol.stats,
4559 frameport,
4560 )
4561 else:
4562 # log.msg("WSElement ignoring message {0}".format(response))
4563 pass
4564
4565
4566def connect_element(url, config, reactor):
4567 factory = MyClientFactory(url=url, useragent="SimpleComplexWebSocketClient")
4568 # factory.protocol = ElementClientProtocol
4569 factory.protocolconfig = config
4570 factory.protocolconfig[
4571 "element-url"
4572 ] = url # this would come from config but needs to be set for colibird-deviceapi
4573 factory.setProtocolOptions(autoPingInterval=5, autoPingTimeout=2)
4574 #log.msg("starting new element with config: {0}".format(factory.protocolconfig))
4575 # reactor.callInThread(connect_ws_element, url, factory)
4576 wst = connect_ws_element(url, factory)
4577 # this spawns a new thread and uses the python-websocket client instead of reactor
4578 return wst
4579
4580# url = urllib.parse.urlsplit(url)
4581# if url.scheme == "wss":
4582# port = url.port
4583# if not port:
4584# port = 443
4585# # Hack to send double "Host:" headers, this one without the port,
4586# # because elements reverse proxy can't handle the ":443" suffix
4587# # in their hosted dev environment...
4588# factory.headers = {"Host": url.hostname}
4589# reactor.connectSSL(url.hostname, port, factory, ssl.ClientContextFactory())
4590# else:
4591# port = url.port
4592# if not port:
4593# port = 80
4594# reactor.connectTCP(url.hostname, port, factory)
4595
4596
4597def connect_tracknet(url, config, reactor):
4598 url = urllib.parse.urlsplit(url)
4599 owner = url.fragment
4600 newurl = urllib.parse.urlunsplit((url[0], url[1], url[2], url[3], ""))
4601 # log.msg(newurl)
4602 factory = WebSocketClientFactory(
4603 url=newurl, useragent="SimpleComplexWebSocketClient"
4604 )
4605 factory.protocol = TracknetInfoClientProtocol
4606 factory.protocol.owner = url.fragment
4607 factory.protocol.reactor = reactor
4608 factory.protocolconfig = config
4609 factory.setProtocolOptions(autoPingInterval=5, autoPingTimeout=2)
4610 # log.msg(url)
4611 if url.scheme == "wss":
4612 port = url.port
4613 if not port:
4614 port = 443
4615 reactor.connectSSL(url.hostname, port, factory, ssl.ClientContextFactory())
4616 else:
4617 port = url.port
4618 if not port:
4619 port = 80
4620 reactor.connectTCP(url.hostname, port, factory)
4621
4622
4623def connect_loriot(url, config, reactor):
4624 factory = MyClientFactory(url=url, useragent="SimpleComplexWebSocketClient")
4625 factory.protocol = LoriotClientProtocol
4626 factory.protocolconfig = config
4627 # factory.protocol.stats = stats
4628 # factory.protocol.statsfile = statsfile
4629 factory.setProtocolOptions(autoPingInterval=5, autoPingTimeout=2)
4630 url = urllib.parse.urlsplit(url)
4631 if url.scheme == "wss":
4632 port = url.port
4633 if not port:
4634 port = 443
4635 reactor.connectSSL(url.hostname, port, factory, ssl.ClientContextFactory())
4636 else:
4637 port = url.port
4638 if not port:
4639 port = 80
4640 reactor.connectTCP(url.hostname, port, factory)
4641
4642
4643def get_colibird_endpointapi(url):
4644 try:
4645 url = urllib.parse.urlsplit(url)
4646 # log.msg('the current url is: {0}'.format(url))
4647 authorizationheader = url.fragment
4648 newurl = urllib.parse.urlunsplit((url[0], url[1], url[2], url[3], ""))
4649 # log.msg('the new url is: {0}'.format(newurl))
4650 r = requests.get(newurl, headers={"Authorization": authorizationheader})
4651 # log.msg('the current r is: {0}'.format(r))
4652 config = r.json()
4653 # log.msg('the current config 3 is: {0}'.format(config))
4654 if config.get("ok", False):
4655 return config
4656 except:
4657 return None
4658 return None
4659
4660
4661def check_colibird_config_update(url):
4662 global colibird
4663 newconfig = get_colibird_endpointapi(url)
4664
4665 if newconfig is not None and newconfig.get("ok", False):
4666 if colibird == newconfig:
4667 log.msg("Colibird endpointapi: no config change")
4668 else:
4669 log.msg(
4670 "Colibird endpointapi: new config available, shutting down to be restarted by supervisord"
4671 )
4672 # sheduling a "last resort exit" if killing threads won't work
4673 reactor.callLater(1, sys.exit)
4674 # manually close all WSElement connections
4675 for wse in wselement_connections:
4676 log.msg("shutting down WSElement thread", wse)
4677 wse.keep_running = False
4678 # shutdown gracefully within a second or the sys.exit above terminates
4679 reactor.callFromThread(reactor.stop)
4680 else:
4681 log.msg(
4682 "Colibird endpointapi: failed getting new config from colibird, continuing as-is"
4683 )
4684 # for thread in threading.enumerate():
4685 # print(thread)
4686 # for wse in wselement_connections:
4687 # print(wse)
4688
4689
4690if __name__ == "__main__":
4691 parser = argparse.ArgumentParser(
4692 description="import Loriot LoRaWan meters to colibird.ch"
4693 )
4694 parser.add_argument(
4695 "-c", "--config", help="path to config.yaml", default="config.yaml"
4696 )
4697 parser.add_argument(
4698 "-s", "--stats", help="path to stats.config.yaml", default="stats.config.yaml"
4699 )
4700 parser.add_argument(
4701 "-v",
4702 "--verbose",
4703 help="enable more verbose debug logging",
4704 action="store_true",
4705 default=False,
4706 )
4707 parser.add_argument(
4708 "-n",
4709 "--noop",
4710 help="dry run, do not post to colibird",
4711 action="store_true",
4712 default=False,
4713 )
4714 parser.add_argument(
4715 "-d",
4716 "--debug",
4717 help="debug a certain device EUI, only query/display that device, ignore status",
4718 default=False,
4719 metavar="EUI",
4720 )
4721 parser.add_argument(
4722 "-m",
4723 "--message",
4724 help="manually parse a message, requires the device EUI in --debug, implies --noop",
4725 default=False,
4726 nargs="*",
4727 )
4728 parser.add_argument(
4729 "-p",
4730 "--ping_interval",
4731 help="ping interval to check status of the socket",
4732 default=5,
4733 )
4734 args = parser.parse_args()
4735
4736 if args.verbose:
4737 txaio.start_logging(level="debug")
4738 else:
4739 txaio.start_logging() # default is info
4740
4741 log.msg("starting with args: {0}".format(args))
4742
4743 configfile = open(args.config, "r")
4744 config = yaml.safe_load(configfile)
4745 configfile.close()
4746 log.msg("starting with config: {0}".format(config))
4747
4748 # statsfile = open(args.stats, "a+") # 'a+' creates the file if it does not exist
4749 # statsfile.seek(0)
4750 # stats = yaml.safe_load(statsfile) or {}
4751 # log.msg("stats: {0}".format(stats))
4752
4753 if args.message:
4754 # simulate message reception for parsing testing
4755 args.noop = True
4756 message = {
4757 "cmd": "rx",
4758 "EUI": args.debug,
4759 "ts": int(time.time()) * 1000,
4760 "data": ("".join(args.message)).encode("utf8"),
4761 }
4762 parser = ColibirdParser()
4763 parser.parse(message["EUI"], message["data"], message["ts"] / 1000, config, {})
4764 else:
4765 # instantiate the twisted reactor
4766 if config.get("loriot-url", False):
4767 connect_loriot(config["loriot-url"], config, reactor)
4768 if config.get("tracknet-url", False):
4769 connect_tracknet(config["tracknet-url"], config, reactor)
4770 if config.get("element-url", False):
4771 wst = connect_element(config["element-url"], config, reactor)
4772 if config.get("colibird-endpointapi", False):
4773 colibird = get_colibird_endpointapi(config["colibird-endpointapi"])
4774 log.msg("Colibird endpoint api config: {0}".format(colibird))
4775 if colibird is not None and colibird.get("ok", False):
4776 # the api says everything is OK :)
4777 for socket in colibird.get("sockets", {}):
4778 log.msg("Colibird endpointapi: starting socket: {0}".format(socket))
4779 subconfig = {}
4780 subconfig["meters"] = colibird["sockets"][socket].get("devices", {})
4781 subconfig["colibird-url"] = config["colibird-url"]
4782 if colibird["sockets"][socket].get("com_type", "") == "element":
4783 connect_element(socket, subconfig, reactor)
4784 if colibird["sockets"][socket].get("com_type", "") == "tracknet":
4785 connect_tracknet(socket, subconfig, reactor)
4786 if colibird["sockets"][socket].get("com_type", "") == "loriot":
4787 connect_loriot(socket, subconfig, reactor)
4788 loop = task.LoopingCall(
4789 check_colibird_config_update, config["colibird-endpointapi"]
4790 )
4791 # log.msg("colibird-endpointapi is: {0}".format(config["colibird-endpointapi"]))
4792 # log.msg("Jetzt wird gelooped")
4793 loop.start(10) # check for config update every 10s
4794
4795 while True:
4796 try:
4797 for i, i_ws in enumerate(wselement_connections):
4798 wst = wsThread_connections[i]
4799 while wst.isAlive() == False:
4800 wst = threading.Thread(target=i_ws.run_forever, kwargs={"ping_interval": int(args.ping_interval)})
4801 wsThread_connections[i] = wst
4802 wst.daemon = True
4803 wst.start()
4804 except:
4805 pass
4806
4807 else:
4808 # something was wrong, retry sooner
4809 log.msg(
4810 "colibird api returned not OK, waiting 10s before shutting down to be restarted by supervisord: {0}".format(
4811 colibird
4812 )
4813 )
4814 # log.msg("Jetzt wird gleich gestoppt")
4815 reactor.callLater(10, reactor.callFromThread, reactor.stop)
4816 reactor.run()