mirror of
https://github.com/home-assistant/core.git
synced 2026-08-24 10:13:52 -05:00
Add RadioFrequencyTransmitterConsumerEntity and migrate consumers (#171026)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: piitaya <5878303+piitaya@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
piitaya
Copilot Autofix powered by AI
parent
1608e058b5
commit
b3851c1be6
@@ -1,76 +1,24 @@
|
||||
"""Common entity for Honeywell String Lights integration."""
|
||||
|
||||
import logging
|
||||
from typing import override
|
||||
|
||||
from homeassistant.components.radio_frequency import (
|
||||
RadioFrequencyTransmitterConsumerEntity,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import STATE_UNAVAILABLE
|
||||
from homeassistant.core import Event, EventStateChangedData, callback
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.event import async_track_state_change_event
|
||||
|
||||
from .const import CONF_TRANSMITTER, DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
from .const import DOMAIN
|
||||
|
||||
|
||||
class HoneywellStringLightsEntity(Entity):
|
||||
class HoneywellStringLightsEntity(RadioFrequencyTransmitterConsumerEntity):
|
||||
"""Honeywell String Lights base entity."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(self, entry: ConfigEntry) -> None:
|
||||
"""Initialize the entity."""
|
||||
self._transmitter = entry.data[CONF_TRANSMITTER]
|
||||
self._attr_unique_id = entry.entry_id
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, entry.entry_id)},
|
||||
manufacturer="Honeywell",
|
||||
model="String Lights",
|
||||
)
|
||||
|
||||
@override
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Subscribe to transmitter entity state changes."""
|
||||
await super().async_added_to_hass()
|
||||
|
||||
transmitter_entity_id = er.async_validate_entity_id(
|
||||
er.async_get(self.hass), self._transmitter
|
||||
)
|
||||
|
||||
@callback
|
||||
def _async_transmitter_state_changed(
|
||||
event: Event[EventStateChangedData],
|
||||
) -> None:
|
||||
"""Handle transmitter entity state changes."""
|
||||
new_state = event.data["new_state"]
|
||||
transmitter_available = (
|
||||
new_state is not None and new_state.state != STATE_UNAVAILABLE
|
||||
)
|
||||
if transmitter_available != self.available:
|
||||
_LOGGER.info(
|
||||
"Transmitter %s used by %s is %s",
|
||||
transmitter_entity_id,
|
||||
self.entity_id,
|
||||
"available" if transmitter_available else "unavailable",
|
||||
)
|
||||
|
||||
self._attr_available = transmitter_available
|
||||
self.async_write_ha_state()
|
||||
|
||||
self.async_on_remove(
|
||||
async_track_state_change_event(
|
||||
self.hass,
|
||||
[transmitter_entity_id],
|
||||
_async_transmitter_state_changed,
|
||||
)
|
||||
)
|
||||
|
||||
# Set initial availability based on current transmitter entity state
|
||||
transmitter_state = self.hass.states.get(transmitter_entity_id)
|
||||
self._attr_available = (
|
||||
transmitter_state is not None
|
||||
and transmitter_state.state != STATE_UNAVAILABLE
|
||||
)
|
||||
|
||||
@@ -5,13 +5,13 @@ from typing import Any, override
|
||||
from rf_protocols.codes.honeywell.string_lights import CODES
|
||||
|
||||
from homeassistant.components.light import ColorMode, LightEntity
|
||||
from homeassistant.components.radio_frequency import async_send_command
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import STATE_ON
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.restore_state import RestoreEntity
|
||||
|
||||
from .const import CONF_TRANSMITTER
|
||||
from .entity import HoneywellStringLightsEntity
|
||||
|
||||
PARALLEL_UPDATES = 1
|
||||
@@ -33,7 +33,11 @@ class HoneywellStringLight(HoneywellStringLightsEntity, LightEntity, RestoreEnti
|
||||
_attr_color_mode = ColorMode.ONOFF
|
||||
_attr_supported_color_modes = {ColorMode.ONOFF}
|
||||
_attr_name = None
|
||||
_attr_should_poll = False
|
||||
|
||||
def __init__(self, entry: ConfigEntry) -> None:
|
||||
"""Initialize the entity."""
|
||||
super().__init__(entry)
|
||||
self._rf_transmitter_entity_id_or_uuid = entry.data[CONF_TRANSMITTER]
|
||||
|
||||
@override
|
||||
async def async_added_to_hass(self) -> None:
|
||||
@@ -45,20 +49,18 @@ class HoneywellStringLight(HoneywellStringLightsEntity, LightEntity, RestoreEnti
|
||||
@override
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn on the light."""
|
||||
await self._async_send_command("turn_on")
|
||||
await self._async_send_rf_command("turn_on")
|
||||
self._attr_is_on = True
|
||||
self.async_write_ha_state()
|
||||
|
||||
@override
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn off the light."""
|
||||
await self._async_send_command("turn_off")
|
||||
await self._async_send_rf_command("turn_off")
|
||||
self._attr_is_on = False
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def _async_send_command(self, name: str) -> None:
|
||||
async def _async_send_rf_command(self, name: str) -> None:
|
||||
"""Load the named command and send it via the configured transmitter."""
|
||||
command = await CODES.async_load_command(name)
|
||||
await async_send_command(
|
||||
self.hass, self._transmitter, command, context=self._context
|
||||
)
|
||||
await self._send_command(command)
|
||||
|
||||
@@ -1,76 +1,24 @@
|
||||
"""Common entity for the Novy Cooker Hood integration."""
|
||||
|
||||
import logging
|
||||
from typing import override
|
||||
|
||||
from homeassistant.components.radio_frequency import (
|
||||
RadioFrequencyTransmitterConsumerEntity,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import STATE_UNAVAILABLE
|
||||
from homeassistant.core import Event, EventStateChangedData, callback
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.event import async_track_state_change_event
|
||||
|
||||
from .const import CONF_TRANSMITTER, DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
from .const import DOMAIN
|
||||
|
||||
|
||||
class NovyCookerHoodEntity(Entity):
|
||||
class NovyCookerHoodEntity(RadioFrequencyTransmitterConsumerEntity):
|
||||
"""Novy Cooker Hood base entity."""
|
||||
|
||||
_attr_assumed_state = True
|
||||
_attr_has_entity_name = True
|
||||
_attr_should_poll = False
|
||||
|
||||
def __init__(self, entry: ConfigEntry) -> None:
|
||||
"""Initialize the entity."""
|
||||
self._transmitter = entry.data[CONF_TRANSMITTER]
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, entry.entry_id)},
|
||||
manufacturer="Novy",
|
||||
model="Cooker Hood",
|
||||
)
|
||||
|
||||
@override
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Subscribe to transmitter entity state changes."""
|
||||
await super().async_added_to_hass()
|
||||
|
||||
transmitter_entity_id = er.async_validate_entity_id(
|
||||
er.async_get(self.hass), self._transmitter
|
||||
)
|
||||
|
||||
@callback
|
||||
def _async_transmitter_state_changed(
|
||||
event: Event[EventStateChangedData],
|
||||
) -> None:
|
||||
"""Handle transmitter entity state changes."""
|
||||
new_state = event.data["new_state"]
|
||||
transmitter_available = (
|
||||
new_state is not None and new_state.state != STATE_UNAVAILABLE
|
||||
)
|
||||
if transmitter_available != self.available:
|
||||
_LOGGER.info(
|
||||
"Transmitter %s used by %s is %s",
|
||||
transmitter_entity_id,
|
||||
self.entity_id,
|
||||
"available" if transmitter_available else "unavailable",
|
||||
)
|
||||
|
||||
self._attr_available = transmitter_available
|
||||
self.async_write_ha_state()
|
||||
|
||||
self.async_on_remove(
|
||||
async_track_state_change_event(
|
||||
self.hass,
|
||||
[transmitter_entity_id],
|
||||
_async_transmitter_state_changed,
|
||||
)
|
||||
)
|
||||
|
||||
transmitter_state = self.hass.states.get(transmitter_entity_id)
|
||||
self._attr_available = (
|
||||
transmitter_state is not None
|
||||
and transmitter_state.state != STATE_UNAVAILABLE
|
||||
)
|
||||
|
||||
@@ -4,14 +4,12 @@ import math
|
||||
from typing import Any, override
|
||||
|
||||
from rf_protocols.codes.novy.cooker_hood import NovyCookerHoodButton
|
||||
from rf_protocols.commands.novy import NovyCookerHoodCommand
|
||||
|
||||
from homeassistant.components.fan import (
|
||||
FanEntity,
|
||||
FanEntityFeature,
|
||||
FanEntityStateAttribute,
|
||||
)
|
||||
from homeassistant.components.radio_frequency import async_send_command
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_CODE
|
||||
from homeassistant.core import HomeAssistant
|
||||
@@ -22,7 +20,7 @@ from homeassistant.util.percentage import (
|
||||
ranged_value_to_percentage,
|
||||
)
|
||||
|
||||
from .const import SPEED_COUNT
|
||||
from .const import CONF_TRANSMITTER, SPEED_COUNT
|
||||
from .entity import NovyCookerHoodEntity
|
||||
|
||||
PARALLEL_UPDATES = 1
|
||||
@@ -53,6 +51,7 @@ class NovyCookerHoodFan(NovyCookerHoodEntity, FanEntity, RestoreEntity):
|
||||
def __init__(self, entry: ConfigEntry) -> None:
|
||||
"""Initialize the fan."""
|
||||
super().__init__(entry)
|
||||
self._rf_transmitter_entity_id_or_uuid = entry.data[CONF_TRANSMITTER]
|
||||
self._code: int = entry.data[CONF_CODE]
|
||||
self._level = 0
|
||||
self._attr_unique_id = entry.entry_id
|
||||
@@ -116,7 +115,7 @@ class NovyCookerHoodFan(NovyCookerHoodEntity, FanEntity, RestoreEntity):
|
||||
steps = self._steps_from_percentage(percentage_step)
|
||||
plus = NovyCookerHoodButton.PLUS.to_command(channel=self._code)
|
||||
for _ in range(steps):
|
||||
await self._async_send(plus)
|
||||
await self._send_command(plus)
|
||||
self._level = min(SPEED_COUNT, self._level + steps)
|
||||
self.async_write_ha_state()
|
||||
|
||||
@@ -126,7 +125,7 @@ class NovyCookerHoodFan(NovyCookerHoodEntity, FanEntity, RestoreEntity):
|
||||
steps = self._steps_from_percentage(percentage_step)
|
||||
minus = NovyCookerHoodButton.MINUS.to_command(channel=self._code)
|
||||
for _ in range(steps):
|
||||
await self._async_send(minus)
|
||||
await self._send_command(minus)
|
||||
self._level = max(0, self._level - steps)
|
||||
self.async_write_ha_state()
|
||||
|
||||
@@ -141,16 +140,10 @@ class NovyCookerHoodFan(NovyCookerHoodEntity, FanEntity, RestoreEntity):
|
||||
"""Reset to off with `SPEED_COUNT` minus presses, then climb to level."""
|
||||
minus = NovyCookerHoodButton.MINUS.to_command(channel=self._code)
|
||||
for _ in range(SPEED_COUNT):
|
||||
await self._async_send(minus)
|
||||
await self._send_command(minus)
|
||||
if level > 0:
|
||||
plus = NovyCookerHoodButton.PLUS.to_command(channel=self._code)
|
||||
for _ in range(level):
|
||||
await self._async_send(plus)
|
||||
await self._send_command(plus)
|
||||
self._level = level
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def _async_send(self, command: NovyCookerHoodCommand) -> None:
|
||||
"""Send a single RF command via the configured transmitter."""
|
||||
await async_send_command(
|
||||
self.hass, self._transmitter, command, context=self._context
|
||||
)
|
||||
|
||||
@@ -5,13 +5,13 @@ from typing import Any, override
|
||||
from rf_protocols.codes.novy.cooker_hood import NovyCookerHoodButton
|
||||
|
||||
from homeassistant.components.light import ColorMode, LightEntity
|
||||
from homeassistant.components.radio_frequency import async_send_command
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_CODE, STATE_ON
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.restore_state import RestoreEntity
|
||||
|
||||
from .const import CONF_TRANSMITTER
|
||||
from .entity import NovyCookerHoodEntity
|
||||
|
||||
PARALLEL_UPDATES = 1
|
||||
@@ -36,6 +36,7 @@ class NovyCookerHoodLight(NovyCookerHoodEntity, LightEntity, RestoreEntity):
|
||||
def __init__(self, entry: ConfigEntry) -> None:
|
||||
"""Initialize the light."""
|
||||
super().__init__(entry)
|
||||
self._rf_transmitter_entity_id_or_uuid = entry.data[CONF_TRANSMITTER]
|
||||
self._code = entry.data[CONF_CODE]
|
||||
self._attr_unique_id = entry.entry_id
|
||||
|
||||
@@ -63,6 +64,4 @@ class NovyCookerHoodLight(NovyCookerHoodEntity, LightEntity, RestoreEntity):
|
||||
async def _async_send_light(self) -> None:
|
||||
"""Send the light toggle command via the configured transmitter."""
|
||||
command = NovyCookerHoodButton.LIGHT.to_command(channel=self._code)
|
||||
await async_send_command(
|
||||
self.hass, self._transmitter, command, context=self._context
|
||||
)
|
||||
await self._send_command(command)
|
||||
|
||||
@@ -6,9 +6,9 @@ import logging
|
||||
from rf_protocols import ModulationType, RadioFrequencyCommand
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import Context, HomeAssistant, callback
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import config_validation as cv, entity_registry as er
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
@@ -18,11 +18,14 @@ from .entity import (
|
||||
RadioFrequencyTransmitterEntity,
|
||||
RadioFrequencyTransmitterEntityDescription,
|
||||
)
|
||||
from .helpers import RadioFrequencyTransmitterConsumerEntity, async_send_command
|
||||
|
||||
__all__ = [
|
||||
"DATA_COMPONENT",
|
||||
"DOMAIN",
|
||||
"ModulationType",
|
||||
"RadioFrequencyCommand",
|
||||
"RadioFrequencyTransmitterConsumerEntity",
|
||||
"RadioFrequencyTransmitterEntity",
|
||||
"RadioFrequencyTransmitterEntityDescription",
|
||||
"async_get_transmitters",
|
||||
@@ -95,60 +98,3 @@ def async_get_transmitters(
|
||||
if entity.supports_modulation(modulation)
|
||||
and entity.supports_frequency(frequency)
|
||||
]
|
||||
|
||||
|
||||
async def async_send_command(
|
||||
hass: HomeAssistant,
|
||||
entity_id_or_uuid: str,
|
||||
command: RadioFrequencyCommand,
|
||||
context: Context | None = None,
|
||||
) -> None:
|
||||
"""Send an RF command to the specified radio_frequency entity.
|
||||
|
||||
Raises:
|
||||
vol.Invalid: If `entity_id_or_uuid` is not a valid entity ID or known entity
|
||||
registry UUID.
|
||||
HomeAssistantError: If the radio_frequency component is not loaded or the
|
||||
resolved entity is not found.
|
||||
"""
|
||||
component = hass.data.get(DATA_COMPONENT)
|
||||
if component is None:
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="component_not_loaded",
|
||||
)
|
||||
|
||||
ent_reg = er.async_get(hass)
|
||||
entity_id = er.async_validate_entity_id(ent_reg, entity_id_or_uuid)
|
||||
entity = component.get_entity(entity_id)
|
||||
if entity is None:
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="entity_not_found",
|
||||
translation_placeholders={"entity_id": entity_id},
|
||||
)
|
||||
|
||||
if not entity.supports_frequency(command.frequency):
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="unsupported_frequency",
|
||||
translation_placeholders={
|
||||
"entity_id": entity_id,
|
||||
"frequency": str(command.frequency),
|
||||
},
|
||||
)
|
||||
|
||||
if not entity.supports_modulation(command.modulation):
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="unsupported_modulation",
|
||||
translation_placeholders={
|
||||
"entity_id": entity_id,
|
||||
"modulation": command.modulation,
|
||||
},
|
||||
)
|
||||
|
||||
if context is not None:
|
||||
entity.async_set_context(context)
|
||||
|
||||
await entity.async_send_command_internal(command)
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
"""Helper base entities for integrations that consume RF transmitters."""
|
||||
|
||||
import logging
|
||||
from typing import override
|
||||
|
||||
from rf_protocols import RadioFrequencyCommand
|
||||
|
||||
from homeassistant.const import ATTR_ENTITY_ID, STATE_UNAVAILABLE
|
||||
from homeassistant.core import (
|
||||
CALLBACK_TYPE,
|
||||
Context,
|
||||
Event,
|
||||
EventStateChangedData,
|
||||
HomeAssistant,
|
||||
callback,
|
||||
)
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.event import (
|
||||
async_track_entity_registry_updated_event,
|
||||
async_track_state_change_event,
|
||||
)
|
||||
|
||||
from .const import DATA_COMPONENT, DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_send_command(
|
||||
hass: HomeAssistant,
|
||||
entity_id_or_uuid: str,
|
||||
command: RadioFrequencyCommand,
|
||||
context: Context | None = None,
|
||||
) -> None:
|
||||
"""Send an RF command to the specified radio_frequency entity.
|
||||
|
||||
Raises:
|
||||
vol.Invalid: If `entity_id_or_uuid` is not a valid entity ID or known entity
|
||||
registry UUID.
|
||||
HomeAssistantError: If the radio_frequency component is not loaded or the
|
||||
resolved entity is not found.
|
||||
"""
|
||||
component = hass.data.get(DATA_COMPONENT)
|
||||
if component is None:
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="component_not_loaded",
|
||||
)
|
||||
|
||||
ent_reg = er.async_get(hass)
|
||||
entity_id = er.async_validate_entity_id(ent_reg, entity_id_or_uuid)
|
||||
entity = component.get_entity(entity_id)
|
||||
if entity is None:
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="entity_not_found",
|
||||
translation_placeholders={"entity_id": entity_id},
|
||||
)
|
||||
|
||||
if not entity.supports_frequency(command.frequency):
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="unsupported_frequency",
|
||||
translation_placeholders={
|
||||
"entity_id": entity_id,
|
||||
"frequency": str(command.frequency),
|
||||
},
|
||||
)
|
||||
|
||||
if not entity.supports_modulation(command.modulation):
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="unsupported_modulation",
|
||||
translation_placeholders={
|
||||
"entity_id": entity_id,
|
||||
"modulation": command.modulation,
|
||||
},
|
||||
)
|
||||
|
||||
if context is not None:
|
||||
entity.async_set_context(context)
|
||||
|
||||
await entity.async_send_command_internal(command)
|
||||
|
||||
|
||||
class RadioFrequencyTransmitterConsumerEntity(Entity):
|
||||
"""Base entity for integrations that send commands via an RF transmitter.
|
||||
|
||||
Tracks the availability of the underlying RF transmitter entity.
|
||||
"""
|
||||
|
||||
_attr_should_poll = False
|
||||
_rf_transmitter_entity_id_or_uuid: str
|
||||
_rf_unsubscribes: list[CALLBACK_TYPE]
|
||||
|
||||
@override
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Subscribe to RF entity state and rename events."""
|
||||
await super().async_added_to_hass()
|
||||
|
||||
self._rf_unsubscribes = []
|
||||
self.async_on_remove(self._async_unsubscribe_rf)
|
||||
self._async_track_rf_entity(
|
||||
er.async_validate_entity_id(
|
||||
er.async_get(self.hass), self._rf_transmitter_entity_id_or_uuid
|
||||
)
|
||||
)
|
||||
|
||||
@callback
|
||||
def _async_unsubscribe_rf(self) -> None:
|
||||
"""Tear down the current transmitter subscriptions."""
|
||||
while self._rf_unsubscribes:
|
||||
self._rf_unsubscribes.pop()()
|
||||
|
||||
@callback
|
||||
def _async_track_rf_entity(self, entity_id: str) -> None:
|
||||
"""Track state and rename events for the resolved transmitter entity_id."""
|
||||
self._async_unsubscribe_rf()
|
||||
self._rf_unsubscribes.append(
|
||||
async_track_state_change_event(
|
||||
self.hass, [entity_id], self._async_rf_state_changed
|
||||
)
|
||||
)
|
||||
self._rf_unsubscribes.append(
|
||||
async_track_entity_registry_updated_event(
|
||||
self.hass, entity_id, self._async_rf_registry_updated
|
||||
)
|
||||
)
|
||||
rf_state = self.hass.states.get(entity_id)
|
||||
self._attr_available = (
|
||||
rf_state is not None and rf_state.state != STATE_UNAVAILABLE
|
||||
)
|
||||
|
||||
async def _send_command(self, command: RadioFrequencyCommand) -> None:
|
||||
"""Send an RF command through the RF transmitter entity."""
|
||||
await async_send_command(
|
||||
self.hass,
|
||||
self._rf_transmitter_entity_id_or_uuid,
|
||||
command,
|
||||
context=self._context,
|
||||
)
|
||||
|
||||
@callback
|
||||
def _async_rf_registry_updated(
|
||||
self, event: Event[er.EventEntityRegistryUpdatedData]
|
||||
) -> None:
|
||||
"""Re-track the transmitter when it is renamed."""
|
||||
data = event.data
|
||||
if data["action"] != "update":
|
||||
return
|
||||
if ATTR_ENTITY_ID not in data["changes"]:
|
||||
return
|
||||
self._async_track_rf_entity(data[ATTR_ENTITY_ID])
|
||||
self.async_write_ha_state()
|
||||
|
||||
@callback
|
||||
def _async_rf_state_changed(self, event: Event[EventStateChangedData]) -> None:
|
||||
"""Handle RF entity state changes."""
|
||||
new_state = event.data["new_state"]
|
||||
rf_available = new_state is not None and new_state.state != STATE_UNAVAILABLE
|
||||
if rf_available != self.available:
|
||||
_LOGGER.info(
|
||||
"Radio frequency entity %s used by %s is %s",
|
||||
event.data["entity_id"],
|
||||
self.entity_id,
|
||||
"available" if rf_available else "unavailable",
|
||||
)
|
||||
|
||||
self._attr_available = rf_available
|
||||
self.async_write_ha_state()
|
||||
@@ -15,6 +15,7 @@ from homeassistant.const import (
|
||||
STATE_UNKNOWN,
|
||||
)
|
||||
from homeassistant.core import Context, HomeAssistant, State
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .conftest import TRANSMITTER_ENTITY_ID
|
||||
|
||||
@@ -96,3 +97,27 @@ async def test_entity_follows_transmitter_availability(
|
||||
await assert_availability_follows_source_entity(
|
||||
hass, ENTITY_ID, TRANSMITTER_ENTITY_ID
|
||||
)
|
||||
|
||||
|
||||
async def test_tracking_follows_transmitter_rename(
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
mock_rf_entity: MockRadioFrequencyEntity,
|
||||
init_novy_cooker_hood: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Availability tracking and sending survive a transmitter entity rename."""
|
||||
new_transmitter_id = "radio_frequency.renamed_transmitter"
|
||||
entity_registry.async_update_entity(
|
||||
TRANSMITTER_ENTITY_ID, new_entity_id=new_transmitter_id
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await assert_availability_follows_source_entity(hass, ENTITY_ID, new_transmitter_id)
|
||||
|
||||
await hass.services.async_call(
|
||||
LIGHT_DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
{ATTR_ENTITY_ID: ENTITY_ID},
|
||||
blocking=True,
|
||||
)
|
||||
assert len(mock_rf_entity.send_command_calls) == 1
|
||||
|
||||
Reference in New Issue
Block a user