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

Further sensor API support #4

Merged
merged 12 commits into from
Jun 15, 2018
Merged

Further sensor API support #4

merged 12 commits into from
Jun 15, 2018

Conversation

marthoc
Copy link
Contributor

@marthoc marthoc commented Apr 12, 2018

Hey @balloob: following our discussion at the end of my last PR, I've put some effort into sketching out how I think the Hue sensor API could be further integrated into aiohue.

This initial commit is just a sample so you can take a look at my thinking and give your comments/direction before I go any further.

I've sketched out additional classes (that subclass the GenericSensor), for the HueTapSwitch, the HueDimmerSwitch, a HueGenericSwitch, a HueTemperatureSensor, and a HueDaylightSensor, so you can see my thinking on how each type of sensor (i.e. switch, sensor, binary sensor) could be handled. You'll see that in each specific class I override GenericSensor's state property method to return the main state property of that class of sensor, be it buttonevent, temperature, daylight, etc. I'm thinking this would made the downstream integration simpler.

In the temperature sensor, you'll see an example of how we could handle the conversion necessary to return useful data (e.g. convert the value the Hue API returns into Celsius, or for light level, we would convert the value the Hue API returns into lux) and also provide the unit of measure for downstream use in HA.

We could go further and also return e.g. the mdi icon appropriate for that sensor, but I'm thinking that's on the dividing line between what should be in this library and what HA should handle - interested in your thoughts.

You'll also see the factory method I started sketching out that now gets passed to Sensors' constructor instead of the old Sensor class.

I've added constants for the types of sensor supported by the Hue bridge (including CLIP sensors and the category they fall into based on what their main state property is, e.g. buttonevent, lightlevel, etc), and also constants for the models of Hue sensor where that is/may be relevant to their further treatment in the factory method (specifically for the Hue Tap and Hue Dimmer).

On a side note, my sincere congratulations on the Ubiquiti Networks news! Good luck!

@raymondelooff
Copy link
Contributor

Sensors would be a great addition, but I think there are some problems with the implementation you are proposing.

First, subclasses are a great idea and will help to make it easy to implement the sensors. However, overriding the 'state' attribute seems like a bad idea to me, because some sensors have more than one relevant 'state' attribute. I think the state attribute shouldn't be changed and should just return the raw state values of the Hue API, so all state values are always available. The idea of adding more properties to specific sensor classes to parse 'raw' values is great and will make the implementation of the sensors easier.

Second, you can't make generic classes for both CLIP and ZLL sensors, because the available config options and states are different. For example, instead of a generic 'HuePresenceSensor' you'll need 'ZLLPresenceSensor' and 'CLIPPresenceSensor' classes. The ZLL Presence sensors have config options for sensitivity, whereas the CLIP Presence sensors don't have those options.

I've created a pull request (marthoc/aiohue#1) that changes the existing classes, adds support for presence and light level sensors, and implements 'set_config' methods. Some sensor types are not implemented yet.

@marthoc
Copy link
Contributor Author

marthoc commented Apr 15, 2018

Thanks @raymondelooff for this. I’ll let @balloob weigh in with his thoughts before I do anything with your PR.

I’m not sure I agree with you re the state property - the idea would be that any relevant state items for each sensor class get exposed as property methods themselves, so they will in fact also always be available. Same goes for config variables like battery and reachable.

I’ll have to think more about the differences between ZLL and CLIP sensors and how they should be treated in the API before offering a response to your other point.

@balloob
Copy link
Collaborator

balloob commented Apr 16, 2018

One design rule for aiohue is that we should have properties named after the values in the raw data which should provide the data as-is. If we are going to interpret the data, we should name the properties differently.

That way, people will be able to understand most of aiohue by looking at the Hue docs.

@marthoc
Copy link
Contributor Author

marthoc commented Apr 16, 2018

OK so @raymondelooff you are right re "state", that should stay unchanged. If we expose additional properties that mirror the state and config values in name, we can do the conversions there.

@raymondelooff I will merge your PR and we can go forward from there. Thanks!

@marthoc
Copy link
Contributor Author

marthoc commented Apr 16, 2018

@raymondelooff - should be expose all values from state as properties on the subclasses? I think if we are doing it for e.g. buttonevent, we should do it for lastupdated too (for example on the ZLL switch).

@raymondelooff
Copy link
Contributor

Thanks for merging!

