mirror of
https://github.com/home-assistant/core.git
synced 2026-08-16 02:02:56 +01:00
215 lines
7.3 KiB
Python
215 lines
7.3 KiB
Python
"""Support for switch platform for Hue resources (V2 only)."""
|
|
|
|
from collections.abc import Callable
|
|
from typing import Any, override
|
|
|
|
from aiohue.v2 import HueBridgeV2
|
|
from aiohue.v2.controllers.config import BehaviorInstance, BehaviorInstanceController
|
|
from aiohue.v2.controllers.events import EventType
|
|
from aiohue.v2.controllers.sensors import (
|
|
LightLevel,
|
|
LightLevelController,
|
|
Motion,
|
|
MotionController,
|
|
)
|
|
from aiohue.v2.models.behavior_instance import PresenceMimickingState
|
|
from aiohue.v2.models.behavior_script import BehaviorScriptCategory
|
|
|
|
from homeassistant.components.switch import (
|
|
SwitchDeviceClass,
|
|
SwitchEntity,
|
|
SwitchEntityDescription,
|
|
)
|
|
from homeassistant.const import EntityCategory, Platform
|
|
from homeassistant.core import HomeAssistant, callback
|
|
from homeassistant.helpers import entity_registry as er
|
|
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
|
|
|
from .bridge import HueConfigEntry
|
|
from .const import DOMAIN
|
|
from .v2.entity import HueBaseEntity
|
|
|
|
|
|
async def async_setup_entry(
|
|
hass: HomeAssistant,
|
|
config_entry: HueConfigEntry,
|
|
async_add_entities: AddConfigEntryEntitiesCallback,
|
|
) -> None:
|
|
"""Set up Hue switch platform from Hue resources."""
|
|
bridge = config_entry.runtime_data
|
|
api: HueBridgeV2 = bridge.api
|
|
|
|
if bridge.api_version == 1:
|
|
# should not happen, but just in case
|
|
raise NotImplementedError("Switch support is only available for V2 bridges")
|
|
|
|
@callback
|
|
def register_items(
|
|
controller: BehaviorInstanceController
|
|
| LightLevelController
|
|
| MotionController,
|
|
switch_class: type[
|
|
HueBehaviorInstanceEnabledEntity
|
|
| HueLightSensorEnabledEntity
|
|
| HueMotionSensorEnabledEntity
|
|
],
|
|
resource_filter: Callable[[Any], bool] | None = None,
|
|
):
|
|
@callback
|
|
def async_add_entity(
|
|
event_type: EventType, resource: BehaviorInstance | LightLevel | Motion
|
|
) -> None:
|
|
"""Add entity from Hue resource."""
|
|
if resource_filter is not None and not resource_filter(resource):
|
|
return
|
|
async_add_entities([switch_class(bridge, controller, resource)])
|
|
|
|
# add all current items in controller
|
|
for item in controller:
|
|
async_add_entity(EventType.RESOURCE_ADDED, item)
|
|
|
|
# register listener for new items only
|
|
config_entry.async_on_unload(
|
|
controller.subscribe(
|
|
async_add_entity, event_filter=EventType.RESOURCE_ADDED
|
|
)
|
|
)
|
|
|
|
@callback
|
|
def is_user_automation(resource: BehaviorInstance) -> bool:
|
|
"""Return if the behavior instance is an automation from the Hue app.
|
|
|
|
Anything else is device configuration, which the bridge keeps running
|
|
even after it accepts switching it off. Categories we do not recognise
|
|
are skipped too, better no switch than one that does nothing.
|
|
"""
|
|
script = api.config.behavior_script.get(resource.script_id)
|
|
return (
|
|
script is not None
|
|
and script.metadata.category is BehaviorScriptCategory.AUTOMATION
|
|
)
|
|
|
|
# clean up entities previously created for internal behavior instances
|
|
entity_registry = er.async_get(hass)
|
|
for resource in api.config.behavior_instance:
|
|
if is_user_automation(resource):
|
|
continue
|
|
if entity_id := entity_registry.async_get_entity_id(
|
|
Platform.SWITCH, DOMAIN, resource.id
|
|
):
|
|
entity_registry.async_remove(entity_id)
|
|
|
|
# setup for each switch-type hue resource
|
|
register_items(api.sensors.motion, HueMotionSensorEnabledEntity)
|
|
register_items(api.sensors.light_level, HueLightSensorEnabledEntity)
|
|
register_items(
|
|
api.config.behavior_instance,
|
|
HueBehaviorInstanceEnabledEntity,
|
|
is_user_automation,
|
|
)
|
|
|
|
|
|
class HueResourceEnabledEntity(HueBaseEntity, SwitchEntity):
|
|
"""Represent a Switch entity from a Hue resource that toggles."""
|
|
|
|
controller: BehaviorInstanceController | LightLevelController | MotionController
|
|
resource: BehaviorInstance | LightLevel | Motion
|
|
|
|
entity_description = SwitchEntityDescription(
|
|
key="sensing_service_enabled",
|
|
device_class=SwitchDeviceClass.SWITCH,
|
|
entity_category=EntityCategory.CONFIG,
|
|
has_entity_name=True,
|
|
)
|
|
|
|
@property
|
|
@override
|
|
def is_on(self) -> bool:
|
|
"""Return true if the switch is on."""
|
|
return self.resource.enabled
|
|
|
|
@override
|
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
|
"""Turn the entity on."""
|
|
await self.bridge.async_request_call(
|
|
self.controller.set_enabled, self.resource.id, enabled=True
|
|
)
|
|
|
|
@override
|
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
|
"""Turn the entity on."""
|
|
await self.bridge.async_request_call(
|
|
self.controller.set_enabled, self.resource.id, enabled=False
|
|
)
|
|
|
|
|
|
class HueBehaviorInstanceEnabledEntity(HueResourceEnabledEntity):
|
|
"""Representation of a Switch entity to enable/disable a Hue Behavior Instance.
|
|
|
|
Automations that the Hue app runs with a play button, such as mimic
|
|
presence, are started and stopped. All others are enabled and disabled.
|
|
"""
|
|
|
|
controller: BehaviorInstanceController
|
|
resource: BehaviorInstance
|
|
|
|
entity_description = SwitchEntityDescription(
|
|
key="behavior_instance",
|
|
device_class=SwitchDeviceClass.SWITCH,
|
|
entity_category=EntityCategory.CONFIG,
|
|
)
|
|
|
|
@property
|
|
@override
|
|
def name(self) -> str:
|
|
"""Return name for this entity."""
|
|
return f"Automation: {self.resource.metadata.name}"
|
|
|
|
@property
|
|
@override
|
|
def is_on(self) -> bool:
|
|
"""Return true if the switch is on."""
|
|
if (run_state := self.resource.presence_mimicking_state) is not None:
|
|
return run_state is PresenceMimickingState.STARTED
|
|
return self.resource.enabled
|
|
|
|
@override
|
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
|
"""Turn the entity on."""
|
|
if self.resource.presence_mimicking_state is None:
|
|
await super().async_turn_on(**kwargs)
|
|
return
|
|
await self.bridge.async_request_call(self.controller.start, self.resource.id)
|
|
|
|
@override
|
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
|
"""Turn the entity off."""
|
|
if self.resource.presence_mimicking_state is None:
|
|
await super().async_turn_off(**kwargs)
|
|
return
|
|
await self.bridge.async_request_call(self.controller.stop, self.resource.id)
|
|
|
|
|
|
class HueMotionSensorEnabledEntity(HueResourceEnabledEntity):
|
|
"""Representation of a Switch entity to enable/disable a Hue motion sensor."""
|
|
|
|
entity_description = SwitchEntityDescription(
|
|
key="motion_sensor_enabled",
|
|
device_class=SwitchDeviceClass.SWITCH,
|
|
entity_category=EntityCategory.CONFIG,
|
|
has_entity_name=True,
|
|
translation_key="motion_sensor_enabled",
|
|
)
|
|
|
|
|
|
class HueLightSensorEnabledEntity(HueResourceEnabledEntity):
|
|
"""Representation of a Switch entity to enable/disable a Hue light sensor."""
|
|
|
|
entity_description = SwitchEntityDescription(
|
|
key="light_sensor_enabled",
|
|
device_class=SwitchDeviceClass.SWITCH,
|
|
entity_category=EntityCategory.CONFIG,
|
|
has_entity_name=True,
|
|
translation_key="light_sensor_enabled",
|
|
)
|