Skip to content

Commit 8a1a917

Browse files
authored
Merge pull request #58 from nutechsoftware/dev
Merge 1.13.10 into master
2 parents 8463a55 + 83cfd4d commit 8a1a917

File tree

6 files changed

+55
-42
lines changed

6 files changed

+55
-42
lines changed

alarmdecoder/devices/serial_device.py

Lines changed: 18 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import select
1515
import sys
1616
from .base_device import Device
17-
from ..util import CommError, TimeoutError, NoDeviceError, bytes_hack
17+
from ..util import CommError, TimeoutError, NoDeviceError, bytes_hack, filter_ad2prot_byte
1818

1919

2020
class SerialDevice(Device):
@@ -141,7 +141,7 @@ def close(self):
141141
def fileno(self):
142142
"""
143143
Returns the file number associated with the device
144-
144+
145145
:returns: int
146146
"""
147147
return self._device.fileno()
@@ -178,13 +178,13 @@ def read(self):
178178
:returns: character read from the device
179179
:raises: :py:class:`~alarmdecoder.util.CommError`
180180
"""
181-
data = ''
181+
data = b''
182182

183183
try:
184184
read_ready, _, _ = select.select([self._device.fileno()], [], [], 0.5)
185185

186186
if len(read_ready) != 0:
187-
data = self._device.read(1)
187+
data = filter_ad2prot_byte(self._device.read(1))
188188

189189
except serial.SerialException as err:
190190
raise CommError('Error reading from device: {0}'.format(str(err)), err)
@@ -213,62 +213,46 @@ def timeout_event():
213213
if purge_buffer:
214214
self._buffer = b''
215215

216-
got_line, data = False, ''
216+
got_line, ret = False, None
217217

218218
timer = threading.Timer(timeout, timeout_event)
219219
if timeout > 0:
220220
timer.start()
221221

222-
leftovers = b''
223222
try:
224-
while timeout_event.reading and not got_line:
223+
while timeout_event.reading:
225224
read_ready, _, _ = select.select([self._device.fileno()], [], [], 0.5)
225+
226226
if len(read_ready) == 0:
227227
continue
228228

229-
bytes_avail = 0
230-
if hasattr(self._device, "in_waiting"):
231-
bytes_avail = self._device.in_waiting
232-
else:
233-
bytes_avail = self._device.inWaiting()
234-
235-
buf = self._device.read(bytes_avail)
236-
237-
for idx in range(len(buf)):
238-
c = buf[idx]
229+
buf = filter_ad2prot_byte(self._device.read(1))
239230

240-
ub = bytes_hack(c)
241-
if sys.version_info > (3,):
242-
ub = bytes([ub])
231+
if buf != b'':
232+
self._buffer += buf
243233

244-
# NOTE: AD2SERIAL and AD2PI apparently sends down \xFF on boot.
245-
if ub != b'' and ub != b"\xff":
246-
self._buffer += ub
247-
248-
if ub == b"\n":
249-
self._buffer = self._buffer.strip(b"\r\n")
250-
251-
if len(self._buffer) > 0:
252-
got_line = True
253-
leftovers = buf[idx:]
254-
break
234+
if buf == b"\n":
235+
self._buffer = self._buffer.rstrip(b"\r\n")
255236

237+
if len(self._buffer) > 0:
238+
got_line = True
239+
break
256240
except (OSError, serial.SerialException) as err:
257241
raise CommError('Error reading from device: {0}'.format(str(err)), err)
258242

259243
else:
260244
if got_line:
261-
data, self._buffer = self._buffer, leftovers
245+
ret, self._buffer = self._buffer, b''
262246

263-
self.on_read(data=data)
247+
self.on_read(data=ret)
264248

265249
else:
266250
raise TimeoutError('Timeout while waiting for line terminator.')
267251

268252
finally:
269253
timer.cancel()
270254

271-
return data.decode('utf-8')
255+
return ret.decode('utf-8')
272256

273257
def purge(self):
274258
"""

alarmdecoder/util.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,22 @@ def bytes_hack(buf):
9393

9494
return ub
9595