Some general properties like lastupdated are inherited from the GenericZLLSensor, GenericZGPSensor or GenericCLIPSensor class, depending on the type of sensor. Some may be missing though, I have not checked that very carefully yet.

I'm not completely sure if it would help to add value formatting in this library. Adding extra methods that almost have the same name as the property could make it very confusing. Maybe it's better to do no conversion in this library, and move all the conversion to Home Assistant. Currently, the conversion of the hue and saturation of the lights is also done by Home Assistant before it's passed to the API via this library.

@balloob
Copy link
Collaborator

balloob commented Apr 17, 2018

Yeah, let's not do conversion in this lib. We should keep this lib as simple as possible to empower people to do as much as possible.

@marthoc marthoc changed the title WIP: Further sensor API support Further sensor API support May 9, 2018
@marthoc
Copy link
Contributor Author

marthoc commented May 9, 2018

@balloob @raymondelooff I've just had a chance to turn my mind back to this PR and have pushed another commit that removes the conversions and units of measure (uom's are not needed if we are just returning the raw, unconverted value, as was previously discussed).

@balloob I've left the "conversions" in for the ZGP and ZLL switch classes (from button event numbers to a human-readable name) but if we are taking out conversions for the rest of the classes then these classes should probably just return the raw value returned by the API too. I'm open to your comments!

class GenericCLIPSensor(GenericSensor):
def __init__(self, id, raw, request):
super().__init__(id, raw, request)
self._on = raw['config'].get('on')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we store raw as instance variable, we should have properties just fetch it from raw. That way we keep a single source of truth.

class DaylightSensor(GenericSensor):
def __init__(self, id, raw, request):
super().__init__(id, raw, request)
self._daylight = raw['state'].get('daylight')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do 'get' on everything. We should only do 'get' if the value might not be there.

@property
def buttonevent(self):
if self._buttonevent == 34:
return 'BUTTON_1'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not convert and then we should expose constants ZGP_SWITCH_BUTTON_1 etc.

That way we can do:

from aiohue.sensors import ZGP_SWITCH_BUTTON_1

if sensor.buttonevent == ZGP_SWITCH_BUTTON_1:
  …

class ZLLSwitchSensor(GenericZLLSensor):
def __init__(self, id, raw, request):
super().__init__(id, raw, request)
self._buttonevent = raw['state'].get('buttonevent')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't cache things. we should just fetch from raw directly. Single source of truth.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm right in the middle of a bunch of updates to the sensors framework based on your earlier feedback. All properties will now be retrieved directly from the raw instance variable and not cached.


@property
def buttonevent(self):
if self._buttonevent == ZLL_SWITCH_BUTTON_1_CLICK:
Copy link
Collaborator

@balloob balloob Jun 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole if/else is no longer needed right? We can just return self.raw.get('buttonevent') ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have to admit I was not thinking clearly when I redid this :) . You are right, this should just return self.raw['state']['buttonevent']. Will fix.

Also, reorganize ZGP Switch into a single class (as there is only one ZGP Switch, it doesn't currently make sense to have a Generic ZGP class and then a ZGP Switch class as we can't really be sure what the base ZGP properties would be).
Provides examples of the properties exposed for each sensor type.
@marthoc
Copy link
Contributor Author

marthoc commented Jun 14, 2018

OK @balloob - now you could review this. Here's what I've done in a nutshell:

  1. Added button constants for all possible values returnable by the Hue API for the ZGP and ZLL switches, and named the constants properly according to the Hue API specification.
  2. Properties now fetch values directly from the raw instance variable in the base sensor class; where the value definitely exists, this is done by returning raw['state']['variable'] rather than using get; where the value only possibly exists (the CLIP sensors don't require many properties to be defined), get is still used.
  3. Added a few missing CLIP sensor type classes.
  4. Reorganized the ZGP switch class to be a single class that subclasses the base sensor class rather than having an intermediate GenericZGPSensor class as @raymondelooff created. There's only one ZGP device at the moment supported on the Hue bridge (the Hue Tap), so we really only know how that one is defined in the API.
  5. Added some missing properties to the various sensor classes (went through the API spec type by type to make sure we were including everything).
  6. Expanded the sensor section of example.py to demonstrate the different properties present for each sensor class (this could be expanded even further but as it's just an example I didn't think it necessary).

To test I created one of each CLIP sensor type on my Hue bridge and ran example.py against it. Seems to work well!

@balloob balloob merged commit d0343fd into home-assistant-libs:master Jun 15, 2018
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

Successfully merging this pull request may close these issues.

3 participants