From c8cdde4b268666af69a41309b79eb0612a4bfead Mon Sep 17 00:00:00 2001 From: doudz Date: Sun, 2 Dec 2018 20:56:20 +0100 Subject: [PATCH 01/40] Add data type 0x23 --- zigate/responses.py | 1 + 1 file changed, 1 insertion(+) diff --git a/zigate/responses.py b/zigate/responses.py index 8afe8b23..c4220674 100644 --- a/zigate/responses.py +++ b/zigate/responses.py @@ -17,6 +17,7 @@ 0x20: 'B', 0x21: 'H', 0x22: 'I', + 0x23: 'I', 0x28: 'b', 0x29: 'h', 0x2a: 'i', From 7d037477c2788ce274b326125810c1a3f9699a33 Mon Sep 17 00:00:00 2001 From: doudz Date: Sun, 2 Dec 2018 20:57:08 +0100 Subject: [PATCH 02/40] Prepare new release --- zigate/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zigate/version.py b/zigate/version.py index c3628eaa..0fcc5eac 100644 --- a/zigate/version.py +++ b/zigate/version.py @@ -5,4 +5,4 @@ # file that was distributed with this source code. # -__version__ = '0.23.1' +__version__ = '0.24.0dev0' From 61131262e6e467d9d6ba8a7f1354c47a41bb7175 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20RAMAGE?= Date: Mon, 3 Dec 2018 09:53:26 +0100 Subject: [PATCH 03/40] Change device repr --- zigate/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zigate/core.py b/zigate/core.py index ea4bcf17..9e476975 100644 --- a/zigate/core.py +++ b/zigate/core.py @@ -1631,7 +1631,7 @@ def __str__(self): typ = self.get_property('type') if typ: name = typ['value'] - return 'Device {} ({}) {}'.format(self.ieee, self.addr, name) + return 'Device {} {}-{}'.format(name, self.addr, self.ieee) def __repr__(self): return self.__str__() From 734f45714a405130a18151ecac9f30f600aa7e50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20RAMAGE?= Date: Mon, 3 Dec 2018 09:56:41 +0100 Subject: [PATCH 04/40] Change device repr --- zigate/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zigate/core.py b/zigate/core.py index 9e476975..a20d089b 100644 --- a/zigate/core.py +++ b/zigate/core.py @@ -1631,7 +1631,7 @@ def __str__(self): typ = self.get_property('type') if typ: name = typ['value'] - return 'Device {} {}-{}'.format(name, self.addr, self.ieee) + return 'Device {} ({}) {}'.format(name, self.addr, self.ieee) def __repr__(self): return self.__str__() From 09d20fd82ccf0b76b91dea45690b13788f44f88c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20RAMAGE?= Date: Mon, 3 Dec 2018 10:01:23 +0100 Subject: [PATCH 05/40] Change Device repr --- zigate/core.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/zigate/core.py b/zigate/core.py index a20d089b..ae04af65 100644 --- a/zigate/core.py +++ b/zigate/core.py @@ -1627,11 +1627,9 @@ def to_json(self, properties=False): return r def __str__(self): - name = '' - typ = self.get_property('type') - if typ: - name = typ['value'] - return 'Device {} ({}) {}'.format(name, self.addr, self.ieee) + name = self.get_property_value('type', '') + manufacturer = self.get_property_value('manufacturer', 'Device') + return '{} {} ({}) {}'.format(manufacturer, name, self.addr, self.ieee) def __repr__(self): return self.__str__() From e7da1d4ff33930d7d0eba8b4e8dbaa0abcb8f887 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20RAMAGE?= Date: Mon, 3 Dec 2018 14:04:44 +0100 Subject: [PATCH 06/40] Experimental auto rejoin --- zigate/core.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/zigate/core.py b/zigate/core.py index ae04af65..e5fec55b 100644 --- a/zigate/core.py +++ b/zigate/core.py @@ -445,9 +445,12 @@ def interpret_response(self, response): for endpoint in response['endpoints']: self.simple_descriptor_request(addr, endpoint['endpoint']) elif response.msg == 0x8048: # leave - device = self.get_device_from_ieee(response['ieee']) - if device: - self._remove_device(device.addr) + if response['rejoin_status'] == 1: + self.permit_join() + else: + device = self.get_device_from_ieee(response['ieee']) + if device: + self._remove_device(device.addr) elif response.msg in (0x8100, 0x8102, 0x8110, 0x8401): # attribute report or IAS Zone status change if response['status'] != 0: LOGGER.debug('Receive Bad status') From 856c5b47bf67752795cba30addfa7259586e6823 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20RAMAGE?= Date: Mon, 3 Dec 2018 16:40:03 +0100 Subject: [PATCH 07/40] Don't remove device trying to rejoin --- zigate/core.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zigate/core.py b/zigate/core.py index e5fec55b..fbca2ccc 100644 --- a/zigate/core.py +++ b/zigate/core.py @@ -445,10 +445,11 @@ def interpret_response(self, response): for endpoint in response['endpoints']: self.simple_descriptor_request(addr, endpoint['endpoint']) elif response.msg == 0x8048: # leave + device = self.get_device_from_ieee(response['ieee']) if response['rejoin_status'] == 1: + device.missing = True self.permit_join() else: - device = self.get_device_from_ieee(response['ieee']) if device: self._remove_device(device.addr) elif response.msg in (0x8100, 0x8102, 0x8110, 0x8401): # attribute report or IAS Zone status change From 26b66cf2b79b1c1f723f290fd254305423a44be4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20RAMAGE?= Date: Mon, 3 Dec 2018 16:43:00 +0100 Subject: [PATCH 08/40] Ignoring Device from list if no IEEE --- zigate/core.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zigate/core.py b/zigate/core.py index fbca2ccc..5c7ec6b0 100644 --- a/zigate/core.py +++ b/zigate/core.py @@ -410,6 +410,8 @@ def interpret_response(self, response): self._tag_missing(addr) # self._remove_device(addr) for d in response['devices']: + if d['ieee'] == '0000000000000000': + continue device = Device(dict(d), self) self._set_device(device) elif response.msg == 0x8042: # node descriptor From 8e4468840d38576fa7a811497049d04956f6cc80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20RAMAGE?= Date: Mon, 10 Dec 2018 16:10:34 +0100 Subject: [PATCH 09/40] initiate new discovery process --- zigate/core.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/zigate/core.py b/zigate/core.py index 5c7ec6b0..bc90f18f 100644 --- a/zigate/core.py +++ b/zigate/core.py @@ -929,6 +929,20 @@ def refresh_device(self, addr): # self.power_descriptor_request(addr) self.active_endpoint_request(addr) + def discover_device(self, addr): + ''' + starts discovery process + ''' + # discovery steps + # step 1 active endpoint request + # step 2 simple description request + # step 3 get type (cluster 0x0000, attribute 0x0005) + # step 4 if unknow type => step 5 attribute discovery else step 6 + # step 5 attribute discovery request then step 7 + # step 6 load config template + # step 7 create actions, bind and report if needed + self.active_endpoint_request(addr) + def _generate_addr(self): addr = None while not addr or addr in self._devices: From cd0505f9ce7f8d14083206929892da7956c78a44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20RAMAGE?= Date: Mon, 10 Dec 2018 16:24:11 +0100 Subject: [PATCH 10/40] Add function to retrieve type --- zigate/core.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/zigate/core.py b/zigate/core.py index bc90f18f..f2e1a186 100644 --- a/zigate/core.py +++ b/zigate/core.py @@ -1700,6 +1700,29 @@ def battery_percent(self): def rssi_percent(self): return round(100 * self.rssi / 255) + @property + def type(self): + typ = self.get_value('type') + if typ is None: + for endpoint in self.endpoints: + if 0 in self.endpoints[endpoint]['in_clusters']: + self._zigate.read_attribute_request(self.addr, + endpoint, + 0x0000, + 0x0005 + ) + break + # wait for type + t1 = time() + while self.get_value('type') is None: + time.sleep(0.1) + t2 = time() + if t2 - t1 > 3: + LOGGER.warning('No response waiting for type') + return + typ = self.get_value('type') + return typ + def refresh_device(self): self._zigate.refresh_device(self.addr) From e2bd82ae4158307eaf3bc34f49a3ebe6e5cb4552 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20RAMAGE?= Date: Mon, 10 Dec 2018 16:26:34 +0100 Subject: [PATCH 11/40] Add found type in log --- zigate/core.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zigate/core.py b/zigate/core.py index f2e1a186..d10c75b2 100644 --- a/zigate/core.py +++ b/zigate/core.py @@ -428,6 +428,8 @@ def interpret_response(self, response): ep.update(response.cleaned_data()) ep['in_clusters'] = response['in_clusters'] ep['out_clusters'] = response['out_clusters'] + typ = d.type + LOGGER.debug('Found type {}'.format(typ)) d._create_actions() d._bind_report(endpoint) # ask for various general information From 00bcd9671fb5d28df28566ac4c8807193cf6fdfe Mon Sep 17 00:00:00 2001 From: doudz Date: Tue, 11 Dec 2018 08:14:48 +0100 Subject: [PATCH 12/40] Fix add group response --- zigate/responses.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zigate/responses.py b/zigate/responses.py index c4220674..d823d020 100644 --- a/zigate/responses.py +++ b/zigate/responses.py @@ -567,6 +567,8 @@ class R8060(Response): s = OrderedDict([('sequence', 'B'), ('endpoint', 'B'), ('cluster', 'H'), + ('status', 'B'), + ('group', 'H'), ]) From a55c7307bef34388fbff7b01657c3bf6c2caccb4 Mon Sep 17 00:00:00 2001 From: doudz Date: Tue, 11 Dec 2018 12:24:39 +0100 Subject: [PATCH 13/40] Move DATA_TYPE in const --- zigate/const.py | 16 ++++++++++++++++ zigate/responses.py | 17 +---------------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/zigate/const.py b/zigate/const.py index 7dcc0231..5d5f3192 100644 --- a/zigate/const.py +++ b/zigate/const.py @@ -42,3 +42,19 @@ ACTIONS_TEMPERATURE = 'temperature' ACTIONS_HUE = 'hue' ACTIONS_LOCK = 'lock' + +DATA_TYPE = {0x00: None, + 0x10: '?', # bool + 0x18: 'b', + 0x20: 'B', + 0x21: 'H', + 0x22: 'I', + 0x23: 'I', + 0x28: 'b', + 0x29: 'h', + 0x2a: 'i', + 0x30: 'b', + 0x39: 'f', + 0x41: 's', + 0x42: 's', + } diff --git a/zigate/responses.py b/zigate/responses.py index d823d020..978736e6 100644 --- a/zigate/responses.py +++ b/zigate/responses.py @@ -8,25 +8,10 @@ import struct from collections import OrderedDict from binascii import hexlify +from const import DATA_TYPE RESPONSES = {} -DATA_TYPE = {0x00: None, - 0x10: '?', # bool - 0x18: 'b', - 0x20: 'B', - 0x21: 'H', - 0x22: 'I', - 0x23: 'I', - 0x28: 'b', - 0x29: 'h', - 0x2a: 'i', - 0x30: 'b', - 0x39: 'f', - 0x41: 's', - 0x42: 's', - } - def register_response(o): RESPONSES[o.msg] = o From 2757081dfeadae72da6637d1b54b05d6ae8555cb Mon Sep 17 00:00:00 2001 From: doudz Date: Tue, 11 Dec 2018 12:26:03 +0100 Subject: [PATCH 14/40] Move DATA_TYPE to const --- zigate/responses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zigate/responses.py b/zigate/responses.py index 978736e6..3747bc21 100644 --- a/zigate/responses.py +++ b/zigate/responses.py @@ -8,7 +8,7 @@ import struct from collections import OrderedDict from binascii import hexlify -from const import DATA_TYPE +from .const import DATA_TYPE RESPONSES = {} From 5100f156bed06f6ad7cde147c518f0f0068d88cf Mon Sep 17 00:00:00 2001 From: doudz Date: Tue, 11 Dec 2018 12:28:27 +0100 Subject: [PATCH 15/40] Fix write_attribute_request --- zigate/core.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/zigate/core.py b/zigate/core.py index d10c75b2..36347555 100644 --- a/zigate/core.py +++ b/zigate/core.py @@ -23,7 +23,7 @@ ZIGATE_DEVICE_ADDED, ZIGATE_DEVICE_REMOVED, ZIGATE_DEVICE_UPDATED, ZIGATE_DEVICE_RENAMED, ZIGATE_PACKET_RECEIVED, ZIGATE_DEVICE_NEED_REFRESH, - ZIGATE_RESPONSE_RECEIVED) + ZIGATE_RESPONSE_RECEIVED, DATA_TYPE) from .clusters import (CLUSTERS, Cluster, get_cluster) import functools @@ -1202,20 +1202,27 @@ def read_attribute_request(self, addr, endpoint, cluster, attribute, manufacturer_id, length, *attribute) self.send_data(0x0100, data) - def write_attribute_request(self, addr, endpoint, cluster, attribute, + def write_attribute_request(self, addr, endpoint, cluster, attributes, direction=0, manufacturer_specific=0, manufacturer_id=0): ''' Write Attribute request - attribute can be a unique int or a list of int + attribute could be a tuple of (attribute_id, attribute_type, data) + or a list of tuple (attribute_id, attribute_type, data) ''' addr = self.__addr(addr) - if not isinstance(attribute, list): - attribute = [attribute] - length = len(attribute) - data = struct.pack('!BHBBHBBHB{}H'.format(length), 2, addr, 1, + fmt = '' + if not isinstance(attributes, list): + attributes = [attributes] + attributes_data = [] + for attribute_tuple in attributes: + data_type = DATA_TYPE[attribute_tuple[1]] + fmt += 'HB' + data_type + attributes_data += attribute_tuple + length = len(attributes) + data = struct.pack('!BHBBHBBHB{}'.format(fmt), 2, addr, 1, endpoint, cluster, direction, manufacturer_specific, - manufacturer_id, length, *attribute) + manufacturer_id, length, *attributes_data) self.send_data(0x0110, data) def reporting_request(self, addr, endpoint, cluster, attribute, attribute_type, From 5d279c3150cd030185d62d37fd21a54dd6f8e2da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20RAMAGE?= Date: Wed, 12 Dec 2018 16:10:35 +0100 Subject: [PATCH 16/40] Ignore wrong value --- zigate/clusters.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/zigate/clusters.py b/zigate/clusters.py index aa2308f4..3f9cf706 100644 --- a/zigate/clusters.py +++ b/zigate/clusters.py @@ -315,8 +315,6 @@ class C0101(Cluster): 'expire': 2, 'expire_value': ''}, 0x0503: {'name': 'rotation', 'value': 'value', 'expire': 2, 'expire_value': ''}, - 0x0505: {'name': 'unknown', 'value': 'value', - 'expire': 2, 'expire_value': ''} } @@ -370,6 +368,13 @@ class C0402(Cluster): 'unit': '°C'}, } + def update(self, data): + added, attribute = Cluster.update(self, data) + # ignore erroneous value + if abs(attribute['value']) > 80: + return + return added, attribute + @register_cluster class C0403(Cluster): @@ -381,6 +386,13 @@ class C0403(Cluster): 'unit': 'mb'}, } + def update(self, data): + added, attribute = Cluster.update(self, data) + # ignore erroneous value + if abs(attribute['value']) > 100: + return + return added, attribute + @register_cluster class C0405(Cluster): From ae52c94477afbf08af190e0c8891c16529c73187 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20RAMAGE?= Date: Wed, 12 Dec 2018 16:11:56 +0100 Subject: [PATCH 17/40] Ignore wrong values --- zigate/clusters.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/zigate/clusters.py b/zigate/clusters.py index 3f9cf706..c4618e26 100644 --- a/zigate/clusters.py +++ b/zigate/clusters.py @@ -386,13 +386,6 @@ class C0403(Cluster): 'unit': 'mb'}, } - def update(self, data): - added, attribute = Cluster.update(self, data) - # ignore erroneous value - if abs(attribute['value']) > 100: - return - return added, attribute - @register_cluster class C0405(Cluster): @@ -402,6 +395,13 @@ class C0405(Cluster): 'unit': '%'}, } + def update(self, data): + added, attribute = Cluster.update(self, data) + # ignore erroneous value + if abs(attribute['value']) > 100: + return + return added, attribute + @register_cluster class C0406(Cluster): From a49e4411dead3fccb66caca87d05c1b13058078c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20RAMAGE?= Date: Fri, 14 Dec 2018 12:19:49 +0100 Subject: [PATCH 18/40] Handle packet decoding exception --- zigate/core.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/zigate/core.py b/zigate/core.py index 36347555..30389840 100644 --- a/zigate/core.py +++ b/zigate/core.py @@ -372,9 +372,13 @@ def decode_data(self, packet): ''' Decode raw packet message ''' - decoded = self.zigate_decode(packet[1:-1]) - msg_type, length, checksum, value, rssi = \ - struct.unpack('!HHB%dsB' % (len(decoded) - 6), decoded) + try: + decoded = self.zigate_decode(packet[1:-1]) + msg_type, length, checksum, value, rssi = \ + struct.unpack('!HHB%dsB' % (len(decoded) - 6), decoded) + except Exception: + LOGGER.error('Failed to decode packet : {}'.format(hexlify(packet))) + return if length != len(value) + 1: # add rssi length LOGGER.error('Bad length {} != {} : {}'.format(length, len(value) + 1, From ffffb4871d5c8be12b5edfd57e3ff9f0d586b9f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20RAMAGE?= Date: Fri, 14 Dec 2018 12:22:53 +0100 Subject: [PATCH 19/40] remove unneedded permit join --- zigate/core.py | 1 - 1 file changed, 1 deletion(-) diff --git a/zigate/core.py b/zigate/core.py index 30389840..43fcdb17 100644 --- a/zigate/core.py +++ b/zigate/core.py @@ -456,7 +456,6 @@ def interpret_response(self, response): device = self.get_device_from_ieee(response['ieee']) if response['rejoin_status'] == 1: device.missing = True - self.permit_join() else: if device: self._remove_device(device.addr) From c43b81066bd99fd339105e3fa00d42af7cf45073 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20RAMAGE?= Date: Fri, 14 Dec 2018 16:16:11 +0100 Subject: [PATCH 20/40] Split transport object to allow unit test --- zigate/transport.py | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/zigate/transport.py b/zigate/transport.py index 834d71c8..f6c0b654 100644 --- a/zigate/transport.py +++ b/zigate/transport.py @@ -28,13 +28,32 @@ class ZIGATE_CANNOT_CONNECT(Exception): pass -class ThreadSerialConnection(object): - def __init__(self, device, port=None): +class BaseTransport(object): + def __init__(self): self._buffer = b'' - self._port = port - self.device = device self.queue = queue.Queue() self.received = queue.Queue() + + def read_data(self, data): + ''' + Read ZiGate output and split messages + ''' + self._buffer += data +# print(self._buffer) + endpos = self._buffer.find(b'\x03') + while endpos != -1: + startpos = self._buffer.find(b'\x01') + raw_message = self._buffer[startpos:endpos + 1] + self.received.put(raw_message) + self._buffer = self._buffer[endpos + 1:] + endpos = self._buffer.find(b'\x03') + + +class ThreadSerialConnection(BaseTransport): + def __init__(self, device, port=None): + BaseTransport.__init__(self) + self._port = port + self.device = device self._running = True self.reconnect() self.thread = threading.Thread(target=self.listen, @@ -64,20 +83,6 @@ def reconnect(self): delay *= 2 return self.serial - def read_data(self, data): - ''' - Read ZiGate output and split messages - ''' - self._buffer += data -# print(self._buffer) - endpos = self._buffer.find(b'\x03') - while endpos != -1: - startpos = self._buffer.find(b'\x01') - raw_message = self._buffer[startpos:endpos + 1] - self.received.put(raw_message) - self._buffer = self._buffer[endpos + 1:] - endpos = self._buffer.find(b'\x03') - def listen(self): while self._running: try: From af207e5d72b2e5914647a015cee4df6bb7305635 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20RAMAGE?= Date: Fri, 14 Dec 2018 16:21:42 +0100 Subject: [PATCH 21/40] Begin some transport test --- tests/test_transport.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 tests/test_transport.py diff --git a/tests/test_transport.py b/tests/test_transport.py new file mode 100644 index 00000000..0f8607a9 --- /dev/null +++ b/tests/test_transport.py @@ -0,0 +1,33 @@ +''' +ZiGate Transport Tests +------------------------- +''' + +import unittest +from zigate import transport +from collections import OrderedDict + + +class TestTransport(unittest.TestCase): + def test_packet(self): + connection = transport.BaseTransport() + data = b'\x01123\x03' + connection.read_data(data) + self.assertEqual(data, connection.received.get()) + print(connection._buffer) + data = b'123\x03' + connection.read_data(data) +# self.assertEqual(data, connection.received.get()) + print(connection._buffer) + data = b'\x01123' + connection.read_data(data) +# self.assertEqual(data, connection.received.get()) + print(connection._buffer) + data = b'123\x01123\x03' + connection.read_data(data) +# self.assertEqual(data, connection.received.get()) + print(connection._buffer) + + +if __name__ == '__main__': + unittest.main() From adffcb5d5522aa3c57dd287a1ff0f35bfc52b6a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20RAMAGE?= Date: Fri, 14 Dec 2018 16:24:09 +0100 Subject: [PATCH 22/40] Begin transport test --- tests/test_transport.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/test_transport.py b/tests/test_transport.py index 0f8607a9..0731e6a2 100644 --- a/tests/test_transport.py +++ b/tests/test_transport.py @@ -5,7 +5,6 @@ import unittest from zigate import transport -from collections import OrderedDict class TestTransport(unittest.TestCase): @@ -27,6 +26,14 @@ def test_packet(self): connection.read_data(data) # self.assertEqual(data, connection.received.get()) print(connection._buffer) + data = b'123\x03123456' + connection.read_data(data) +# self.assertEqual(data, connection.received.get()) + print(connection._buffer) + data = b'456' + connection.read_data(data) +# self.assertEqual(data, connection.received.get()) + print(connection._buffer) if __name__ == '__main__': From 1997b8ee1794318d0e71964dc078435a56deb7f1 Mon Sep 17 00:00:00 2001 From: doudz Date: Fri, 14 Dec 2018 20:27:59 +0100 Subject: [PATCH 23/40] Add various transport test --- tests/test_transport.py | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/tests/test_transport.py b/tests/test_transport.py index 0731e6a2..211d044e 100644 --- a/tests/test_transport.py +++ b/tests/test_transport.py @@ -12,28 +12,41 @@ def test_packet(self): connection = transport.BaseTransport() data = b'\x01123\x03' connection.read_data(data) - self.assertEqual(data, connection.received.get()) - print(connection._buffer) + self.assertEqual(1, connection.received.qsize()) + self.assertEqual(b'\x01123\x03', connection.received.get()) + + data = b'\x01123' + connection.read_data(data) + self.assertEqual(0, connection.received.qsize()) + data = b'123\x03' connection.read_data(data) -# self.assertEqual(data, connection.received.get()) - print(connection._buffer) - data = b'\x01123' + self.assertEqual(1, connection.received.qsize()) + self.assertEqual(b'\x01123123\x03', connection.received.get()) + + data = b'123\x03' + connection.read_data(data) + self.assertEqual(0, connection.received.qsize()) + + data = b'123\x03\x01123\x03' connection.read_data(data) -# self.assertEqual(data, connection.received.get()) - print(connection._buffer) + self.assertEqual(1, connection.received.qsize()) + self.assertEqual(b'\x01123\x03', connection.received.get()) + data = b'123\x01123\x03' connection.read_data(data) -# self.assertEqual(data, connection.received.get()) - print(connection._buffer) - data = b'123\x03123456' + self.assertEqual(1, connection.received.qsize()) + self.assertEqual(b'\x01123\x03', connection.received.get()) + + data = b'\x01123\x03123\x03\x01123\x03\x011' connection.read_data(data) -# self.assertEqual(data, connection.received.get()) - print(connection._buffer) + self.assertEqual(2, connection.received.qsize()) + self.assertEqual(b'\x01123\x03', connection.received.get()) + self.assertEqual(b'\x01123\x03', connection.received.get()) + data = b'456' connection.read_data(data) -# self.assertEqual(data, connection.received.get()) - print(connection._buffer) + self.assertEqual(0, connection.received.qsize()) if __name__ == '__main__': From 3aa0fa61c02357672fcbab8773f8ae284ff76ffe Mon Sep 17 00:00:00 2001 From: doudz Date: Fri, 14 Dec 2018 20:34:48 +0100 Subject: [PATCH 24/40] Handle bad packet --- tests/test_transport.py | 5 +++++ zigate/transport.py | 5 +++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/test_transport.py b/tests/test_transport.py index 211d044e..3c47e6eb 100644 --- a/tests/test_transport.py +++ b/tests/test_transport.py @@ -48,6 +48,11 @@ def test_packet(self): connection.read_data(data) self.assertEqual(0, connection.received.qsize()) + data = b'123\x03' + connection.read_data(data) + self.assertEqual(1, connection.received.qsize()) + self.assertEqual(b'\x011456123\x03', connection.received.get()) + if __name__ == '__main__': unittest.main() diff --git a/zigate/transport.py b/zigate/transport.py index f6c0b654..b011bc3b 100644 --- a/zigate/transport.py +++ b/zigate/transport.py @@ -43,8 +43,9 @@ def read_data(self, data): endpos = self._buffer.find(b'\x03') while endpos != -1: startpos = self._buffer.find(b'\x01') - raw_message = self._buffer[startpos:endpos + 1] - self.received.put(raw_message) + if startpos != -1 and startpos < endpos: + raw_message = self._buffer[startpos:endpos + 1] + self.received.put(raw_message) self._buffer = self._buffer[endpos + 1:] endpos = self._buffer.find(b'\x03') From 2812c97c0944b8e7bdb018c964ca93ce18db352b Mon Sep 17 00:00:00 2001 From: doudz Date: Fri, 14 Dec 2018 20:36:50 +0100 Subject: [PATCH 25/40] Add error log on malformed packet --- zigate/transport.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zigate/transport.py b/zigate/transport.py index b011bc3b..af929131 100644 --- a/zigate/transport.py +++ b/zigate/transport.py @@ -46,6 +46,8 @@ def read_data(self, data): if startpos != -1 and startpos < endpos: raw_message = self._buffer[startpos:endpos + 1] self.received.put(raw_message) + else: + LOGGER.error('Malformed packet received, ignore it') self._buffer = self._buffer[endpos + 1:] endpos = self._buffer.find(b'\x03') From 0e580a2fdc38e969cdc7cb6c960ac2793c44b03e Mon Sep 17 00:00:00 2001 From: ISO-B <3048685+ISO-B@users.noreply.github.com> Date: Mon, 17 Dec 2018 13:23:19 +0200 Subject: [PATCH 26/40] Added support for OTA. - New commands: 0x0500, 0x0502, 0x0505 - New responses: 0x8501, 0x8503 - New public methods: ota_load_image, ota_image_notify, ota_get_status --- zigate/core.py | 236 ++++++++++++++++++++++++++++++++++++++++++-- zigate/responses.py | 38 +++++++ 2 files changed, 268 insertions(+), 6 deletions(-) diff --git a/zigate/core.py b/zigate/core.py index 910b9f5a..4e1d8183 100644 --- a/zigate/core.py +++ b/zigate/core.py @@ -143,6 +143,17 @@ def __init__(self, port='auto', path='~/.zigate.json', dispatcher.connect(self.interpret_response, ZIGATE_RESPONSE_RECEIVED) + self._ota = { + 'image': { + 'header': None, + 'data': None, + }, + 'active': False, + 'starttime': False, + 'transfered': 0, + 'addr': None + } + if auto_start: self.autoStart() if auto_save: @@ -328,7 +339,7 @@ def send_to_transport(self, data): raise Exception('Not connected to zigate') self.connection.send(data) - def send_data(self, cmd, data="", wait_response=None): + def send_data(self, cmd, data="", wait_response=None, wait_status=True): ''' send data through ZiGate ''' @@ -362,11 +373,13 @@ def send_data(self, cmd, data="", wait_response=None): LOGGER.debug('Encoded Msg to send {}'.format(hexlify(encoded_output))) self.send_to_transport(encoded_output) - status = self._wait_status(cmd) - if wait_response and status is not None: - r = self._wait_response(wait_response) - return r - return status + if wait_status: + status = self._wait_status(cmd) + if wait_response and status is not None: + r = self._wait_response(wait_response) + return r + return status + return False def decode_data(self, packet): ''' @@ -475,6 +488,12 @@ def interpret_response(self, response): LOGGER.debug('Device Announce') device = Device(response.data, self) self._set_device(device) + elif response.msg == 0x8501: # OTA image block request + LOGGER.debug('Client is requesting ota image data') + self._ota_send_image_data(response) + elif response.msg == 0x8503: # OTA Upgrade end request + LOGGER.debug('Client ended ota process') + self._ota_handle_upgrade_end_request(response) # else: # LOGGER.debug('Do nothing special for response {}'.format(response)) @@ -1221,6 +1240,211 @@ def reporting_request(self, addr, endpoint, cluster, attribute, attribute_type, max_interval, timeout, change) self.send_data(0x0120, data, 0x8120) + def ota_load_image(self, path_to_file): + # Check that ota process is not active + if self._ota['active'] is True: + LOGGER.error('Cannot load image while OTA process is active.') + self.get_ota_status() + return + + # Try reading file from user provided path + try: + with open(path_to_file, 'rb') as f: + ota_file_content = f.read() + except OSError as err: + LOGGER.error('{path}: {error}'.format(path=path_to_file, error=err)) + return False + + # Ensure that file has 69 bytes so it can contain header + if len(ota_file_content) < 69: + LOGGER.error('OTA file is too short') + return False + + # Read header data + try: + header_data = list(struct.unpack(' Date: Mon, 17 Dec 2018 13:25:06 +0100 Subject: [PATCH 27/40] Handle missing message beginning --- tests/test_transport.py | 21 +++++++++++++++++++-- zigate/transport.py | 2 +- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/tests/test_transport.py b/tests/test_transport.py index 3c47e6eb..9f6c6dad 100644 --- a/tests/test_transport.py +++ b/tests/test_transport.py @@ -38,11 +38,11 @@ def test_packet(self): self.assertEqual(1, connection.received.qsize()) self.assertEqual(b'\x01123\x03', connection.received.get()) - data = b'\x01123\x03123\x03\x01123\x03\x011' + data = b'\x01123\x03123\x03\x01456\x03\x011' connection.read_data(data) self.assertEqual(2, connection.received.qsize()) self.assertEqual(b'\x01123\x03', connection.received.get()) - self.assertEqual(b'\x01123\x03', connection.received.get()) + self.assertEqual(b'\x01456\x03', connection.received.get()) data = b'456' connection.read_data(data) @@ -53,6 +53,23 @@ def test_packet(self): self.assertEqual(1, connection.received.qsize()) self.assertEqual(b'\x011456123\x03', connection.received.get()) + data = b'\x01123\x01123\x01123\x03' + connection.read_data(data) + self.assertEqual(1, connection.received.qsize()) + self.assertEqual(b'\x01123\x03', connection.received.get()) + + data = b'\x01123\x01123\x01123\x03\x01456\x03' + connection.read_data(data) + self.assertEqual(2, connection.received.qsize()) + self.assertEqual(b'\x01123\x03', connection.received.get()) + self.assertEqual(b'\x01456\x03', connection.received.get()) + + data = b'\x01123\x01123\x01\x01123\x03\x03\x01456\x03' + connection.read_data(data) + self.assertEqual(2, connection.received.qsize()) + self.assertEqual(b'\x01123\x03', connection.received.get()) + self.assertEqual(b'\x01456\x03', connection.received.get()) + if __name__ == '__main__': unittest.main() diff --git a/zigate/transport.py b/zigate/transport.py index af929131..1926b1e9 100644 --- a/zigate/transport.py +++ b/zigate/transport.py @@ -42,7 +42,7 @@ def read_data(self, data): # print(self._buffer) endpos = self._buffer.find(b'\x03') while endpos != -1: - startpos = self._buffer.find(b'\x01') + startpos = self._buffer.rfind(b'\x01', 0, endpos) if startpos != -1 and startpos < endpos: raw_message = self._buffer[startpos:endpos + 1] self.received.put(raw_message) From aa88a6a23e94fd5761b2dcc67b5e428d0d495ba4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20RAMAGE?= Date: Mon, 17 Dec 2018 13:30:37 +0100 Subject: [PATCH 28/40] Fix bind crash when no IEEE --- zigate/core.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/zigate/core.py b/zigate/core.py index 43fcdb17..e97ccbd7 100644 --- a/zigate/core.py +++ b/zigate/core.py @@ -834,8 +834,10 @@ def bind_addr(self, addr, endpoint, cluster, dst_addr=None, convenient function to use addr instead of ieee ''' if addr in self._devices: - ieee = self._devices[addr]['ieee'] - return self.bind(ieee, endpoint, cluster, dst_addr, dst_endpoint) + ieee = self._devices[addr].ieee + if ieee: + return self.bind(ieee, endpoint, cluster, dst_addr, dst_endpoint) + LOGGER.error('Failed to bind, addr {}, IEEE is missing'.format(addr)) LOGGER.error('Failed to bind, addr {} unknown'.format(addr)) def unbind(self, ieee, endpoint, cluster, dst_addr=None, dst_endpoint=1): @@ -1596,19 +1598,19 @@ def _bind_report(self, enpoint_id=None): if endpoint['device'] in ACTUATORS: # light if 0x0006 in endpoint['in_clusters']: LOGGER.debug('bind and report for cluster 0x0006') - self._zigate.bind(self.ieee, endpoint_id, 0x0006) + self._zigate.bind_addr(self.addr, endpoint_id, 0x0006) self._zigate.reporting_request(self.addr, endpoint_id, 0x0006, 0x0000, 0x10) # TODO: auto select data type if 0x0008 in endpoint['in_clusters']: LOGGER.debug('bind and report for cluster 0x0008') - self._zigate.bind(self.ieee, endpoint_id, 0x0008) + self._zigate.bind_addr(self.addr, endpoint_id, 0x0008) self._zigate.reporting_request(self.addr, endpoint_id, 0x0008, 0x0000, 0x20) # TODO : auto select data type # TODO : check if the following is needed if 0x0300 in endpoint['in_clusters']: LOGGER.debug('bind and report for cluster 0x0300') - self._zigate.bind(self.ieee, endpoint_id, 0x0300) + self._zigate.bind_addr(self.addr, endpoint_id, 0x0300) for i in range(9): # all color informations self._zigate.reporting_request(self.addr, endpoint_id, 0x0300, i, 0x20) From e7c70a340f2973eb740c19489c6e9c1f468324a6 Mon Sep 17 00:00:00 2001 From: ISO-B <3048685+ISO-B@users.noreply.github.com> Date: Mon, 17 Dec 2018 14:54:26 +0200 Subject: [PATCH 29/40] Fixed travis errors --- zigate/core.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/zigate/core.py b/zigate/core.py index 50f5bcd0..e23208f4 100644 --- a/zigate/core.py +++ b/zigate/core.py @@ -1341,7 +1341,7 @@ def _ota_send_image_data(self, request): # Get requested bytes from ota file self._ota['transfered'] = request['file_offset'] - end_position = request['file_offset']+request['max_data_size'] + end_position = request['file_offset'] + request['max_data_size'] ota_data_to_send = self._ota['image']['data'][request['file_offset']:end_position] data_size = len(ota_data_to_send) ota_data_to_send = struct.unpack('<{}B'.format(data_size), ota_data_to_send) @@ -1354,7 +1354,7 @@ def _ota_send_image_data(self, request): request['file_offset'], self._ota['image']['header']['image_version'], self._ota['image']['header']['image_type'], self._ota['image']['header']['manufacturer_code'], data_size, *ota_data_to_send) - response = self.send_data(0x0502, data, wait_status=False) + self.send_data(0x0502, data, wait_status=False) def _ota_handle_upgrade_end_request(self, request): if self._ota['active'] is True: @@ -1390,7 +1390,7 @@ def get_ota_status(self, debug=False): image_size = len(self._ota['image']['data']) time_passed = (datetime.datetime.now() - self._ota['starttime']).seconds try: - time_remaining = int((image_size/self._ota['transfered'])*time_passed) - time_passed + time_remaining = int((image_size / self._ota['transfered']) * time_passed) - time_passed except ZeroDivisionError: time_remaining = -1 message = 'OTA upgrade address {addr}: {sent:>{width}}/{total:>{width}} {percentage:.3%}'.format( @@ -1444,7 +1444,6 @@ def ota_image_notify(self, addr, destination_endpoint=0x01, payload_type=0): image_version, image_type, manufacturer_code, query_jitter) self.send_data(0x0505, data) - def attribute_discovery_request(self, addr, endpoint, cluster, direction=0, manufacturer_specific=0, manufacturer_id=0): From 51d4b1247a8a9a489dddb4f87bbc35bd78433a64 Mon Sep 17 00:00:00 2001 From: ISO-B <3048685+ISO-B@users.noreply.github.com> Date: Mon, 17 Dec 2018 14:57:08 +0200 Subject: [PATCH 30/40] Fixed travis errors --- zigate/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zigate/core.py b/zigate/core.py index e23208f4..5996a374 100644 --- a/zigate/core.py +++ b/zigate/core.py @@ -1395,7 +1395,7 @@ def get_ota_status(self, debug=False): time_remaining = -1 message = 'OTA upgrade address {addr}: {sent:>{width}}/{total:>{width}} {percentage:.3%}'.format( addr=self._ota['addr'], sent=self._ota['transfered'], total=image_size, - percentage=self._ota['transfered']/image_size, width=len(str(image_size))) + percentage=self._ota['transfered'] / image_size, width=len(str(image_size))) message += ' time elapsed: {passed}s Time remaining estimate: {remaining}s'.format( passed=time_passed, remaining=time_remaining ) From e2861e819e823f18b0e6e435eaf186bd39dc404e Mon Sep 17 00:00:00 2001 From: ISO-B <3048685+ISO-B@users.noreply.github.com> Date: Mon, 17 Dec 2018 16:54:57 +0200 Subject: [PATCH 31/40] Fixed travis errors --- zigate/core.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/zigate/core.py b/zigate/core.py index 5996a374..8754f202 100644 --- a/zigate/core.py +++ b/zigate/core.py @@ -1273,14 +1273,14 @@ def ota_load_image(self, path_to_file): if header_data[i] == 0x00: header_data[i] = 0x20 # Reconstruct header data - header_data = header_data[0:8] + [header_data[8:40]] + header_data[40:] + header_data_compact = header_data[0:8] + [header_data[8:40]] + header_data[40:] # Convert header data to dict header_headers = [ 'file_id', 'header_version', 'header_length', 'header_fctl', 'manufacturer_code', 'image_type', 'image_version', 'stack_version', 'header_str', 'size', 'security_cred_version', 'upgrade_file_dest', 'min_hw_version', 'max_hw_version' ] - header = dict(zip(header_headers, header_data)) + header = dict(zip(header_headers, header_data_compact)) # Check that size from header corresponds to file size if header['size'] != len(ota_file_content): @@ -1290,8 +1290,7 @@ def ota_load_image(self, path_to_file): destination_address_mode = 0x02 destination_address = 0x0000 - data = struct.pack('!BHlHHHHHLH32BLBQHH', destination_address_mode, destination_address, - *header_data[0:8], *header_data[8], *header_data[9:]) + data = struct.pack('!BHlHHHHHLH32BLBQHH', destination_address_mode, destination_address, *header_data) response = self.send_data(0x0500, data) # If response is success place header and file content to variable From 2e6638d111f40f571b45638c69e9019aeb6dac42 Mon Sep 17 00:00:00 2001 From: ISO-B <3048685+ISO-B@users.noreply.github.com> Date: Mon, 17 Dec 2018 17:30:28 +0200 Subject: [PATCH 32/40] Minor cleaning --- zigate/core.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/zigate/core.py b/zigate/core.py index 8754f202..64d8f9dc 100644 --- a/zigate/core.py +++ b/zigate/core.py @@ -143,16 +143,7 @@ def __init__(self, port='auto', path='~/.zigate.json', dispatcher.connect(self.interpret_response, ZIGATE_RESPONSE_RECEIVED) - self._ota = { - 'image': { - 'header': None, - 'data': None, - }, - 'active': False, - 'starttime': False, - 'transfered': 0, - 'addr': None - } + self._ota_reset_local_variables() if auto_start: self.autoStart() From cd11aea8d394817ef0480f73f199e6feb95571d2 Mon Sep 17 00:00:00 2001 From: ISO-B <3048685+ISO-B@users.noreply.github.com> Date: Mon, 17 Dec 2018 17:34:48 +0200 Subject: [PATCH 33/40] Minor cleaning --- zigate/responses.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/zigate/responses.py b/zigate/responses.py index 354d6fbc..db42e42a 100644 --- a/zigate/responses.py +++ b/zigate/responses.py @@ -784,7 +784,6 @@ class R8501(Response): ('max_data_size', 'B'), ('field_control', 'B') ]) - format = {'addr': '{:04x}'} @register_response @@ -801,7 +800,6 @@ class R8503(Response): ('manufacture_code', 'H'), ('status', 'B') ]) - format = {'addr': '{:04x}'} @register_response From b312daf0b43488f33f0a44166fcdc1cccdd815d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20RAMAGE?= Date: Tue, 18 Dec 2018 09:05:30 +0100 Subject: [PATCH 34/40] Rename vibration sensor event from take to touched --- zigate/clusters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zigate/clusters.py b/zigate/clusters.py index c4618e26..de0f655e 100644 --- a/zigate/clusters.py +++ b/zigate/clusters.py @@ -295,7 +295,7 @@ def vibration_decode(value): ''' if value == '' or value is None: return value - events = {0x0001: 'take', + events = {0x0001: 'touched', 0x0002: 'tilt', 0x0003: 'drop', } From 3e0c09d9828870351dd92067ab23237d94532c50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20RAMAGE?= Date: Tue, 18 Dec 2018 09:53:27 +0100 Subject: [PATCH 35/40] Add donate link --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a7959cf9..b464d250 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ [![PyPI version](https://badge.fury.io/py/zigate.svg)](https://pypi.python.org/pypi/zigate) [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/doudz/zigate.svg)](http://isitmaintained.com/project/doudz/zigate "Average time to resolve an issue") [![Percentage of issues still open](http://isitmaintained.com/badge/open/doudz/zigate.svg)](http://isitmaintained.com/project/doudz/zigate "Percentage of issues still open") +[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://paypal.me/sebramage) Python library for [ZiGate](http://zigate.fr/). This library manage communication between python and zigate key, both USB and WiFi key are supported. From 69089df408f77e8b08ef6ecf1ddb14f8d99508dc Mon Sep 17 00:00:00 2001 From: ISO-B <3048685+ISO-B@users.noreply.github.com> Date: Tue, 18 Dec 2018 13:03:08 +0200 Subject: [PATCH 36/40] Updated readme --- README.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a7959cf9..52f3e337 100644 --- a/README.md +++ b/README.md @@ -93,8 +93,24 @@ True # call action on devices z.action_onoff('b8ce', 1, zigate.ON) - or from devices + # or from devices z.devices[1].action_onoff(zigate.ON) + + # OTA process + # Load image and send headers to ZiGate + z.ota_load_image('path/to/ota/image_file.ota') + # Tell client that image is available + z.ota_image_notify('addr') + # It will take client usually couple seconds to query headers + # from server. Upgrade process start automatically if correct + # headers are loaded to ZiGate. If you have logging level debug + # enabled you will get automatically progress updates. + # Manually check ota status - logging level INFO + z.get_ota_status() + # Whole upgrade process time depends on device and ota image size + # Upgrading ikea bulb took ~15 minutes + # Upgrading ikea remote took ~45 minutes + ``` ### Callback From 5209f41d3a6a883d4e3eda705f313268585c65a6 Mon Sep 17 00:00:00 2001 From: ISO-B <3048685+ISO-B@users.noreply.github.com> Date: Tue, 18 Dec 2018 13:24:04 +0200 Subject: [PATCH 37/40] Updated readme --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 52f3e337..4ab27f16 100644 --- a/README.md +++ b/README.md @@ -95,22 +95,22 @@ True # or from devices z.devices[1].action_onoff(zigate.ON) - + # OTA process # Load image and send headers to ZiGate z.ota_load_image('path/to/ota/image_file.ota') # Tell client that image is available z.ota_image_notify('addr') # It will take client usually couple seconds to query headers - # from server. Upgrade process start automatically if correct - # headers are loaded to ZiGate. If you have logging level debug + # from server. Upgrade process start automatically if correct + # headers are loaded to ZiGate. If you have logging level debug # enabled you will get automatically progress updates. # Manually check ota status - logging level INFO z.get_ota_status() # Whole upgrade process time depends on device and ota image size # Upgrading ikea bulb took ~15 minutes # Upgrading ikea remote took ~45 minutes - + ``` ### Callback From b69b3f94ad5baa2769dace8a0eba5e68236afdae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20RAMAGE?= Date: Tue, 18 Dec 2018 13:33:00 +0100 Subject: [PATCH 38/40] Change max-line-length to 119, like django coding style --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index a5d65bc3..f51ac06c 100644 --- a/tox.ini +++ b/tox.ini @@ -17,6 +17,6 @@ commands = flake8 . [flake8] -max-line-length = 160 +max-line-length = 119 exclude = .tox,*.egg,build/*,docs/*, select = E,W,F \ No newline at end of file From 97bcba8f4ebee7261b51a9770b8e21daee6ad47d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20RAMAGE?= Date: Tue, 18 Dec 2018 13:37:25 +0100 Subject: [PATCH 39/40] Fix PEP8 --- zigate/core.py | 3 ++- zigate/flasher.py | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/zigate/core.py b/zigate/core.py index 70094871..e5dd2db8 100644 --- a/zigate/core.py +++ b/zigate/core.py @@ -1376,7 +1376,8 @@ def _ota_send_image_data(self, request): data = struct.pack('!BHBBBBLLHHB{}B'.format(data_size), request['address_mode'], self.__addr(request['addr']), source_endpoint, request['endpoint'], request['sequence'], ota_status, request['file_offset'], self._ota['image']['header']['image_version'], - self._ota['image']['header']['image_type'], self._ota['image']['header']['manufacturer_code'], + self._ota['image']['header']['image_type'], + self._ota['image']['header']['manufacturer_code'], data_size, *ota_data_to_send) self.send_data(0x0502, data, wait_status=False) diff --git a/zigate/flasher.py b/zigate/flasher.py index 08f43983..2f46c53e 100644 --- a/zigate/flasher.py +++ b/zigate/flasher.py @@ -172,7 +172,10 @@ def __init__(self, *args): self.manufacturer_id, self.device_id = struct.unpack('!BB', self.data) def __str__(self): - return 'ReadFlashIDResponse %d (ok=%s, manufacturer_id=0x%02x, device_id=0x%02x)' % (self.status, self.ok, self.manufacturer_id, self.device_id) + return 'ReadFlashIDResponse %d (ok=%s, manufacturer_id=0x%02x, device_id=0x%02x)' % (self.status, + self.ok, + self.manufacturer_id, + self.device_id) @register(0x28) From fe84eb045e71832fc3cb7e864a36187575f4a332 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20RAMAGE?= Date: Thu, 20 Dec 2018 10:06:24 +0100 Subject: [PATCH 40/40] Release 0.24.0 --- zigate/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zigate/version.py b/zigate/version.py index 0fcc5eac..2a844165 100644 --- a/zigate/version.py +++ b/zigate/version.py @@ -5,4 +5,4 @@ # file that was distributed with this source code. # -__version__ = '0.24.0dev0' +__version__ = '0.24.0'