Skip to content
This repository has been archived by the owner on Oct 5, 2024. It is now read-only.

Commit

Permalink
Merge pull request #115 from doudz/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
doudz authored Feb 26, 2019
2 parents b0cbc03 + 51e8e29 commit 715687f
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 35 deletions.
18 changes: 15 additions & 3 deletions tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ def setUp(self):
# self.zigate._ieee = '0123456789abcdef'
# self.zigate.connection = transport.FakeTransport()
self.zigate = core.FakeZiGate(auto_start=False)
self.zigate._start_event_thread()
self.zigate.setup_connection()
self.test_dir = tempfile.mkdtemp()

Expand Down Expand Up @@ -650,6 +651,15 @@ def test_assumed_state(self):
# {'attribute': 0, 'data': False, 'name': 'onoff', 'value': False, 'type': bool,
# 'state': 'assumed'})

def test_handle_response_8045(self):
device = core.Device({'addr': '1234', 'ieee': '0123456789abcdef'},
self.zigate)
self.zigate._devices['1234'] = device
msg_data = unhexlify(b'7e0012340101')
r = responses.R8045(msg_data, 255)
self.zigate.interpret_response(r)
self.assertTrue(1 in device.endpoints)

def test_handle_response_8085(self):
device = core.Device({'addr': '1234', 'ieee': '0123456789abcdef'},
self.zigate)
Expand Down Expand Up @@ -699,10 +709,12 @@ def test_build_neighbours_table(self):
self.zigate.connection.add_auto_response((0x004e, b'000000'), 0x804e,
unhexlify(b'0100010100abcd0123456789abcdef0123456789abcdef01b665'))
self.zigate.connection.add_auto_response((0x004e, b'abcd00'), 0x804e,
unhexlify(b'010002020000000123456789abcdef0123456789abcdef01b645'
b'98760123456789abcdef0123456789abcdef01b616'))
unhexlify(b'010003030000000123456789abcdef0123456789abcdef01b645'
b'98760123456789abcdef0123456789abcdef01b616'
b'12340123456789abcdef0123456789abcdef00b622'))
table = self.zigate.build_neighbours_table()
self.assertEqual(table, [('0000', 'abcd', 182), ('abcd', '9876', 182)])
self.assertEqual(table, [('0000', 'abcd', 182), ('abcd', '9876', 182),
('0000', '1234', 182)])

def test_build_network_map(self):
filename = os.path.join(self.test_dir, 'zigate_network.png')
Expand Down
34 changes: 34 additions & 0 deletions tests/test_responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,40 @@


class TestResponses(unittest.TestCase):
def test_response_8002(self):
msg_data = unhexlify(b'0001000006020102123402abcd0401234567')
r = responses.R8002(msg_data, 255)
self.assertDictEqual(r.data,
OrderedDict([('status', 0),
('profile_id', 256),
('cluster_id', 6),
('source_endpoint', 2),
('destination_endpoint', 1),
('lqi', 255),
('source_address_mode', 2),
('source_address', '1234'),
('dst_address_mode', 2),
('dst_address', 'abcd'),
('payload_size', 4),
('payload', b'\x01#Eg')])
)
msg_data = unhexlify(b'00010000060201030123456789abcdef03fedcba98765432100401234567')
r = responses.R8002(msg_data, 255)
self.assertDictEqual(r.data,
OrderedDict([('status', 0),
('profile_id', 256),
('cluster_id', 6),
('source_endpoint', 2),
('destination_endpoint', 1),
('lqi', 255),
('source_address_mode', 3),
('source_address', '0123456789abcdef'),
('dst_address_mode', 3),
('dst_address', 'fedcba9876543210'),
('payload_size', 4),
('payload', b'\x01#Eg')])
)

def test_response_8024(self):
# good status
msg_data = b'\x01\x124\x00\x00\x00\x00\x00\x00\x00\x00\x01'
Expand Down
72 changes: 50 additions & 22 deletions zigate/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,18 +167,18 @@ def __init__(self, port='auto', path='~/.zigate.json',
self._started = False
self._no_response_count = 0

self._event_thread = threading.Thread(target=self._event_loop,
name='ZiGate-Event Loop')
self._event_thread.setDaemon(True)
self._event_thread.start()
# self._event_thread = threading.Thread(target=self._event_loop,
# name='ZiGate-Event Loop')
# self._event_thread.setDaemon(True)
# self._event_thread.start()

self._ota_reset_local_variables()

if adminpanel:
self.start_adminpanel()

if auto_start:
self.autoStart(channel)
self.startup(channel)
if auto_save:
self.start_auto_save()

Expand All @@ -198,7 +198,7 @@ def start_adminpanel(self):
start_adminpanel(self)

def _event_loop(self):
while True:
while not self._closing:
if self.connection and not self.connection.received.empty():
packet = self.connection.received.get()
dispatch_signal(ZIGATE_PACKET_RECEIVED, self, packet=packet)
Expand All @@ -223,6 +223,7 @@ def close(self):
except Exception:
LOGGER.error('Exception during closing')
LOGGER.error(traceback.format_exc())
self.connection = None
self._started = False

def save_state(self, path=None):
Expand Down Expand Up @@ -310,9 +311,18 @@ def start_auto_save(self):
def __del__(self):
self.close()

def _start_event_thread(self):
self._event_thread = threading.Thread(target=self._event_loop,
name='ZiGate-Event Loop')
self._event_thread.setDaemon(True)
self._event_thread.start()

def autoStart(self, channel=None):
self.startup(channel)

def startup(self, channel=None):
'''
Auto Start sequence:
Startup sequence:
- Load persistent file
- setup connection
- Set Channel mask
Expand All @@ -322,6 +332,8 @@ def autoStart(self, channel=None):
'''
if self._started:
return
self._closing = False
self._start_event_thread()
self.load_state()
self.setup_connection()
version = self.get_version()
Expand All @@ -332,7 +344,8 @@ def autoStart(self, channel=None):
network_state = self.get_network_state()
if not network_state:
LOGGER.error('Failed to get network state')
if not network_state or network_state.get('extended_panid') == 0:
if not network_state or network_state.get('extended_panid') == 0 or \
network_state.get('addr') == 'ffff':
LOGGER.debug('Network is down, start it')
self.start_network(True)

Expand Down Expand Up @@ -482,6 +495,10 @@ def interpret_response(self, response):
response.status_text(),
response['error']))
self._last_status[response['packet_type']] = response
elif response.msg == 0x8007: # factory reset
if response['status'] == 0:
self._devices = {}
self.start_network()
elif response.msg == 0x8015: # device list
keys = set(self._devices.keys())
known_addr = set([d['addr'] for d in response['devices']])
Expand Down Expand Up @@ -517,7 +534,7 @@ def interpret_response(self, response):
d = self.get_device_from_addr(addr)
if d:
for endpoint in response['endpoints']:
ep = d.get_endpoint(endpoint)
ep = d.get_endpoint(endpoint['endpoint'])
self.simple_descriptor_request(addr, endpoint['endpoint'])
self.discover_device(addr)
elif response.msg == 0x8048: # leave
Expand Down Expand Up @@ -765,15 +782,13 @@ def erase_persistent(self):
'''
erase persistent data in zigate
'''
self._devices = {}
return self.send_data(0x0012)
return self.send_data(0x0012, wait_status=False)

def factory_reset(self):
'''
ZLO/ZLL "Factory New" Reset
'''
self._devices = {}
return self.send_data(0x0013)
return self.send_data(0x0013, wait_status=False)

def is_permitting_join(self):
'''
Expand Down Expand Up @@ -1055,7 +1070,13 @@ def lqi_request(self, addr='0000', index=0, wait=False):
r = self.send_data(0x004e, data, wait_response=wait_response)
return r

def build_neighbours_table(self, addr='0000', nodes=None):
def build_neighbours_table(self):
'''
Build neighbours table
'''
return self._neighbours_table(self.addr)

def _neighbours_table(self, addr, nodes=None):
'''
Build neighbours table
'''
Expand All @@ -1074,16 +1095,23 @@ def build_neighbours_table(self, addr='0000', nodes=None):
data = r.cleaned_data()
entries = data['entries']
for n in data['neighbours']:
# bit_field
# bit 0-1 = u2RxOnWhenIdle 0/1
# bit 2-3 = u2Relationship 0/1/2
# bit 4-5 = u2PermitJoining 0/1
# bit 6-7 = u2DeviceType 0/1/2
is_parent = n['bit_field'][2:4] == '00'
is_child = n['bit_field'][2:4] == '01'
is_router = n['bit_field'][6:8] == '01'
if is_parent:
neighbours.append((n['addr'], addr, n['lqi']))
elif is_child:
neighbours.append((addr, n['addr'], n['lqi']))
elif n['depth'] == 0:
neighbours.append((self.addr, n['addr'], n['lqi']))
if is_router and n['addr'] not in nodes:
LOGGER.debug('{} is a router, search for children'.format(n['addr']))
n2 = self.build_neighbours_table(n['addr'], nodes)
n2 = self._neighbours_table(n['addr'], nodes)
if n2:
neighbours += n2
index += data['count']
Expand Down Expand Up @@ -1868,7 +1896,7 @@ def action_move_hue_hex(self, addr, endpoint, color_hex, transition=0):
transition in second
'''
rgb = hex_to_rgb(color_hex)
return self.actions_move_hue_rgb(addr, endpoint, rgb, transition)
return self.action_move_hue_rgb(addr, endpoint, rgb, transition)

@register_actions(ACTIONS_HUE)
def action_move_hue_rgb(self, addr, endpoint, rgb, transition=0):
Expand All @@ -1881,7 +1909,7 @@ def action_move_hue_rgb(self, addr, endpoint, rgb, transition=0):
saturation = int(saturation * 100)
level = int(level * 100)
self.action_move_level_onoff(addr, endpoint, ON, level, 0)
return self.actions_move_hue_saturation(addr, endpoint, hue, saturation, transition)
return self.action_move_hue_saturation(addr, endpoint, hue, saturation, transition)

@register_actions(ACTIONS_COLOR)
def action_move_colour(self, addr, endpoint, x, y, transition=0):
Expand All @@ -1908,7 +1936,7 @@ def action_move_colour_hex(self, addr, endpoint, color_hex, transition=0):
transition in second
'''
x, y = hex_to_xy(color_hex)
return self.actions_move_colour(addr, endpoint, x, y, transition)
return self.action_move_colour(addr, endpoint, x, y, transition)

@register_actions(ACTIONS_COLOR)
def action_move_colour_rgb(self, addr, endpoint, rgb, transition=0):
Expand All @@ -1918,7 +1946,7 @@ def action_move_colour_rgb(self, addr, endpoint, rgb, transition=0):
transition in second
'''
x, y = rgb_to_xy(rgb)
return self.actions_move_colour(addr, endpoint, x, y, transition)
return self.action_move_colour(addr, endpoint, x, y, transition)

@register_actions(ACTIONS_TEMPERATURE)
def action_move_temperature(self, addr, endpoint, mired, transition=0):
Expand All @@ -1942,7 +1970,7 @@ def action_move_temperature_kelvin(self, addr, endpoint, temperature, transition
convenient function to use kelvin instead of mired
'''
temperature = int(1000000 // temperature)
return self.actions_move_temperature(addr, endpoint, temperature, transition)
return self.action_move_temperature(addr, endpoint, temperature, transition)

@register_actions(ACTIONS_TEMPERATURE)
def action_move_temperature_rate(self, addr, endpoint, mode, rate, min_temperature, max_temperature):
Expand Down Expand Up @@ -2055,8 +2083,8 @@ def __init__(self, port='auto', path='~/.zigate.json',
device.load_template()
self._devices['abcd'] = device

def autoStart(self, channel=None):
ZiGate.autoStart(self, channel=channel)
def startup(self, channel=None):
ZiGate.startup(self, channel=channel)
self.connection.start_fake_response()

def setup_connection(self):
Expand Down
53 changes: 44 additions & 9 deletions zigate/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def __getitem__(self, key):
def __delitem__(self, key):
return self.data.__delitem__(key)

def get(self, key, default):
def get(self, key, default=None):
return self.data.get(key, default)

def __contains__(self, key):
Expand Down Expand Up @@ -174,18 +174,45 @@ class R8002(Response):
('cluster_id', 'H'),
('source_endpoint', 'B'),
('destination_endpoint', 'B'),
('source_address_mode', 'B'),
('source_address', 'H'),
('dst_address_mode', 'B'),
('dst_address', 'H'),
('payload_size', 'B'),
('payload', 'rawend')
# ('source_address_mode', 'B'),
# ('source_address', 'H'),
# ('dst_address_mode', 'B'),
# ('dst_address', 'H'),
# ('payload_size', 'B'),
# ('payload', 'rawend')
])

def decode(self):
Response.decode(self)
self.data['payload'] = struct.unpack('!{}B'.format(self.data['payload_size']),
self.data['payload'])[0]
additionnal = self.data.pop('additionnal')
source_address_mode = struct.unpack('!B', additionnal[:1])[0]
self.data['source_address_mode'] = source_address_mode
additionnal = additionnal[1:]
if source_address_mode == 3:
source_address = struct.unpack('!Q', additionnal[:8])[0]
source_address = '{:016x}'.format(source_address)
additionnal = additionnal[8:]
else:
source_address = struct.unpack('!H', additionnal[:2])[0]
source_address = '{:04x}'.format(source_address)
additionnal = additionnal[2:]
self.data['source_address'] = source_address

dst_address_mode = struct.unpack('!B', additionnal[:1])[0]
self.data['dst_address_mode'] = dst_address_mode
additionnal = additionnal[1:]
if dst_address_mode == 3:
dst_address = struct.unpack('!Q', additionnal[:8])[0]
dst_address = '{:016x}'.format(dst_address)
additionnal = additionnal[8:]
else:
dst_address = struct.unpack('!H', additionnal[:2])[0]
dst_address = '{:04x}'.format(dst_address)
additionnal = additionnal[2:]
self.data['dst_address'] = dst_address
payload_size = struct.unpack('!B', additionnal[:1])[0]
self.data['payload_size'] = payload_size
self.data['payload'] = additionnal[1:]


@register_response
Expand Down Expand Up @@ -362,6 +389,14 @@ class R8040(Response):
class R8041(R8040):
msg = 0x8041
type = 'IEEE Address response'
s = OrderedDict([('sequence', 'B'),
('status', 'B'),
('ieee', 'Q'),
('addr', 'H'),
('count', 'B'),
('index', 'B'),
('devices', OrderedDict([('ieee', 'Q')]))
])


@register_response
Expand Down
3 changes: 3 additions & 0 deletions zigate/transport.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ def send(self, data):
def is_connected(self):
pass

def close(self):
pass


class FakeTransport(BaseTransport):
'''
Expand Down
2 changes: 1 addition & 1 deletion zigate/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
# file that was distributed with this source code.
#

__version__ = '0.28.1'
__version__ = '0.28.2'

0 comments on commit 715687f

Please sign in to comment.