Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Device specialization is confusing and unnecessarily complicated #28

Closed
jaraco opened this issue Jul 7, 2024 · 1 comment
Closed

Device specialization is confusing and unnecessarily complicated #28

jaraco opened this issue Jul 7, 2024 · 1 comment

Comments

@jaraco
Copy link
Owner

jaraco commented Jul 7, 2024

In the handling of Device.new, there are some convoluted behaviors in resolving the specialized subclasses of BinarySensor (namely Sensor, Occupancy, and Motion):

@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."""
pkg.import_all()
try:
type_tag = state['type_tag']
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)
@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):
lookup = {
f'device_type.{tag}': sub_cls.__name__.lower()
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())

In particular, in get_generic_type, the device tags are explicitly managed rather than being associated with a sub_cls and then indicated as "unknown", even though they are known. Then another routine resolve_type_unknown replaces these falsely-unknown types with real types.

This roundabout approach to resolving types is confusing, error-prone, and counter-intuitive, contributing to home-assistant/core#121300 and delaying its resolution.

Let's find a better way.

jaraco added a commit that referenced this issue Jul 7, 2024
Instead of relying on a confusingly-named 'unknown' sentinel, moved these tags to the `Motion` type and gave that type a `.specialize` method to handle the state-sensitive specialization.

Now a truly "unknown" device will resolve to `Unknown` and will be filtered when loading devices.

Closes #28
@jaraco
Copy link
Owner Author

jaraco commented Jul 8, 2024

Attempting to understand the difference between "Motion" and "Occupancy" sensors as coded in:

state['generic_type'] = (
'occupancy' if version.startswith('MINIPIR') else 'motion'
)

I did some searching and found MisterWil/abodepy#10 (comment), which seems to indicate that the origin of these two classes is javascript (presumably from the Abode web UI) that alters the labels based on the reported version.

Moreover, there seems to be no examples of a device that returns a version prefixed with "MINIPIR", so this code path may never be activated.

jaraco added a commit that referenced this issue Jul 8, 2024
It seems to provide little value over the Motion class. Ref #28
@jaraco jaraco closed this as completed in 81c8695 Jul 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant