Skip to content

Commit 3a39838

Browse files
authored
Merge pull request #19 from OleksandrBerchenko/master
Improve logging and error handling
2 parents 1a7c88b + 4b37dc8 commit 3a39838

File tree

2 files changed

+131
-105
lines changed

2 files changed

+131
-105
lines changed

lightify/__init__.py

Lines changed: 130 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
import time
3535
from enum import Enum
3636

37-
__version__ = '1.0.7.1'
37+
__version__ = '1.0.7.2'
3838
MODULE = __name__
3939
PORT = 4000
4040

@@ -652,6 +652,10 @@ def update_status(self):
652652
653653
:return:
654654
"""
655+
if not [addr for addr in self.__lights
656+
if addr in self.__conn.lights()]:
657+
return
658+
655659
features = [self.__conn.lights()[addr].supported_features()
656660
for addr in self.__lights if addr in self.__conn.lights()]
657661
self.__supported_features = set.union(*features)
@@ -1297,25 +1301,36 @@ def update_group_list(self, throttling_interval=None):
12971301
command = self.build_group_list()
12981302
data = self.send(command)
12991303

1300-
(num,) = struct.unpack('<H', data[7:9])
1301-
self.__logger.debug('Number of groups: %d', num)
1302-
new_groups = {}
1303-
1304-
for i in range(0, num):
1305-
pos = 9 + i * 18
1306-
payload = data[pos:pos + 18]
1307-
1308-
(idx, name) = struct.unpack('<H16s', payload)
1309-
name = name.decode('utf-8').replace('\0', '')
1304+
try:
1305+
(num,) = struct.unpack('<H', data[7:9])
1306+
self.__logger.debug('Number of groups: %d', num)
1307+
if len(data) < 8 + num * 18:
1308+
raise struct.error('Incorrect data length for {} records:'
1309+
' {}'.format(num, len(data)))
1310+
1311+
new_groups = {}
1312+
for i in range(0, num):
1313+
pos = 9 + i * 18
1314+
payload = data[pos:pos + 18]
1315+
self.__logger.debug('Group payload: %d', i)
1316+
1317+
(idx, name) = struct.unpack('<H16s', payload)
1318+
name = name.decode('utf-8').replace('\0', '')
1319+
1320+
if (name in self.__groups and
1321+
self.__groups[name].idx() == idx):
1322+
group = self.__groups[name]
1323+
self.__logger.debug('Old group %d: %s', idx, name)
1324+
else:
1325+
group = Group(self, idx, name)
1326+
self.__logger.debug('New group %d: %s', idx, name)
13101327

1311-
if name in self.__groups and self.__groups[name].idx() == idx:
1312-
group = self.__groups[name]
1313-
self.__logger.debug('Old group %d: %s', idx, name)
1314-
else:
1315-
group = Group(self, idx, name)
1316-
self.__logger.debug('New group %d: %s', idx, name)
1328+
new_groups[name] = group
13171329

1318-
new_groups[name] = group
1330+
except (struct.error, UnicodeDecodeError) as err:
1331+
self.__logger.error('Couldn\'t parse group status: %s', err)
1332+
self.__logger.error('Data: %s', binascii.hexlify(data))
1333+
return {}
13191334

13201335
for name in self.__groups:
13211336
if (name not in new_groups or
@@ -1397,30 +1412,40 @@ def update_scene_list(self, throttling_interval=None):
13971412
command = self.build_scene_list()
13981413
data = self.send(command)
13991414

1400-
(num,) = struct.unpack('<H', data[7:9])
1401-
self.__logger.debug('Number of scenes: %d', num)
1402-
new_scenes = {}
1403-
1404-
for i in range(0, num):
1405-
pos = 9 + i * 20
1406-
payload = data[pos:pos + 20]
1407-
1408-
(idx, name, group) = struct.unpack('<Bx16sH', payload)
1409-
name = name.decode('utf-8').replace('\0', '')
1410-
group = 16 - format(group, '016b').index('1')
1415+
try:
1416+
(num,) = struct.unpack('<H', data[7:9])
1417+
self.__logger.debug('Number of scenes: %d', num)
1418+
if len(data) < 8 + num * 20:
1419+
raise struct.error('Incorrect data length for {} records:'
1420+
' {}'.format(num, len(data)))
1421+
1422+
new_scenes = {}
1423+
for i in range(0, num):
1424+
pos = 9 + i * 20
1425+
payload = data[pos:pos + 20]
1426+
self.__logger.debug('Scene payload: %d', i)
1427+
1428+
(idx, name, group) = struct.unpack('<Bx16sH', payload)
1429+
name = name.decode('utf-8').replace('\0', '')
1430+
group = 16 - format(group, '016b').index('1')
1431+
1432+
if (name in self.__scenes and
1433+
self.__scenes[name].idx() == idx and
1434+
self.__scenes[name].group() == group):
1435+
scene = self.__scenes[name]
1436+
self.__logger.debug('Old scene %d: %s, group: %d', idx,
1437+
name, group)
1438+
else:
1439+
scene = Scene(self, idx, name, group)
1440+
self.__logger.debug('New scene %d: %s, group: %d', idx,
1441+
name, group)
14111442

1412-
if (name in self.__scenes and
1413-
self.__scenes[name].idx() == idx and
1414-
self.__scenes[name].group() == group):
1415-
scene = self.__scenes[name]
1416-
self.__logger.debug('Old scene %d: %s, group: %d', idx,
1417-
name, group)
1418-
else:
1419-
scene = Scene(self, idx, name, group)
1420-
self.__logger.debug('New scene %d: %s, group: %d', idx,
1421-
name, group)
1443+
new_scenes[name] = scene
14221444

1423-
new_scenes[name] = scene
1445+
except (struct.error, UnicodeDecodeError, ValueError) as err:
1446+
self.__logger.error('Couldn\'t parse scene status: %s', err)
1447+
self.__logger.error('Data: %s', binascii.hexlify(data))
1448+
return {}
14241449

14251450
for name in self.__scenes:
14261451
if (name not in new_scenes or
@@ -1541,74 +1566,75 @@ def update_all_light_status(self, throttling_interval=None):
15411566
self.__lights_updated = time.time()
15421567
return {}
15431568

1544-
(num,) = struct.unpack('<H', data[7:9])
1545-
self.__logger.debug('Number of lights: %d', num)
1546-
new_lights = {}
1547-
1548-
for i in range(0, num):
1549-
pos = 9 + i * 50
1550-
payload = data[pos:pos + 50]
1551-
self.__logger.debug('Light payload: %d %d %d', i, pos,
1552-
len(payload))
1569+
try:
1570+
(num,) = struct.unpack('<H', data[7:9])
1571+
self.__logger.debug('Number of lights: %d', num)
1572+
if len(data) < 8 + num * 50:
1573+
raise struct.error('Incorrect data length for {} records:'
1574+
' {}'.format(num, len(data)))
1575+
1576+
new_lights = {}
1577+
for i in range(0, num):
1578+
pos = 9 + i * 50
1579+
payload = data[pos:pos + 50]
1580+
self.__logger.debug('Light payload: %d', i)
15531581

1554-
try:
15551582
(addr, stat, name, last_seen) = struct.unpack(
15561583
'<2xQ16s16sI4x', payload)
1557-
except struct.error as err:
1558-
self.__logger.warning(
1559-
'Couldn\'t unpack light status packet:')
1560-
self.__logger.warning('struct.error: %s', err)
1561-
self.__logger.warning('payload: %s',
1562-
binascii.hexlify(payload))
1563-
return {}
1564-
1565-
(type_id, version, reachable, groups, onoff, lum, temp, red,
1566-
green, blue, alpha) = struct.unpack('<B4sBH2BH4B', stat)
1567-
name = name.decode('utf-8').replace('\0', '')
1568-
groups = [16 - j for j, val
1569-
in enumerate(format(groups, '016b')) if val == '1']
1570-
version = format(struct.unpack('>I', version)[0], '032b')
1571-
version = ''.join('{0:01X}'.format(
1572-
int(version[i * 4:(i + 1) * 4], 2)) for i in range(8))
1573-
1574-
if addr in self.__lights:
1575-
light = self.__lights[addr]
1576-
self.__logger.debug('Old light: %x', addr)
1577-
else:
1578-
if type_id not in self.__device_types:
1579-
self.__logger.warning(
1580-
'Unknown device type id: %s. Please report to '
1581-
'https://github.com/tfriedel/python-lightify',
1582-
type_id)
1583-
if (red, green, blue) == NO_RGB_VALUES:
1584-
type_id_assumed = TYPE_LIGHT_TUNABLE_WHITE
1585-
else:
1586-
type_id_assumed = TYPE_LIGHT_RGBW
1584+
(type_id, version, reachable, groups,
1585+
onoff, lum, temp, red,
1586+
green, blue, alpha) = struct.unpack('<B4sBH2BH4B', stat)
1587+
name = name.decode('utf-8').replace('\0', '')
1588+
groups = [16 - j for j, val
1589+
in enumerate(format(groups, '016b'))
1590+
if val == '1']
1591+
version = format(struct.unpack('>I', version)[0], '032b')
1592+
version = ''.join('{0:01X}'.format(
1593+
int(version[i * 4:(i + 1) * 4], 2)) for i in range(8))
1594+
1595+
if addr in self.__lights:
1596+
light = self.__lights[addr]
1597+
self.__logger.debug('Old light: %x', addr)
15871598
else:
1588-
type_id_assumed = type_id
1589-
1590-
light = Light(self, addr, type_id, type_id_assumed)
1591-
self.__logger.debug('New light: %x', addr)
1592-
1593-
self.__logger.debug('name: %s', name)
1594-
self.__logger.debug('reachable: %d', reachable)
1595-
self.__logger.debug('last seen: %d', last_seen)
1596-
self.__logger.debug('onoff: %d', onoff)
1597-
self.__logger.debug('lum: %d', lum)
1598-
self.__logger.debug('temp: %d', temp)
1599-
self.__logger.debug('red: %d', red)
1600-
self.__logger.debug('green: %d', green)
1601-
self.__logger.debug('blue: %d', blue)
1602-
self.__logger.debug('alpha: %d', alpha)
1603-
self.__logger.debug('type id: %d', type_id)
1604-
self.__logger.debug('groups: %s', groups)
1605-
self.__logger.debug('version: %s', version)
1606-
self.__logger.debug('idx: %s', i)
1607-
1608-
light.update_status(reachable, last_seen, onoff, lum, temp,
1609-
red, green, blue, alpha, name, groups,
1610-
version, i)
1611-
new_lights[addr] = light
1599+
if type_id not in self.__device_types:
1600+
self.__logger.warning(
1601+
'Unknown device type id: %s. Please report to '
1602+
'https://github.com/tfriedel/python-lightify',
1603+
type_id)
1604+
if (red, green, blue) == NO_RGB_VALUES:
1605+
type_id_assumed = TYPE_LIGHT_TUNABLE_WHITE
1606+
else:
1607+
type_id_assumed = TYPE_LIGHT_RGBW
1608+
else:
1609+
type_id_assumed = type_id
1610+
1611+
light = Light(self, addr, type_id, type_id_assumed)
1612+
self.__logger.debug('New light: %x', addr)
1613+
1614+
self.__logger.debug('name: %s', name)
1615+
self.__logger.debug('reachable: %d', reachable)
1616+
self.__logger.debug('last seen: %d', last_seen)
1617+
self.__logger.debug('onoff: %d', onoff)
1618+
self.__logger.debug('lum: %d', lum)
1619+
self.__logger.debug('temp: %d', temp)
1620+
self.__logger.debug('red: %d', red)
1621+
self.__logger.debug('green: %d', green)
1622+
self.__logger.debug('blue: %d', blue)
1623+
self.__logger.debug('alpha: %d', alpha)
1624+
self.__logger.debug('type id: %d', type_id)
1625+
self.__logger.debug('groups: %s', groups)
1626+
self.__logger.debug('version: %s', version)
1627+
self.__logger.debug('idx: %s', i)
1628+
1629+
light.update_status(reachable, last_seen, onoff, lum, temp,
1630+
red, green, blue, alpha, name, groups,
1631+
version, i)
1632+
new_lights[addr] = light
1633+
1634+
except (struct.error, UnicodeDecodeError) as err:
1635+
self.__logger.error('Couldn\'t parse light status: %s', err)
1636+
self.__logger.error('Data: %s', binascii.hexlify(data))
1637+
return {}
16121638

16131639
for addr in self.__lights:
16141640
if addr not in new_lights:

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
setup(
44
name='lightify',
5-
version='1.0.7.1',
5+
version='1.0.7.2',
66
packages=['lightify'],
77
include_package_data=True,
88
license='Apache License (2.0)',

0 commit comments

Comments
 (0)