-
-
Notifications
You must be signed in to change notification settings - Fork 37.4k
zha: additional sensors #12515
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
zha: additional sensors #12515
Changes from all commits
c8a6d38
ff5d39c
4205be9
5b52890
b7bb9d1
3b1d26f
34d267e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,20 +25,33 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): | |
| return | ||
|
|
||
| sensor = yield from make_sensor(discovery_info) | ||
| async_add_devices([sensor]) | ||
| async_add_devices([sensor], update_before_add=True) | ||
|
|
||
|
|
||
| @asyncio.coroutine | ||
| def make_sensor(discovery_info): | ||
| """Create ZHA sensors factory.""" | ||
| from zigpy.zcl.clusters.measurement import ( | ||
| RelativeHumidity, TemperatureMeasurement | ||
| RelativeHumidity, TemperatureMeasurement, IlluminanceMeasurement | ||
| ) | ||
| from zigpy.zcl.clusters.general import PowerConfiguration | ||
| from zigpy.zcl.clusters.smartenergy import Metering | ||
| from zigpy.zcl.clusters.homeautomation import ElectricalMeasurement | ||
| in_clusters = discovery_info['in_clusters'] | ||
| if RelativeHumidity.cluster_id in in_clusters: | ||
| sensor = RelativeHumiditySensor(**discovery_info) | ||
| elif TemperatureMeasurement.cluster_id in in_clusters: | ||
| sensor = TemperatureSensor(**discovery_info) | ||
| elif PowerConfiguration.cluster_id in in_clusters \ | ||
| and discovery_info['manufacturer'] == 'CentraLite': | ||
| sensor = CentraliteBatterySensor(**discovery_info) | ||
| elif Metering.cluster_id in in_clusters: | ||
| sensor = MeteringSensor(**discovery_info) | ||
| elif IlluminanceMeasurement.cluster_id in in_clusters: | ||
| sensor = IlluminanceMeasurementSensor(**discovery_info) | ||
| elif ElectricalMeasurement.cluster_id in in_clusters: | ||
| sensor = ElectricalMeasurementSensor(**discovery_info) | ||
| return sensor | ||
| else: | ||
| sensor = Sensor(**discovery_info) | ||
|
|
||
|
|
@@ -66,6 +79,14 @@ def state(self) -> str: | |
| return str(round(self._state, 2)) | ||
| return self._state | ||
|
|
||
| @property | ||
| def should_poll(self) -> bool: | ||
| """Return True if entity has to be polled for state. | ||
|
|
||
| False if entity pushes its state to HA. | ||
| """ | ||
| return False | ||
|
|
||
| def attribute_updated(self, attribute, value): | ||
| """Handle attribute update from device.""" | ||
| _LOGGER.debug("Attribute updated: %s %s %s", self, attribute, value) | ||
|
|
@@ -111,3 +132,155 @@ def state(self): | |
| return 'unknown' | ||
|
|
||
| return round(float(self._state) / 100, 1) | ||
|
|
||
|
|
||
| class GenericBatterySensor(Sensor): | ||
| """ZHA generic battery sensor.""" | ||
|
|
||
| value_attribute = 32 | ||
| battery_sizes = { | ||
| 0: 'No battery', | ||
| 1: 'Built in', | ||
| 2: 'Other', | ||
| 3: 'AA', | ||
| 4: 'AAA', | ||
| 5: 'C', | ||
| 6: 'D', | ||
| 7: 'CR2', | ||
| 8: 'CR123A', | ||
| 255: 'Unknown' | ||
| } | ||
|
|
||
| @property | ||
| def unit_of_measurement(self): | ||
| """Return the unit of measurement of this entity.""" | ||
| return '%' | ||
|
|
||
| @asyncio.coroutine | ||
| def async_update(self): | ||
| """Retrieve latest state.""" | ||
| _LOGGER.debug("%s async_update", self.entity_id) | ||
|
|
||
| result = yield from zha.safe_read( | ||
| self._endpoint.power, | ||
| ['battery_size', 'battery_quantity', 'battery_voltage'] | ||
| ) | ||
| self._device_state_attributes['battery_size'] = \ | ||
| self.battery_sizes.get( | ||
| result.get('battery_size', 255), | ||
| 'Unknown' | ||
| ) | ||
| self._device_state_attributes['battery_quantity'] = result.get( | ||
| 'battery_quantity', 'Unknown') | ||
| self._state = result.get('battery_voltage', self._state) | ||
|
|
||
|
|
||
| class CentraliteBatterySensor(GenericBatterySensor): | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this should live in Home Assistant.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "I'd be OKish with this going in and changing to use the normal BatterySensor when zigpy quirks handling is ready, though." Will leave here until quirks mode is ready. |
||
| """ZHA battery sensor.""" | ||
|
|
||
| # currently restricted to centralite sensors because the value | ||
| # conversion is specific to centralite sensors. | ||
|
|
||
| minVolts = 15 | ||
| maxVolts = 28 | ||
| values = { | ||
| 28: 100, | ||
| 27: 100, | ||
| 26: 100, | ||
| 25: 90, | ||
| 24: 90, | ||
| 23: 70, | ||
| 22: 70, | ||
| 21: 50, | ||
| 20: 50, | ||
| 19: 30, | ||
| 18: 30, | ||
| 17: 15, | ||
| 16: 1, | ||
| 15: 0 | ||
| } | ||
|
|
||
| @property | ||
| def state(self): | ||
| """Return the state of the entity.""" | ||
| if self._state == 'unknown': | ||
| return 'unknown' | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Return There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why are multiple input values mapped to the same output percentage?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ryanwinter please move your comment to a general PR comment. I don't see that it's related to my comment or this code line. |
||
|
|
||
| if self._state < self.minVolts: | ||
| self._state = self.minVolts | ||
| elif self._state > self.maxVolts: | ||
| self._state = self.maxVolts | ||
|
|
||
| return self.values.get(self._state, 'unknown') | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See above. |
||
|
|
||
|
|
||
| class MeteringSensor(Sensor): | ||
| """ZHA Metering sensor.""" | ||
|
|
||
| value_attribute = 1024 | ||
|
|
||
| @property | ||
| def unit_of_measurement(self): | ||
| """Return the unit of measurement of this entity.""" | ||
| return 'W' | ||
|
|
||
| @property | ||
| def state(self): | ||
| """Return the state of the entity.""" | ||
| if self._state == 'unknown': | ||
| return 'unknown' | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See above. |
||
|
|
||
| return self._state | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no newline at end of file |
||
|
|
||
|
|
||
| class ElectricalMeasurementSensor(Sensor): | ||
| """ZHA Electrical Measurement sensor.""" | ||
|
|
||
| value_attribute = 1291 | ||
|
|
||
| @property | ||
| def unit_of_measurement(self): | ||
| """Return the unit of measurement of this entity.""" | ||
| return 'W' | ||
|
|
||
| @property | ||
| def state(self): | ||
| """Return the state of the entity.""" | ||
| if self._state == 'unknown': | ||
| return 'unknown' | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See above. |
||
|
|
||
| return round(float(self._state) / 10, 1) | ||
|
|
||
| @property | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I assume this kind of device is normally wired, and so polling it is OK? Have you tried configuring reporting on your device? That'd be a better option.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, they're wired devices and the electrical measurement cluster attributes don't support reporting according to the ZCL docs. |
||
| def should_poll(self) -> bool: | ||
| """Return True if entity has to be polled for state. | ||
|
|
||
| False if entity pushes its state to HA. | ||
| """ | ||
| return True | ||
|
|
||
| @asyncio.coroutine | ||
| def async_update(self): | ||
| """Retrieve latest state.""" | ||
| _LOGGER.debug("%s async_update", self.entity_id) | ||
|
|
||
| result = yield from zha.safe_read( | ||
| self._endpoint.electrical_measurement, ['active_power']) | ||
| self._state = result.get('active_power', self._state) | ||
|
|
||
|
|
||
| class IlluminanceMeasurementSensor(Sensor): | ||
| """ZHA lux sensor.""" | ||
|
|
||
| @property | ||
| def unit_of_measurement(self): | ||
| """Return the unit of measurement of this entity.""" | ||
| return 'lux' | ||
|
|
||
| @property | ||
| def state(self): | ||
| """Return the state of the entity.""" | ||
| if self._state == 'unknown': | ||
| return 'unknown' | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Return |
||
|
|
||
| return self._state | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should go into zigpy as an enum, as it is part of the standard.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll submit a PR to zigpy for this