|
|
@@ -6,12 +6,11 @@ Provides the full AD2 class and factory. |
|
|
|
|
|
|
|
import time |
|
|
|
import threading |
|
|
|
import re |
|
|
|
from .event import event |
|
|
|
from . import devices |
|
|
|
from . import util |
|
|
|
from . import messages |
|
|
|
from . import zonetracking |
|
|
|
from .devices import USBDevice |
|
|
|
from .util import CommError, NoDeviceError |
|
|
|
from .messages import Message, ExpanderMessage, RFMessage, LRRMessage |
|
|
|
from .zonetracking import Zonetracker |
|
|
|
|
|
|
|
class Overseer(object): |
|
|
|
""" |
|
|
@@ -30,9 +29,9 @@ class Overseer(object): |
|
|
|
Returns all AD2USB devices located on the system. |
|
|
|
|
|
|
|
:returns: list of devices found |
|
|
|
:raises: util.CommError |
|
|
|
:raises: CommError |
|
|
|
""" |
|
|
|
cls.__devices = devices.USBDevice.find_all() |
|
|
|
cls.__devices = USBDevice.find_all() |
|
|
|
|
|
|
|
return cls.__devices |
|
|
|
|
|
|
@@ -54,18 +53,18 @@ class Overseer(object): |
|
|
|
:type device: tuple |
|
|
|
|
|
|
|
:returns: AD2USB object utilizing the specified device. |
|
|
|
:raises: util.NoDeviceError |
|
|
|
:raises: NoDeviceError |
|
|
|
""" |
|
|
|
cls.find_all() |
|
|
|
|
|
|
|
if len(cls.__devices) == 0: |
|
|
|
raise util.NoDeviceError('No AD2USB devices present.') |
|
|
|
raise NoDeviceError('No AD2USB devices present.') |
|
|
|
|
|
|
|
if device is None: |
|
|
|
device = cls.__devices[0] |
|
|
|
|
|
|
|
vendor, product, sernum, ifcount, description = device |
|
|
|
device = devices.USBDevice((sernum, ifcount - 1)) |
|
|
|
device = USBDevice((sernum, ifcount - 1)) |
|
|
|
|
|
|
|
return AD2(device) |
|
|
|
|
|
|
@@ -163,7 +162,7 @@ class Overseer(object): |
|
|
|
for d in removed_devices: |
|
|
|
self._overseer.on_detached(d) |
|
|
|
|
|
|
|
except util.CommError, err: |
|
|
|
except CommError, err: |
|
|
|
pass |
|
|
|
|
|
|
|
time.sleep(0.25) |
|
|
@@ -220,10 +219,10 @@ class AD2(object): |
|
|
|
Constructor |
|
|
|
|
|
|
|
:param device: The low-level device used for this AD2 interface. |
|
|
|
:type device: devices.Device |
|
|
|
:type device: Device |
|
|
|
""" |
|
|
|
self._device = device |
|
|
|
self._zonetracker = zonetracking.Zonetracker() |
|
|
|
self._zonetracker = Zonetracker() |
|
|
|
|
|
|
|
self._power_status = None |
|
|
|
self._alarm_status = None |
|
|
@@ -271,7 +270,9 @@ class AD2(object): |
|
|
|
""" |
|
|
|
Closes the device. |
|
|
|
""" |
|
|
|
self._device.close() |
|
|
|
if self._device: |
|
|
|
self._device.close() |
|
|
|
|
|
|
|
del self._device |
|
|
|
self._device = None |
|
|
|
|
|
|
@@ -283,7 +284,7 @@ class AD2(object): |
|
|
|
""" |
|
|
|
Retrieves the configuration from the device. |
|
|
|
""" |
|
|
|
self._device.write("C\r") |
|
|
|
self.send("C\r") |
|
|
|
|
|
|
|
def save_config(self): |
|
|
|
""" |
|
|
@@ -312,13 +313,13 @@ class AD2(object): |
|
|
|
|
|
|
|
config_string = '&'.join(['='.join(t) for t in config_entries]) |
|
|
|
|
|
|
|
self._device.write("C{0}\r".format(config_string)) |
|
|
|
self.send("C{0}\r".format(config_string)) |
|
|
|
|
|
|
|
def reboot(self): |
|
|
|
""" |
|
|
|
Reboots the device. |
|
|
|
""" |
|
|
|
self._device.write('=') |
|
|
|
self.send('=') |
|
|
|
|
|
|
|
def fault_zone(self, zone, simulate_wire_problem=False): |
|
|
|
""" |
|
|
@@ -339,7 +340,7 @@ class AD2(object): |
|
|
|
|
|
|
|
status = 2 if simulate_wire_problem else 1 |
|
|
|
|
|
|
|
self._device.write("L{0:02}{1}\r".format(zone, status)) |
|
|
|
self.send("L{0:02}{1}\r".format(zone, status)) |
|
|
|
|
|
|
|
def clear_zone(self, zone): |
|
|
|
""" |
|
|
@@ -348,7 +349,7 @@ class AD2(object): |
|
|
|
:param zone: The zone to clear. |
|
|
|
:type zone: int |
|
|
|
""" |
|
|
|
self._device.write("L{0:02}0\r".format(zone)) |
|
|
|
self.send("L{0:02}0\r".format(zone)) |
|
|
|
|
|
|
|
def _wire_events(self): |
|
|
|
""" |
|
|
@@ -371,19 +372,19 @@ class AD2(object): |
|
|
|
:returns: An object representing the message. |
|
|
|
""" |
|
|
|
if data is None: |
|
|
|
return None |
|
|
|
raise InvalidMessageError() |
|
|
|
|
|
|
|
msg = None |
|
|
|
|
|
|
|
header = data[0:4] |
|
|
|
if header[0] != '!' or header == '!KPE': |
|
|
|
msg = messages.Message(data) |
|
|
|
msg = Message(data) |
|
|
|
|
|
|
|
if self.address_mask & msg.mask > 0: |
|
|
|
self._update_internal_states(msg) |
|
|
|
|
|
|
|
elif header == '!EXP' or header == '!REL': |
|
|
|
msg = messages.ExpanderMessage(data) |
|
|
|
msg = ExpanderMessage(data) |
|
|
|
|
|
|
|
self._update_internal_states(msg) |
|
|
|
|
|
|
@@ -402,7 +403,7 @@ class AD2(object): |
|
|
|
return msg |
|
|
|
|
|
|
|
def _handle_rfx(self, data): |
|
|
|
msg = messages.RFMessage(data) |
|
|
|
msg = RFMessage(data) |
|
|
|
|
|
|
|
self.on_rfx_message(msg) |
|
|
|
|
|
|
@@ -417,7 +418,7 @@ class AD2(object): |
|
|
|
|
|
|
|
:returns: An object representing the LRR message. |
|
|
|
""" |
|
|
|
msg = messages.LRRMessage(data) |
|
|
|
msg = LRRMessage(data) |
|
|
|
|
|
|
|
if msg.event_type == 'ALARM_PANIC': |
|
|
|
self._panic_status = True |
|
|
@@ -469,7 +470,7 @@ class AD2(object): |
|
|
|
:param message: Message to update internal states with. |
|
|
|
:type message: Message, ExpanderMessage, LRRMessage, or RFMessage |
|
|
|
""" |
|
|
|
if isinstance(message, messages.Message): |
|
|
|
if isinstance(message, Message): |
|
|
|
if message.ac_power != self._power_status: |
|
|
|
self._power_status, old_status = message.ac_power, self._power_status |
|
|
|
|
|
|
@@ -511,8 +512,8 @@ class AD2(object): |
|
|
|
self._fire_status = (message.fire_alarm, time.time()) |
|
|
|
self.on_fire(self._fire_status) |
|
|
|
|
|
|
|
elif isinstance(message, messages.ExpanderMessage): |
|
|
|
if message.type == messages.ExpanderMessage.RELAY: |
|
|
|
elif isinstance(message, ExpanderMessage): |
|
|
|
if message.type == ExpanderMessage.RELAY: |
|
|
|
self._relay_status[(message.address, message.channel)] = message.value |
|
|
|
|
|
|
|
self.on_relay_changed(message) |
|
|
@@ -529,9 +530,9 @@ class AD2(object): |
|
|
|
|
|
|
|
# Retrieve a list of faults. |
|
|
|
# NOTE: This only happens on first boot or after exiting programming mode. |
|
|
|
if isinstance(message, messages.Message): |
|
|
|
if isinstance(message, Message): |
|
|
|
if not message.ready and "Hit * for faults" in message.text: |
|
|
|
self._device.write('*') |
|
|
|
self.send('*') |
|
|
|
return |
|
|
|
|
|
|
|
self._zonetracker.update(message) |
|
|
|