96+
def filter_ad2prot_byte(buf):
97+
"""
98+
Return the byte sent in back if valid visible terminal characters or line terminators.
99+
"""
100+
if sys.version_info > (3,):
101+
c = buf[0]
102+
else:
103+
c = ord(buf)
104+
105+
if (c == 10 or c == 13):
106+
return buf
107+
if (c > 31 and c < 127):
108+
return buf
109+
else:
110+
return b''
111+
96112
def read_firmware_file(file_path):
97113
"""
98114
Reads a firmware file into a dequeue for processing.

alarmdecoder/zonetracking.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -151,9 +151,7 @@ def update(self, message):
151151
status = Zone.CHECK
152152

153153
# NOTE: Expander zone faults are handled differently than
154-
# regular messages. We don't include them in
155-
# self._zones_faulted because they are not reported
156-
# by the panel in it's rolling list of faults.
154+
# regular messages.
157155
try:
158156
self._update_zone(zone, status=status)
159157

@@ -198,6 +196,9 @@ def update(self, message):
198196
self._update_zone(zone)
199197
self._clear_zones(zone)
200198

199+
# Save our spot for the next message.
200+
self._last_zone_fault = zone
201+
201202
else:
202203
status = Zone.FAULT
203204
if message.check_zone:
@@ -207,8 +208,8 @@ def update(self, message):
207208
self._zones_faulted.append(zone)
208209
self._zones_faulted.sort()
209210

210-
# Save our spot for the next message.
211-
self._last_zone_fault = zone
211+
# A new zone fault, so it is out of sequence.
212+
self._last_zone_fault = 0
212213

213214
self._clear_expired_zones()
214215

@@ -245,6 +246,11 @@ def _clear_zones(self, zone):
245246
:param zone: current zone being processed
246247
:type zone: int
247248
"""
249+
250+
if self._last_zone_fault == 0:
251+
# We don't know what the last faulted zone was, nothing to do
252+
return
253+
248254
cleared_zones = []
249255
found_last_faulted = found_current = at_end = False
250256

@@ -296,7 +302,9 @@ def _clear_zones(self, zone):
296302

297303
# Actually remove the zones and trigger the restores.
298304
for z in cleared_zones:
299-
self._update_zone(z, Zone.CLEAR)
305+
# Don't clear expander zones, expander messages will fix this
306+
if self._zones[z].expander is False:
307+
self._update_zone(z, Zone.CLEAR)
300308

301309
def _clear_expired_zones(self):
302310
"""

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def readme():
1414
extra_requirements.append('future>=0.14.3')
1515

1616
setup(name='alarmdecoder',
17-
version='1.13.9',
17+
version='1.13.10',
1818
description='Python interface for the AlarmDecoder (AD2) family '
1919
'of alarm devices which includes the AD2USB, AD2SERIAL and AD2PI.',
2020
long_description=readme(),

test/test_ad2.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,5 +362,7 @@ def test_zone_fault_and_restore(self):
362362
self._decoder._on_read(self, data=b'[00010001000000000A--],005,[f70000051003000008020000000000],"FAULT 05 "')
363363
self.assertEquals(self._zone_faulted, 5)
364364

365+
self._decoder._on_read(self, data=b'[00010001000000000A--],004,[f70000051003000008020000000000],"FAULT 04 "')
366+
self._decoder._on_read(self, data=b'[00010001000000000A--],004,[f70000051003000008020000000000],"FAULT 05 "')
365367
self._decoder._on_read(self, data=b'[00010001000000000A--],004,[f70000051003000008020000000000],"FAULT 04 "')
366368
self.assertEquals(self._zone_restored, 3)

test/test_devices.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,11 @@ def test_write_exception(self):
8080
def test_read(self):
8181
self._device.interface = '/dev/ttyS0'
8282
self._device.open(no_reader_thread=True)
83+
side_effect = ["t"]
84+
if sys.version_info > (3,):
85+
side_effect = ["t".encode('utf-8')]
8386

84-
with patch.object(self._device._device, 'read') as mock:
87+
with patch.object(self._device._device, 'read', side_effect=side_effect) as mock:
8588
with patch('serial.Serial.fileno', return_value=1):
8689
with patch.object(select, 'select', return_value=[[1], [], []]):
8790
ret = self._device.read()

0 commit comments

Comments
 (0)