diff --git a/jaraco/abode/client.py b/jaraco/abode/client.py index b94b30a..71a250c 100644 --- a/jaraco/abode/client.py +++ b/jaraco/abode/client.py @@ -19,7 +19,7 @@ from . import config, settings from .automation import Automation from .devices import alarm as ALARM -from .devices.base import Device +from .devices.base import Device, Unknown from .event_controller import EventController from .exceptions import AuthenticationException from .helpers import errors as ERROR @@ -212,7 +212,7 @@ def _reuse_device(self, doc): def _create_new_device(self, doc): device = Device.new(doc, self) - if not device: + if isinstance(device, Unknown): log.debug("Skipping unknown device: %s", doc) return diff --git a/jaraco/abode/devices/alarm.py b/jaraco/abode/devices/alarm.py index 13ce349..f351f40 100644 --- a/jaraco/abode/devices/alarm.py +++ b/jaraco/abode/devices/alarm.py @@ -23,7 +23,6 @@ def state_from_panel(panel_state, area='1'): alarm_state['id'] = id(area) alarm_state['type'] = 'Alarm' alarm_state['type_tag'] = 'device_type.alarm' - alarm_state['generic_type'] = 'alarm' return alarm_state diff --git a/jaraco/abode/devices/base.py b/jaraco/abode/devices/base.py index 73d9abc..37c8e00 100644 --- a/jaraco/abode/devices/base.py +++ b/jaraco/abode/devices/base.py @@ -17,6 +17,11 @@ class Device(Stateful): """Class to represent each Abode device.""" tags: Tuple[str, ...] = () + """ + Each device has a ``type_tag``, something like "device_type.door_lock". + Each Device subclass declares the tags that it services (with the + "device_type." prefix omitted). + """ _desc_t = '{name} (ID: {id}, UUID: {uuid}) - {type} - {status}' _url_t = urls.DEVICE @@ -124,23 +129,6 @@ def device_uuid(self): ) return self.uuid - @staticmethod - def resolve_type_unknown(state): - if state['generic_type'] != 'unknown': - return - - from .sensor import Sensor - - if Sensor.is_sensor(state): - state['generic_type'] = 'sensor' - return - - version = state.get('version', '') - - state['generic_type'] = ( - 'occupancy' if version.startswith('MINIPIR') else 'motion' - ) - @classmethod def new(cls, state, client): """Create new device object for the given type.""" @@ -150,31 +138,29 @@ def new(cls, state, client): except KeyError as exc: raise jaraco.abode.Exception(ERROR.UNABLE_TO_MAP_DEVICE) from exc - state['generic_type'] = cls.get_generic_type(type_tag) - cls.resolve_type_unknown(state) - sensor = cls.by_type().get(state['generic_type'], lambda *args: None) - return sensor(state, client) + return cls.resolve_class(type_tag).specialize(state)(state, client) + + @property + def generic_type(self): + return self.__class__.__name__.lower() @classmethod def by_type(cls): return {sub_cls.__name__.lower(): sub_cls for sub_cls in iter_subclasses(cls)} @classmethod - def get_generic_type(cls, type_tag): + def resolve_class(cls, type_tag): lookup = { - f'device_type.{tag}': sub_cls.__name__.lower() + f'device_type.{tag}': sub_cls for sub_cls in iter_subclasses(cls) for tag in sub_cls.tags } - # These device types are all considered 'occupancy' but could apparently - # also be multi-sensors based on their state. - unknowns = ( - 'room_sensor', - 'temperature_sensor', - # LM = LIGHT MOTION? - 'lm', - 'pir', - 'povs', - ) - lookup.update((f'device_type.{unknown}', 'unknown') for unknown in unknowns) - return lookup.get(type_tag.lower()) + return lookup.get(type_tag.lower(), Unknown) + + @classmethod + def specialize(cls, state): + return cls + + +class Unknown(Device): + pass diff --git a/jaraco/abode/devices/binary_sensor.py b/jaraco/abode/devices/binary_sensor.py index 39e046d..17eebea 100644 --- a/jaraco/abode/devices/binary_sensor.py +++ b/jaraco/abode/devices/binary_sensor.py @@ -36,16 +36,23 @@ class Connectivity(BinarySensor): ) -class Moisture(BinarySensor): - pass - - class Motion(BinarySensor): - pass + # These device types are all considered 'occupancy' but could apparently + # also be multi-sensors based on their state. + tags = ( + 'room_sensor', + 'temperature_sensor', + # LM = LIGHT MOTION? + 'lm', + 'pir', + 'povs', + ) + @classmethod + def specialize(cls, state): + from . import sensor -class Occupancy(BinarySensor): - pass + return sensor.Sensor if sensor.Sensor.is_sensor(state) else cls class Door(BinarySensor): diff --git a/newsfragments/28.feature.rst b/newsfragments/28.feature.rst new file mode 100644 index 0000000..7242351 --- /dev/null +++ b/newsfragments/28.feature.rst @@ -0,0 +1 @@ +Refactored sensor specialization. \ No newline at end of file diff --git a/tests/mock/devices/valve.py b/tests/mock/devices/valve.py index 34ab298..f3cfb76 100644 --- a/tests/mock/devices/valve.py +++ b/tests/mock/devices/valve.py @@ -25,7 +25,6 @@ def device(devid=DEVICE_ID, status=STATUS.OPEN, low_battery=False, no_response=F 'out_of_order': 0, 'no_response': int(no_response), }, - generic_type='valve', group_id='xxxxx', group_name='Water leak', has_subscription=None,