Repository: nickw444/nessclient

Issue:
Zones 15 and 16 cause Nessclient 0.9.13 to loose connection
Hi Nick,

I'm not too sure about the best way to get in touch with you regarding the issue I'm having with the current build of nessclient.  
With P199E options 1, 2, 3 and 6 enabled I get the following error when triggering Zones 15 and 16.  I only have zones 1-4, 15 and 16 connected to my panel at the moment.

I'm pretty sure the issue is in the decode function of the event class as it's converting decimal 15/16 to hex 21/22 and saving that as the zone.  

Unfortunately i'm not yet confident enough in python (or github) to create a pull request to fix this but I think you need to change 
`zone=data[1],` on line 102 of event.py to
`zone=int(data[1], 16),`

The debug log for outputs 15 and 16 is copied below.

### Zone 16:

```

DEBUG:nessclient.client:Decoding data: '8700036100160019022823032274'
DEBUG:nessclient.packet:Decoding bytes: '8700036100160019022823032274'
<SystemStatusEvent {'address': 0, 'timestamp': datetime.datetime(2019, 2, 28, 23, 3, 22), 'type': <EventType.UNSEALED: 0>, 'zone': 22, 'area': 0}>
ERROR:asyncio:Task exception was never retrieved
future: <Task finished coro=<Client.keepalive() done, defined at /usr/local/lib/python3.7/site-packages/nessclient/client.py:145> exception=IndexError('list index out of range')>
Traceback (most recent call last):
  File "/usr/local/lib/python3.7/site-packages/nessclient/client.py", line 148, in keepalive
    self._update_loop(),
  File "/usr/local/lib/python3.7/site-packages/nessclient/client.py", line 131, in _recv_loop
    self.alarm.handle_event(event)
  File "/usr/local/lib/python3.7/site-packages/nessclient/alarm.py", line 50, in handle_event
    self._handle_system_status_event(event)
  File "/usr/local/lib/python3.7/site-packages/nessclient/alarm.py", line 81, in _handle_system_status_event
    return self._update_zone(event.zone, True)
  File "/usr/local/lib/python3.7/site-packages/nessclient/alarm.py", line 113, in _update_zone
    zone = self.zones[zone_id - 1]
IndexError: list index out of range
```

### Zone 15
```

DEBUG:nessclient.client:Decoding data: '8700036100150019022823094849'
DEBUG:nessclient.packet:Decoding bytes: '8700036100150019022823094849'
<SystemStatusEvent {'address': 0, 'timestamp': datetime.datetime(2019, 2, 28, 23, 9, 48), 'type': <EventType.UNSEALED: 0>, 'zone': 21, 'area': 0}>
ERROR:asyncio:Task exception was never retrieved
future: <Task finished coro=<Client.keepalive() done, defined at /usr/local/lib/python3.7/site-packages/nessclient/client.py:145> exception=IndexError('list index out of range')>
Traceback (most recent call last):
  File "/usr/local/lib/python3.7/site-packages/nessclient/client.py", line 148, in keepalive
    self._update_loop(),
  File "/usr/local/lib/python3.7/site-packages/nessclient/client.py", line 131, in _recv_loop
    self.alarm.handle_event(event)
  File "/usr/local/lib/python3.7/site-packages/nessclient/alarm.py", line 50, in handle_event
    self._handle_system_status_event(event)
  File "/usr/local/lib/python3.7/site-packages/nessclient/alarm.py", line 81, in _handle_system_status_event
    return self._update_zone(event.zone, True)
  File "/usr/local/lib/python3.7/site-packages/nessclient/alarm.py", line 113, in _update_zone
    zone = self.zones[zone_id - 1]
IndexError: list index out of range
```
Regards,
Anthony



Discussion:

Patch:
diff --git a/nessclient/event.py b/nessclient/event.py
index d4aab6c..2877a70 100644
--- a/nessclient/event.py
+++ b/nessclient/event.py
@@ -96,11 +96,13 @@ class SystemStatusEvent(BaseEvent):
 
     @classmethod
     def decode(cls, packet: Packet) -> 'SystemStatusEvent':
-        data = bytearray.fromhex(packet.data)
+        event_type = int(packet.data[0:2], 16)
+        zone = int(packet.data[2:4])
+        area = int(packet.data[4:6], 16)
         return SystemStatusEvent(
-            type=SystemStatusEvent.EventType(data[0]),
-            zone=data[1],
-            area=data[2],
+            type=SystemStatusEvent.EventType(event_type),
+            zone=zone,
+            area=area,
             timestamp=packet.timestamp,
             address=packet.address,
         )


Test patch:
diff --git a/nessclient_tests/test_event.py b/nessclient_tests/test_event.py
index a8a1d1b..85c904e 100644
--- a/nessclient_tests/test_event.py
+++ b/nessclient_tests/test_event.py
@@ -127,6 +127,20 @@ class SystemStatusEventTestCase(unittest.TestCase):
         self.assertEqual(event.type,
                          SystemStatusEvent.EventType.SEALED)
 
+    def test_zone_unsealed_with_zone_15(self):
+        pkt = make_packet(CommandType.SYSTEM_STATUS, '001500')
+        event = SystemStatusEvent.decode(pkt)
+        self.assertEqual(event.area, 0)
+        self.assertEqual(event.zone, 15)
+        self.assertEqual(event.type, SystemStatusEvent.EventType.UNSEALED)
+
+    def test_zone_unsealed_with_zone_16(self):
+        pkt = make_packet(CommandType.SYSTEM_STATUS, '001600')
+        event = SystemStatusEvent.decode(pkt)
+        self.assertEqual(event.area, 0)
+        self.assertEqual(event.zone, 16)
+        self.assertEqual(event.type, SystemStatusEvent.EventType.UNSEALED)
+
 
 def make_packet(command: CommandType, data: str):
     return Packet(address=0, command=command,
diff --git a/nessclient_tests/test_packet.py b/nessclient_tests/test_packet.py
index 3464909..9ef6bd3 100644
--- a/nessclient_tests/test_packet.py
+++ b/nessclient_tests/test_packet.py
@@ -131,3 +131,14 @@ class PacketTestCase(unittest.TestCase):
         self.assertEqual(pkt.data, '000700')
         self.assertEqual(pkt.timestamp, datetime.datetime(
             year=2019, month=2, day=25, hour=18, minute=0, second=0))
+
+    def test_decode_zone_16(self):
+        pkt = Packet.decode('8700036100160019022823032274')
+        self.assertEqual(pkt.start, 0x87)
+        self.assertEqual(pkt.address, 0x00)
+        self.assertEqual(pkt.length, 3)
+        self.assertEqual(pkt.seq, 0x00)
+        self.assertEqual(pkt.command, CommandType.SYSTEM_STATUS)
+        self.assertEqual(pkt.data, '001600')
+        self.assertEqual(pkt.timestamp, datetime.datetime(
+            year=2019, month=2, day=28, hour=23, minute=3, second=22))