Skip to content

Commit

Permalink
Merge pull request #31 from jaraco/refactor/occupancy-specialization
Browse files Browse the repository at this point in the history
Refactor occupancy specialization
  • Loading branch information
jaraco authored Jul 8, 2024
2 parents abb2c6a + 8502d8f commit 318f302
Show file tree
Hide file tree
Showing 6 changed files with 38 additions and 46 deletions.
4 changes: 2 additions & 2 deletions jaraco/abode/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down
1 change: 0 additions & 1 deletion jaraco/abode/devices/alarm.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down
56 changes: 21 additions & 35 deletions jaraco/abode/devices/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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."""
Expand All @@ -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
21 changes: 14 additions & 7 deletions jaraco/abode/devices/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
1 change: 1 addition & 0 deletions newsfragments/28.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Refactored sensor specialization.
1 change: 0 additions & 1 deletion tests/mock/devices/valve.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit 318f302

Please sign in to comment.