Fire events for hue remote buttons pressed (#33277)
* Add remote platform to hue integration supporting ZGPSwitch, ZLLSwitch and ZLLRotary switches. * Ported from custom component Hue-remotes-HASS from @robmarkcole * Add options flow for hue, to toggle handling of sensors and remotes * Sensors are enabled by default, and remotes are disabled, to not generate any breaking change for existent users. Also, when linking a new bridge these defaults are used, so unless going explicitly to the Options menu, the old behavior is preserved. * SensorManager stores the enabled platforms and ignores everything else. * Bridge is created with flags for `add_sensors` and `add_remotes`, and uses them to forward entry setup to only the enabled platforms. * Update listener removes disabled kinds of devices when options are changed, so device list is in sync with options, and disabled kinds disappear from HA, leaving the enable/disable entity option for individual devices. * Fix hue bridge mock with new parameters * Revert changes in hue bridge mock * Remove OptionsFlow and platform flags * Extract `GenericHueDevice` from `GenericHueSensor` to use it as base class for all hue devices, including those without any entity, like remotes without battery. * Add `HueBattery` sensor for battery powered remotes and generate entities for TYPE_ZLL_ROTARY and TYPE_ZLL_SWITCH remotes. * Remove remote platform * Add HueEvent class to fire events for button presses * Use `sensor.lastupdated` string to control state changes * Event data includes: - "id", as pretty name of the remote - "unique_id" of the remote device - "event", with the raw code of the pressed button ('buttonevent' or 'rotaryevent' property) - "last_updated", with the bridge timestamp for the button press * Register ZGP_SWITCH, ZLL_SWITCH, ZLL_ROTARY remotes * fix removal * Exclude W0611 * Extract GenericHueDevice to its own module and solve import tree, also fixing lint in CI * Store registered events to do not repeat device reg * Minor cleaning * Add tests for hue_event and battery entities for hue remotes
This commit is contained in:
parent
dd1608db0d
commit
f5cbc9d208
5 changed files with 385 additions and 73 deletions
|
@ -10,8 +10,10 @@ from homeassistant.core import callback
|
|||
from homeassistant.helpers import debounce, entity
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
|
||||
from .const import DOMAIN as HUE_DOMAIN, REQUEST_REFRESH_DELAY
|
||||
from .const import REQUEST_REFRESH_DELAY
|
||||
from .helpers import remove_devices
|
||||
from .hue_event import EVENT_CONFIG_MAP
|
||||
from .sensor_device import GenericHueDevice
|
||||
|
||||
SENSOR_CONFIG_MAP = {}
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
@ -38,6 +40,9 @@ class SensorManager:
|
|||
self.bridge = bridge
|
||||
self._component_add_entities = {}
|
||||
self.current = {}
|
||||
self.current_events = {}
|
||||
|
||||
self._enabled_platforms = ("binary_sensor", "sensor")
|
||||
self.coordinator = DataUpdateCoordinator(
|
||||
bridge.hass,
|
||||
_LOGGER,
|
||||
|
@ -66,7 +71,8 @@ class SensorManager:
|
|||
"""Register async_add_entities methods for components."""
|
||||
self._component_add_entities[platform] = async_add_entities
|
||||
|
||||
if len(self._component_add_entities) < 2:
|
||||
if len(self._component_add_entities) < len(self._enabled_platforms):
|
||||
_LOGGER.debug("Aborting start with %s, waiting for the rest", platform)
|
||||
return
|
||||
|
||||
# We have all components available, start the updating.
|
||||
|
@ -81,7 +87,7 @@ class SensorManager:
|
|||
"""Update sensors from the bridge."""
|
||||
api = self.bridge.api.sensors
|
||||
|
||||
if len(self._component_add_entities) < 2:
|
||||
if len(self._component_add_entities) < len(self._enabled_platforms):
|
||||
return
|
||||
|
||||
to_add = {}
|
||||
|
@ -110,12 +116,24 @@ class SensorManager:
|
|||
# Iterate again now we have all the presence sensors, and add the
|
||||
# related sensors with nice names where appropriate.
|
||||
for item_id in api:
|
||||
existing = current.get(api[item_id].uniqueid)
|
||||
if existing is not None:
|
||||
uniqueid = api[item_id].uniqueid
|
||||
if current.get(uniqueid, self.current_events.get(uniqueid)) is not None:
|
||||
continue
|
||||
|
||||
primary_sensor = None
|
||||
sensor_config = SENSOR_CONFIG_MAP.get(api[item_id].type)
|
||||
sensor_type = api[item_id].type
|
||||
|
||||
# Check for event generator devices
|
||||
event_config = EVENT_CONFIG_MAP.get(sensor_type)
|
||||
if event_config is not None:
|
||||
base_name = api[item_id].name
|
||||
name = event_config["name_format"].format(base_name)
|
||||
new_event = event_config["class"](api[item_id], name, self.bridge)
|
||||
self.bridge.hass.async_create_task(
|
||||
new_event.async_update_device_registry()
|
||||
)
|
||||
self.current_events[uniqueid] = new_event
|
||||
|
||||
sensor_config = SENSOR_CONFIG_MAP.get(sensor_type)
|
||||
if sensor_config is None:
|
||||
continue
|
||||
|
||||
|
@ -125,13 +143,11 @@ class SensorManager:
|
|||
base_name = primary_sensor.name
|
||||
name = sensor_config["name_format"].format(base_name)
|
||||
|
||||
current[api[item_id].uniqueid] = sensor_config["class"](
|
||||
current[uniqueid] = sensor_config["class"](
|
||||
api[item_id], name, self.bridge, primary_sensor=primary_sensor
|
||||
)
|
||||
|
||||
to_add.setdefault(sensor_config["platform"], []).append(
|
||||
current[api[item_id].uniqueid]
|
||||
)
|
||||
to_add.setdefault(sensor_config["platform"], []).append(current[uniqueid])
|
||||
|
||||
self.bridge.hass.async_create_task(
|
||||
remove_devices(
|
||||
|
@ -143,53 +159,23 @@ class SensorManager:
|
|||
self._component_add_entities[platform](to_add[platform])
|
||||
|
||||
|
||||
class GenericHueSensor(entity.Entity):
|
||||
class GenericHueSensor(GenericHueDevice, entity.Entity):
|
||||
"""Representation of a Hue sensor."""
|
||||
|
||||
should_poll = False
|
||||
|
||||
def __init__(self, sensor, name, bridge, primary_sensor=None):
|
||||
"""Initialize the sensor."""
|
||||
self.sensor = sensor
|
||||
self._name = name
|
||||
self._primary_sensor = primary_sensor
|
||||
self.bridge = bridge
|
||||
|
||||
async def _async_update_ha_state(self, *args, **kwargs):
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def primary_sensor(self):
|
||||
"""Return the primary sensor entity of the physical device."""
|
||||
return self._primary_sensor or self.sensor
|
||||
|
||||
@property
|
||||
def device_id(self):
|
||||
"""Return the ID of the physical device this sensor is part of."""
|
||||
return self.unique_id[:23]
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Return the ID of this Hue sensor."""
|
||||
return self.sensor.uniqueid
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return a friendly name for the sensor."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def available(self):
|
||||
"""Return if sensor is available."""
|
||||
return self.bridge.sensor_manager.coordinator.last_update_success and (
|
||||
self.bridge.allow_unreachable or self.sensor.config["reachable"]
|
||||
self.bridge.allow_unreachable
|
||||
# remotes like Hue Tap (ZGPSwitchSensor) have no _reachability_
|
||||
or self.sensor.config.get("reachable", True)
|
||||
)
|
||||
|
||||
@property
|
||||
def swupdatestate(self):
|
||||
"""Return detail of available software updates for this device."""
|
||||
return self.primary_sensor.raw.get("swupdate", {}).get("state")
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""When entity is added to hass."""
|
||||
self.bridge.sensor_manager.coordinator.async_add_listener(
|
||||
|
@ -209,21 +195,6 @@ class GenericHueSensor(entity.Entity):
|
|||
"""
|
||||
await self.bridge.sensor_manager.coordinator.async_request_refresh()
|
||||
|
||||
@property
|
||||
def device_info(self):
|
||||
"""Return the device info.
|
||||
|
||||
Links individual entities together in the hass device registry.
|
||||
"""
|
||||
return {
|
||||
"identifiers": {(HUE_DOMAIN, self.device_id)},
|
||||
"name": self.primary_sensor.name,
|
||||
"manufacturer": self.primary_sensor.manufacturername,
|
||||
"model": (self.primary_sensor.productname or self.primary_sensor.modelid),
|
||||
"sw_version": self.primary_sensor.swversion,
|
||||
"via_device": (HUE_DOMAIN, self.bridge.api.config.bridgeid),
|
||||
}
|
||||
|
||||
|
||||
class GenericZLLSensor(GenericHueSensor):
|
||||
"""Representation of a Hue-brand, physical sensor."""
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue