diff --git a/sample.py b/sample.py index 9e2f44b..d3a64b9 100755 --- a/sample.py +++ b/sample.py @@ -20,14 +20,13 @@ from builtins import input -import os import re import sys import logging -import errno import binascii import wyzesense + def on_event(ws, e): s = "[%s][%s]" % (e.Timestamp.strftime("%Y-%m-%d %H:%M:%S"), e.MAC) if e.Type == 'state': @@ -36,6 +35,7 @@ def on_event(ws, e): s += "RawEvent: type=%s, data=%r" % (e.Type, e.Data) print(s) + def main(args): if args['--debug']: loglevel = logging.DEBUG - (1 if args['--verbose'] else 0) @@ -56,7 +56,7 @@ def main(args): except IOError: print("No device found on path %r" % device) return 2 - + def List(unused_args): result = ws.List() print("%d sensor paired:" % len(result)) @@ -101,7 +101,7 @@ def HandleCmd(): cmd_and_args = input("Action:").strip().upper().split() if len(cmd_and_args) == 0: return True - + cmd = cmd_and_args[0] if cmd not in cmd_handlers: return True @@ -109,7 +109,7 @@ def HandleCmd(): handler = cmd_handlers[cmd] if not handler[1]: return False - + handler[1](cmd_and_args[1:]) return True @@ -121,6 +121,7 @@ def HandleCmd(): return 0 + if __name__ == '__main__': logging.basicConfig(format='%(levelname)s %(asctime)s %(message)s') diff --git a/wyzesense/__main__.py b/wyzesense/__main__.py index 2136b2a..41a4366 100755 --- a/wyzesense/__main__.py +++ b/wyzesense/__main__.py @@ -18,15 +18,14 @@ from builtins import input -import os import re import sys import logging -import errno import binascii from . import gateway as wyzesense + def on_event(ws, e): s = "[%s][%s]" % (e.Timestamp.strftime("%Y-%m-%d %H:%M:%S"), e.MAC) if e.Type == 'state': @@ -35,6 +34,7 @@ def on_event(ws, e): s += "RawEvent: type=%s, data=%r" % (e.Type, e.Data) print(s) + def main(args): if args['--debug']: loglevel = logging.DEBUG - (1 if args['--verbose'] else 0) @@ -55,7 +55,7 @@ def main(args): except IOError: print("No device found on path %r" % device) return 2 - + def List(unused_args): result = ws.List() print("%d sensor paired:" % len(result)) @@ -100,7 +100,7 @@ def HandleCmd(): cmd_and_args = input("Action:").strip().upper().split() if len(cmd_and_args) == 0: return True - + cmd = cmd_and_args[0] if cmd not in cmd_handlers: return True @@ -108,7 +108,7 @@ def HandleCmd(): handler = cmd_handlers[cmd] if not handler[1]: return False - + handler[1](cmd_and_args[1:]) return True @@ -120,6 +120,7 @@ def HandleCmd(): return 0 + if __name__ == '__main__': logging.basicConfig(format='%(levelname)s %(asctime)s %(message)s') diff --git a/wyzesense/gateway.py b/wyzesense/gateway.py index b1889dd..f298811 100755 --- a/wyzesense/gateway.py +++ b/wyzesense/gateway.py @@ -3,63 +3,66 @@ import os import time -import six import struct import threading import datetime -import argparse import binascii import logging log = logging.getLogger(__name__) + def bytes_to_hex(s): if s: return binascii.hexlify(s) else: return "" + def checksum_from_bytes(s): return sum(bytes(s)) & 0xFFFF -TYPE_SYNC = 0x43 -TYPE_ASYNC = 0x53 + +TYPE_SYNC = 0x43 +TYPE_ASYNC = 0x53 + def MAKE_CMD(type, cmd): return (type << 8) | cmd + class Packet(object): _CMD_TIMEOUT = 5 # Sync packets: # Commands initiated from host side - CMD_GET_ENR = MAKE_CMD(TYPE_SYNC, 0x02) - CMD_GET_MAC = MAKE_CMD(TYPE_SYNC, 0x04) - CMD_GET_KEY = MAKE_CMD(TYPE_SYNC, 0x06) - CMD_INQUIRY = MAKE_CMD(TYPE_SYNC, 0x27) - CMD_UPDATE_CC1310 = MAKE_CMD(TYPE_SYNC, 0x12) - CMD_SET_CH554_UPGRADE = MAKE_CMD(TYPE_SYNC, 0x0E) + CMD_GET_ENR = MAKE_CMD(TYPE_SYNC, 0x02) + CMD_GET_MAC = MAKE_CMD(TYPE_SYNC, 0x04) + CMD_GET_KEY = MAKE_CMD(TYPE_SYNC, 0x06) + CMD_INQUIRY = MAKE_CMD(TYPE_SYNC, 0x27) + CMD_UPDATE_CC1310 = MAKE_CMD(TYPE_SYNC, 0x12) + CMD_SET_CH554_UPGRADE = MAKE_CMD(TYPE_SYNC, 0x0E) # Async packets: - ASYNC_ACK = MAKE_CMD(TYPE_ASYNC, 0xFF) + ASYNC_ACK = MAKE_CMD(TYPE_ASYNC, 0xFF) # Commands initiated from dongle side - CMD_FINISH_AUTH = MAKE_CMD(TYPE_ASYNC, 0x14) - CMD_GET_DONGLE_VERSION = MAKE_CMD(TYPE_ASYNC, 0x16) - CMD_START_STOP_SCAN = MAKE_CMD(TYPE_ASYNC, 0x1C) - CMD_GET_SENSOR_R1 = MAKE_CMD(TYPE_ASYNC, 0x21) - CMD_VERIFY_SENSOR = MAKE_CMD(TYPE_ASYNC, 0x23) - CMD_DEL_SENSOR = MAKE_CMD(TYPE_ASYNC, 0x25) - CMD_GET_SENSOR_COUNT = MAKE_CMD(TYPE_ASYNC, 0x2E) - CMD_GET_SENSOR_LIST = MAKE_CMD(TYPE_ASYNC, 0x30) + CMD_FINISH_AUTH = MAKE_CMD(TYPE_ASYNC, 0x14) + CMD_GET_DONGLE_VERSION = MAKE_CMD(TYPE_ASYNC, 0x16) + CMD_START_STOP_SCAN = MAKE_CMD(TYPE_ASYNC, 0x1C) + CMD_GET_SENSOR_R1 = MAKE_CMD(TYPE_ASYNC, 0x21) + CMD_VERIFY_SENSOR = MAKE_CMD(TYPE_ASYNC, 0x23) + CMD_DEL_SENSOR = MAKE_CMD(TYPE_ASYNC, 0x25) + CMD_GET_SENSOR_COUNT = MAKE_CMD(TYPE_ASYNC, 0x2E) + CMD_GET_SENSOR_LIST = MAKE_CMD(TYPE_ASYNC, 0x30) # Notifications initiated from dongle side - NOTIFY_SENSOR_ALARM = MAKE_CMD(TYPE_ASYNC, 0x19) - NOTIFY_SENSOR_SCAN = MAKE_CMD(TYPE_ASYNC, 0x20) - NOITFY_SYNC_TIME = MAKE_CMD(TYPE_ASYNC, 0x32) - NOTIFY_EVENT_LOG = MAKE_CMD(TYPE_ASYNC, 0x35) + NOTIFY_SENSOR_ALARM = MAKE_CMD(TYPE_ASYNC, 0x19) + NOTIFY_SENSOR_SCAN = MAKE_CMD(TYPE_ASYNC, 0x20) + NOITFY_SYNC_TIME = MAKE_CMD(TYPE_ASYNC, 0x32) + NOTIFY_EVENT_LOG = MAKE_CMD(TYPE_ASYNC, 0x35) - def __init__(self, cmd, payload = bytes()): + def __init__(self, cmd, payload=bytes()): self._cmd = cmd if self._cmd == self.ASYNC_ACK: assert isinstance(payload, int) @@ -83,14 +86,14 @@ def Length(self): @property def Cmd(self): return self._cmd - + @property def Payload(self): return self._payload def Send(self, fd): pkt = bytes() - + pkt += struct.pack(">HB", 0xAA55, self._cmd >> 8) if self._cmd == self.ASYNC_ACK: pkt += struct.pack("BB", (self._payload & 0xFF), self._cmd & 0xFF) @@ -125,10 +128,12 @@ def Parse(cls, s): assert len(s) >= 7 s = s[:7] payload = MAKE_CMD(cmd_type, b2) - else: - assert len(s) >= b2 + 4 + elif len(s) >= b2 + 4: s = s[: b2 + 4] payload = s[5:-2] + else: + log.error("Invalid packet: %s", bytes_to_hex(s)) + return None cs_remote = (s[-2] << 8) | s[-1] cs_local = checksum_from_bytes(s[:-2]) @@ -142,11 +147,11 @@ def Parse(cls, s): @classmethod def GetVersion(cls): return cls(cls.CMD_GET_DONGLE_VERSION) - + @classmethod def Inquiry(cls): return cls(cls.CMD_INQUIRY) - + @classmethod def GetEnr(cls, r): assert isinstance(r, bytes) @@ -156,7 +161,7 @@ def GetEnr(cls, r): @classmethod def GetMAC(cls): return cls(cls.CMD_GET_MAC) - + @classmethod def GetKey(cls): return cls(cls.CMD_GET_KEY) @@ -187,7 +192,7 @@ def DelSensor(cls, mac): assert isinstance(mac, str) assert len(mac) == 8 return cls(cls.CMD_DEL_SENSOR, mac.encode('ascii')) - + @classmethod def GetSensorR1(cls, mac, r): assert isinstance(r, bytes) @@ -219,13 +224,14 @@ def AsyncAck(cls, cmd): assert (cmd >> 0x8) == TYPE_ASYNC return cls(cls.ASYNC_ACK, cmd) + class SensorEvent(object): def __init__(self, mac, timestamp, event_type, event_data): self.MAC = mac self.Timestamp = timestamp self.Type = event_type self.Data = event_data - + def __str__(self): s = "[%s][%s]" % (self.Timestamp.strftime("%Y-%m-%d %H:%M:%S"), self.MAC) if self.Type == 'state': @@ -234,6 +240,7 @@ def __str__(self): s += "RawEvent: type=%s, data=%s" % (self.Type, bytes_to_hex(self.Data)) return s + class Dongle(object): _CMD_TIMEOUT = 2 @@ -248,7 +255,7 @@ def _OnSensorAlarm(self, pkt): return timestamp, event_type, sensor_mac = struct.unpack_from(">QB8s", pkt.Payload) - timestamp = datetime.datetime.fromtimestamp(timestamp/1000.0) + timestamp = datetime.datetime.fromtimestamp(timestamp / 1000.0) sensor_mac = sensor_mac.decode('ascii') alarm_data = pkt.Payload[17:] if event_type == 0xA2: @@ -284,7 +291,7 @@ def _OnEventLog(self, pkt): assert len(pkt.Payload) >= 9 ts, msg_len = struct.unpack_from(">QB", pkt.Payload) # assert msg_len + 8 == len(pkt.Payload) - tm = datetime.datetime.fromtimestamp(ts/1000.0) + tm = datetime.datetime.fromtimestamp(ts / 1000.0) msg = pkt.Payload[9:] log.info("LOG: time=%s, data=%s", tm.isoformat(), bytes_to_hex(msg)) @@ -293,12 +300,12 @@ def __init__(self, device, event_handler): self.__fd = os.open(device, os.O_RDWR | os.O_NONBLOCK) self.__sensors = {} self.__exit_event = threading.Event() - self.__thread = threading.Thread(target = self._Worker) + self.__thread = threading.Thread(target=self._Worker) self.__on_event = event_handler self.__handlers = { Packet.NOITFY_SYNC_TIME: self._OnSyncTime, - Packet.NOTIFY_SENSOR_ALARM: self._OnSensorAlarm, + Packet.NOTIFY_SENSOR_ALARM: self._OnSensorAlarm, Packet.NOTIFY_EVENT_LOG: self._OnEventLog, } @@ -320,7 +327,7 @@ def _ReadRawHID(self): if length > 0x3F: length = 0x3F - #log.debug("Raw HID packet: %s", bytes_to_hex(s)) + # log.debug("Raw HID packet: %s", bytes_to_hex(s)) assert len(s) >= length + 1 return s[1: 1 + length] @@ -342,9 +349,9 @@ def _HandlePacket(self, pkt): log.debug("<=== Received: %s", str(pkt)) with self.__lock: handler = self.__handlers.get(pkt.Cmd, self._DefaultHandler) - + if (pkt.Cmd >> 8) == TYPE_ASYNC and pkt.Cmd != Packet.ASYNC_ACK: - #log.info("Sending ACK packet for cmd %04X", pkt.Cmd) + # log.info("Sending ACK packet for cmd %04X", pkt.Cmd) self._SendPacket(Packet.AsyncAck(pkt.Cmd)) handler(pkt) @@ -353,10 +360,10 @@ def _Worker(self): while True: if self.__exit_event.isSet(): break - + s += self._ReadRawHID() - #if s: - # log.info("Incoming buffer: %s", bytes_to_hex(s)) + # if s: + # log.info("Incoming buffer: %s", bytes_to_hex(s)) start = s.find(b"\x55\xAA") if start == -1: time.sleep(0.1) @@ -384,7 +391,7 @@ def _DoCommand(self, pkt, handler, timeout=_CMD_TIMEOUT): raise TimeoutError("_DoCommand") def _DoSimpleCommand(self, pkt, timeout=_CMD_TIMEOUT): - ctx = self.CmdContext(result = None) + ctx = self.CmdContext(result=None) def cmd_handler(pkt, e): ctx.result = pkt @@ -406,7 +413,7 @@ def _Inquiry(self): def _GetEnr(self, r): log.debug("Start GetEnr...") assert len(r) == 4 - assert all(isinstance(x, int) for x in r) + assert all(isinstance(x, int) for x in r) r_string = bytes(struct.pack("