mirror of
https://github.com/home-assistant/core.git
synced 2026-05-22 00:35:16 +02:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e08ba030a0 | |||
| 39066b6e3a | |||
| a23a9b350b | |||
| fdaa807ca8 | |||
| f290dcc03f | |||
| ba9504a70d | |||
| 5c5d9828f8 |
Generated
+2
-2
@@ -1413,8 +1413,8 @@ CLAUDE.md @home-assistant/core
|
||||
/tests/components/pushover/ @engrbm87
|
||||
/homeassistant/components/pvoutput/ @frenck
|
||||
/tests/components/pvoutput/ @frenck
|
||||
/homeassistant/components/pvpc_hourly_pricing/ @azogue
|
||||
/tests/components/pvpc_hourly_pricing/ @azogue
|
||||
/homeassistant/components/pvpc_hourly_pricing/ @azogue @chiro79
|
||||
/tests/components/pvpc_hourly_pricing/ @azogue @chiro79
|
||||
/homeassistant/components/pyload/ @tr4nt0r
|
||||
/tests/components/pyload/ @tr4nt0r
|
||||
/homeassistant/components/qbittorrent/ @geoffreylagaisse @finder39
|
||||
|
||||
@@ -1,18 +1,10 @@
|
||||
"""Common entity for Honeywell String Lights integration."""
|
||||
|
||||
import logging
|
||||
|
||||
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):
|
||||
@@ -22,53 +14,9 @@ class HoneywellStringLightsEntity(Entity):
|
||||
|
||||
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",
|
||||
)
|
||||
|
||||
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,16 @@ from typing import Any
|
||||
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.components.radio_frequency import (
|
||||
RadioFrequencyTransmitterConsumerEntity,
|
||||
)
|
||||
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
|
||||
@@ -26,14 +29,23 @@ async def async_setup_entry(
|
||||
async_add_entities([HoneywellStringLight(config_entry)])
|
||||
|
||||
|
||||
class HoneywellStringLight(HoneywellStringLightsEntity, LightEntity, RestoreEntity):
|
||||
class HoneywellStringLight(
|
||||
HoneywellStringLightsEntity,
|
||||
RadioFrequencyTransmitterConsumerEntity,
|
||||
LightEntity,
|
||||
RestoreEntity,
|
||||
):
|
||||
"""Representation of a Honeywell String Lights set controlled via RF."""
|
||||
|
||||
_attr_assumed_state = True
|
||||
_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 = entry.data[CONF_TRANSMITTER]
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Restore last known state."""
|
||||
@@ -43,19 +55,17 @@ class HoneywellStringLight(HoneywellStringLightsEntity, LightEntity, RestoreEnti
|
||||
|
||||
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()
|
||||
|
||||
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)
|
||||
|
||||
@@ -12,6 +12,6 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/indevolt",
|
||||
"integration_type": "device",
|
||||
"iot_class": "local_polling",
|
||||
"quality_scale": "silver",
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["indevolt-api==1.8.1"]
|
||||
}
|
||||
|
||||
@@ -77,7 +77,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: LunatoneConfigEntry) ->
|
||||
await coordinator_info.async_config_entry_first_refresh()
|
||||
|
||||
if info_api.data is None or info_api.serial_number is None:
|
||||
# pylint: disable-next=home-assistant-exception-translation-key-missing
|
||||
raise ConfigEntryError(
|
||||
translation_domain=DOMAIN, translation_key="missing_device_info"
|
||||
)
|
||||
|
||||
@@ -35,5 +35,10 @@
|
||||
"description": "Enter the URL of your Lunatone device.\nHome Assistant will use this address to connect to the device API."
|
||||
}
|
||||
}
|
||||
},
|
||||
"exceptions": {
|
||||
"missing_device_info": {
|
||||
"message": "Unable to read device information. Please verify the device's network connection."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ from music_assistant_models.errors import (
|
||||
from music_assistant_models.player import Player
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry, ConfigEntryState
|
||||
from homeassistant.const import CONF_URL, EVENT_HOMEASSISTANT_STOP, Platform
|
||||
from homeassistant.const import CONF_TOKEN, CONF_URL, EVENT_HOMEASSISTANT_STOP, Platform
|
||||
from homeassistant.core import Event, HomeAssistant
|
||||
from homeassistant.exceptions import (
|
||||
ConfigEntryAuthFailed,
|
||||
@@ -38,7 +38,7 @@ from homeassistant.helpers.issue_registry import (
|
||||
async_delete_issue,
|
||||
)
|
||||
|
||||
from .const import ATTR_CONF_EXPOSE_PLAYER_TO_HA, CONF_TOKEN, DOMAIN, LOGGER
|
||||
from .const import ATTR_CONF_EXPOSE_PLAYER_TO_HA, DOMAIN, LOGGER
|
||||
from .helpers import get_music_assistant_client
|
||||
from .services import register_actions
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ from homeassistant.config_entries import (
|
||||
ConfigFlow,
|
||||
ConfigFlowResult,
|
||||
)
|
||||
from homeassistant.const import CONF_URL
|
||||
from homeassistant.const import CONF_TOKEN, CONF_URL
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import aiohttp_client
|
||||
from homeassistant.helpers.config_entry_oauth2_flow import (
|
||||
@@ -31,13 +31,7 @@ from homeassistant.helpers.config_entry_oauth2_flow import (
|
||||
from homeassistant.helpers.service_info.hassio import HassioServiceInfo
|
||||
from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo
|
||||
|
||||
from .const import (
|
||||
AUTH_SCHEMA_VERSION,
|
||||
CONF_TOKEN,
|
||||
DOMAIN,
|
||||
HASSIO_DISCOVERY_SCHEMA_VERSION,
|
||||
LOGGER,
|
||||
)
|
||||
from .const import AUTH_SCHEMA_VERSION, DOMAIN, HASSIO_DISCOVERY_SCHEMA_VERSION, LOGGER
|
||||
|
||||
DEFAULT_TITLE = "Music Assistant"
|
||||
DEFAULT_URL = "http://mass.local:8095"
|
||||
|
||||
@@ -12,9 +12,6 @@ AUTH_SCHEMA_VERSION = 28
|
||||
# Schema version where hassio discovery support was added
|
||||
HASSIO_DISCOVERY_SCHEMA_VERSION = 28
|
||||
|
||||
# pylint: disable-next=home-assistant-duplicate-const
|
||||
CONF_TOKEN = "token"
|
||||
|
||||
ATTR_IS_GROUP = "is_group"
|
||||
ATTR_GROUP_MEMBERS = "group_members"
|
||||
ATTR_GROUP_PARENTS = "group_parents"
|
||||
|
||||
@@ -1,18 +1,10 @@
|
||||
"""Common entity for the Novy Cooker Hood integration."""
|
||||
|
||||
import logging
|
||||
|
||||
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):
|
||||
@@ -20,55 +12,11 @@ class NovyCookerHoodEntity(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",
|
||||
)
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
@@ -6,7 +6,9 @@ from typing import Any
|
||||
from rf_protocols.codes.novy.cooker_hood import get_codes_for_code
|
||||
|
||||
from homeassistant.components.fan import ATTR_PERCENTAGE, FanEntity, FanEntityFeature
|
||||
from homeassistant.components.radio_frequency import async_send_command
|
||||
from homeassistant.components.radio_frequency import (
|
||||
RadioFrequencyTransmitterConsumerEntity,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_CODE
|
||||
from homeassistant.core import HomeAssistant
|
||||
@@ -18,7 +20,7 @@ from homeassistant.util.percentage import (
|
||||
)
|
||||
|
||||
from .commands import COMMAND_MINUS, COMMAND_PLUS
|
||||
from .const import SPEED_COUNT
|
||||
from .const import CONF_TRANSMITTER, SPEED_COUNT
|
||||
from .entity import NovyCookerHoodEntity
|
||||
|
||||
PARALLEL_UPDATES = 1
|
||||
@@ -35,7 +37,12 @@ async def async_setup_entry(
|
||||
async_add_entities([NovyCookerHoodFan(config_entry)])
|
||||
|
||||
|
||||
class NovyCookerHoodFan(NovyCookerHoodEntity, FanEntity, RestoreEntity):
|
||||
class NovyCookerHoodFan(
|
||||
NovyCookerHoodEntity,
|
||||
RadioFrequencyTransmitterConsumerEntity,
|
||||
FanEntity,
|
||||
RestoreEntity,
|
||||
):
|
||||
"""Calibration-based fan: each change resets to off then climbs to target."""
|
||||
|
||||
_attr_name = None
|
||||
@@ -49,6 +56,7 @@ class NovyCookerHoodFan(NovyCookerHoodEntity, FanEntity, RestoreEntity):
|
||||
def __init__(self, entry: ConfigEntry) -> None:
|
||||
"""Initialize the fan."""
|
||||
super().__init__(entry)
|
||||
self._rf_transmitter_entity_id = entry.data[CONF_TRANSMITTER]
|
||||
self._codes = get_codes_for_code(entry.data[CONF_CODE])
|
||||
self._level = 0
|
||||
self._attr_unique_id = entry.entry_id
|
||||
@@ -105,7 +113,7 @@ class NovyCookerHoodFan(NovyCookerHoodEntity, FanEntity, RestoreEntity):
|
||||
steps = self._steps_from_percentage(percentage_step)
|
||||
plus = await self._codes.async_load_command(COMMAND_PLUS)
|
||||
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()
|
||||
|
||||
@@ -114,7 +122,7 @@ class NovyCookerHoodFan(NovyCookerHoodEntity, FanEntity, RestoreEntity):
|
||||
steps = self._steps_from_percentage(percentage_step)
|
||||
minus = await self._codes.async_load_command(COMMAND_MINUS)
|
||||
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()
|
||||
|
||||
@@ -129,16 +137,10 @@ class NovyCookerHoodFan(NovyCookerHoodEntity, FanEntity, RestoreEntity):
|
||||
"""Reset to off with `SPEED_COUNT` minus presses, then climb to level."""
|
||||
minus = await self._codes.async_load_command(COMMAND_MINUS)
|
||||
for _ in range(SPEED_COUNT):
|
||||
await self._async_send(minus)
|
||||
await self._send_command(minus)
|
||||
if level > 0:
|
||||
plus = await self._codes.async_load_command(COMMAND_PLUS)
|
||||
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: Any) -> None:
|
||||
"""Send a single RF command via the configured transmitter."""
|
||||
await async_send_command(
|
||||
self.hass, self._transmitter, command, context=self._context
|
||||
)
|
||||
|
||||
@@ -5,7 +5,9 @@ from typing import Any
|
||||
from rf_protocols.codes.novy.cooker_hood import get_codes_for_code
|
||||
|
||||
from homeassistant.components.light import ColorMode, LightEntity
|
||||
from homeassistant.components.radio_frequency import async_send_command
|
||||
from homeassistant.components.radio_frequency import (
|
||||
RadioFrequencyTransmitterConsumerEntity,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_CODE, STATE_ON
|
||||
from homeassistant.core import HomeAssistant
|
||||
@@ -13,6 +15,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.restore_state import RestoreEntity
|
||||
|
||||
from .commands import COMMAND_LIGHT
|
||||
from .const import CONF_TRANSMITTER
|
||||
from .entity import NovyCookerHoodEntity
|
||||
|
||||
PARALLEL_UPDATES = 1
|
||||
@@ -27,7 +30,12 @@ async def async_setup_entry(
|
||||
async_add_entities([NovyCookerHoodLight(config_entry)])
|
||||
|
||||
|
||||
class NovyCookerHoodLight(NovyCookerHoodEntity, LightEntity, RestoreEntity):
|
||||
class NovyCookerHoodLight(
|
||||
NovyCookerHoodEntity,
|
||||
RadioFrequencyTransmitterConsumerEntity,
|
||||
LightEntity,
|
||||
RestoreEntity,
|
||||
):
|
||||
"""Novy cooker hood light toggled via a single RF press."""
|
||||
|
||||
_attr_color_mode = ColorMode.ONOFF
|
||||
@@ -37,6 +45,7 @@ class NovyCookerHoodLight(NovyCookerHoodEntity, LightEntity, RestoreEntity):
|
||||
def __init__(self, entry: ConfigEntry) -> None:
|
||||
"""Initialize the light."""
|
||||
super().__init__(entry)
|
||||
self._rf_transmitter_entity_id = entry.data[CONF_TRANSMITTER]
|
||||
self._codes = get_codes_for_code(entry.data[CONF_CODE])
|
||||
self._attr_unique_id = entry.entry_id
|
||||
|
||||
@@ -48,19 +57,17 @@ class NovyCookerHoodLight(NovyCookerHoodEntity, LightEntity, RestoreEntity):
|
||||
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn the light on by sending the toggle command."""
|
||||
await self._async_send_command(COMMAND_LIGHT)
|
||||
await self._async_send_rf_command(COMMAND_LIGHT)
|
||||
self._attr_is_on = True
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn the light off by sending the toggle command."""
|
||||
await self._async_send_command(COMMAND_LIGHT)
|
||||
await self._async_send_rf_command(COMMAND_LIGHT)
|
||||
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 self._codes.async_load_command(name)
|
||||
await async_send_command(
|
||||
self.hass, self._transmitter, command, context=self._context
|
||||
)
|
||||
await self._send_command(command)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
from collections.abc import Mapping
|
||||
from typing import Any
|
||||
|
||||
from aiopvpc import DEFAULT_POWER_KW, PVPCData
|
||||
from esios_api import DEFAULT_POWER_KW, PVPCData
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import (
|
||||
@@ -63,9 +63,10 @@ class TariffSelectorConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
if user_input is not None:
|
||||
await self.async_set_unique_id(user_input[ATTR_TARIFF])
|
||||
self._abort_if_unique_id_configured()
|
||||
calc_name = f"{DEFAULT_NAME} - {user_input[ATTR_TARIFF]}"
|
||||
if not user_input[CONF_USE_API_TOKEN]:
|
||||
return self.async_create_entry(
|
||||
title=DEFAULT_NAME,
|
||||
title=calc_name,
|
||||
data={
|
||||
ATTR_TARIFF: user_input[ATTR_TARIFF],
|
||||
ATTR_POWER: user_input[ATTR_POWER],
|
||||
@@ -74,7 +75,7 @@ class TariffSelectorConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
},
|
||||
)
|
||||
|
||||
self._name = DEFAULT_NAME
|
||||
self._name = calc_name
|
||||
self._tariff = user_input[ATTR_TARIFF]
|
||||
self._power = user_input[ATTR_POWER]
|
||||
self._power_p3 = user_input[ATTR_POWER_P3]
|
||||
@@ -150,7 +151,7 @@ class TariffSelectorConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
"""Handle re-authentication with ESIOS Token."""
|
||||
self._api_token = entry_data.get(CONF_API_TOKEN)
|
||||
self._use_api_token = self._api_token is not None
|
||||
self._name = DEFAULT_NAME
|
||||
self._name = f"{DEFAULT_NAME} - {entry_data[ATTR_TARIFF]}"
|
||||
self._tariff = entry_data[ATTR_TARIFF]
|
||||
self._power = entry_data[ATTR_POWER]
|
||||
self._power_p3 = entry_data[ATTR_POWER_P3]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Constant values for pvpc_hourly_pricing."""
|
||||
|
||||
from aiopvpc.const import TARIFFS
|
||||
from esios_api.const import TARIFFS
|
||||
import voluptuous as vol
|
||||
|
||||
DOMAIN = "pvpc_hourly_pricing"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
from aiopvpc import BadApiTokenAuthError, EsiosApiData, PVPCData
|
||||
from esios_api import BadApiTokenAuthError, EsiosApiData, PVPCData
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_API_TOKEN
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Helper functions to relate sensors keys and unique ids."""
|
||||
|
||||
from aiopvpc.const import (
|
||||
from esios_api.const import (
|
||||
ALL_SENSORS,
|
||||
KEY_INJECTION,
|
||||
KEY_MAG,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"domain": "pvpc_hourly_pricing",
|
||||
"name": "Spain electricity hourly pricing (PVPC)",
|
||||
"codeowners": ["@azogue"],
|
||||
"codeowners": ["@azogue", "@chiro79"],
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/pvpc_hourly_pricing",
|
||||
"integration_type": "service",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["aiopvpc"],
|
||||
"requirements": ["aiopvpc==4.3.1"]
|
||||
"loggers": ["esios_api"],
|
||||
"requirements": ["esios_api==4.4.0"]
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ from datetime import datetime
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from aiopvpc.const import KEY_INJECTION, KEY_MAG, KEY_OMIE, KEY_PVPC
|
||||
from esios_api.const import KEY_INJECTION, KEY_MAG, KEY_OMIE, KEY_PVPC
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
SensorEntity,
|
||||
|
||||
@@ -6,22 +6,24 @@ 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
|
||||
from homeassistant.util.hass_dict import HassKey
|
||||
|
||||
from .const import DOMAIN
|
||||
from .const import DATA_COMPONENT, DOMAIN
|
||||
from .entity import (
|
||||
RadioFrequencyTransmitterEntity,
|
||||
RadioFrequencyTransmitterEntityDescription,
|
||||
)
|
||||
from .helpers import RadioFrequencyTransmitterConsumerEntity, async_send_command
|
||||
|
||||
__all__ = [
|
||||
"DOMAIN",
|
||||
"ModulationType",
|
||||
"RadioFrequencyCommand",
|
||||
"RadioFrequencyTransmitterConsumerEntity",
|
||||
"RadioFrequencyTransmitterEntity",
|
||||
"RadioFrequencyTransmitterEntityDescription",
|
||||
"async_get_transmitters",
|
||||
@@ -30,9 +32,6 @@ __all__ = [
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DATA_COMPONENT: HassKey[EntityComponent[RadioFrequencyTransmitterEntity]] = HassKey(
|
||||
DOMAIN
|
||||
)
|
||||
ENTITY_ID_FORMAT = DOMAIN + ".{}"
|
||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA
|
||||
PLATFORM_SCHEMA_BASE = cv.PLATFORM_SCHEMA_BASE
|
||||
@@ -95,60 +94,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)
|
||||
|
||||
@@ -2,4 +2,12 @@
|
||||
|
||||
from typing import Final
|
||||
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.util.hass_dict import HassKey
|
||||
|
||||
from .entity import RadioFrequencyTransmitterEntity
|
||||
|
||||
DOMAIN: Final = "radio_frequency"
|
||||
DATA_COMPONENT: HassKey[EntityComponent[RadioFrequencyTransmitterEntity]] = HassKey(
|
||||
DOMAIN
|
||||
)
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
"""Helper base entities for integrations that consume RF transmitters."""
|
||||
|
||||
import logging
|
||||
|
||||
from rf_protocols import RadioFrequencyCommand
|
||||
|
||||
from homeassistant.const import STATE_UNAVAILABLE
|
||||
from homeassistant.core import (
|
||||
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_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: str
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Subscribe to RF entity state changes."""
|
||||
await super().async_added_to_hass()
|
||||
|
||||
# Resolve UUID to entity ID if needed
|
||||
self._rf_transmitter_entity_id = er.async_validate_entity_id(
|
||||
er.async_get(self.hass), self._rf_transmitter_entity_id
|
||||
)
|
||||
|
||||
self.async_on_remove(
|
||||
async_track_state_change_event(
|
||||
self.hass,
|
||||
[self._rf_transmitter_entity_id],
|
||||
self._async_rf_state_changed,
|
||||
)
|
||||
)
|
||||
|
||||
# Set initial availability based on current RF entity state
|
||||
rf_state = self.hass.states.get(self._rf_transmitter_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, command, context=self._context
|
||||
)
|
||||
|
||||
@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",
|
||||
self._rf_transmitter_entity_id,
|
||||
self.entity_id,
|
||||
"available" if rf_available else "unavailable",
|
||||
)
|
||||
|
||||
self._attr_available = rf_available
|
||||
self.async_write_ha_state()
|
||||
Generated
+3
-3
@@ -374,9 +374,6 @@ aiopurpleair==2025.08.1
|
||||
# homeassistant.components.hunterdouglas_powerview
|
||||
aiopvapi==3.3.0
|
||||
|
||||
# homeassistant.components.pvpc_hourly_pricing
|
||||
aiopvpc==4.3.1
|
||||
|
||||
# homeassistant.components.lidarr
|
||||
# homeassistant.components.radarr
|
||||
# homeassistant.components.sonarr
|
||||
@@ -951,6 +948,9 @@ epson-projector==0.6.0
|
||||
# homeassistant.components.eq3btsmart
|
||||
eq3btsmart==2.3.0
|
||||
|
||||
# homeassistant.components.pvpc_hourly_pricing
|
||||
esios_api==4.4.0
|
||||
|
||||
# homeassistant.components.esphome
|
||||
esphome-dashboard-api==1.3.0
|
||||
|
||||
|
||||
@@ -203,7 +203,6 @@ FORBIDDEN_PACKAGE_EXCEPTIONS: dict[str, dict[str, set[str]]] = {
|
||||
"opengarage": {"open-garage": {"async-timeout"}},
|
||||
"overkiz": {"pyoverkiz": {"backoff"}},
|
||||
"prosegur": {"pyprosegur": {"backoff"}},
|
||||
"pvpc_hourly_pricing": {"aiopvpc": {"async-timeout"}},
|
||||
"radio_browser": {"radios": {"backoff"}},
|
||||
"remote_rpi_gpio": {
|
||||
# https://github.com/waveform80/colorzero/issues/9
|
||||
|
||||
@@ -22,7 +22,6 @@ from homeassistant.components.music_assistant.config_flow import (
|
||||
)
|
||||
from homeassistant.components.music_assistant.const import (
|
||||
AUTH_SCHEMA_VERSION,
|
||||
CONF_TOKEN,
|
||||
DEFAULT_NAME,
|
||||
DOMAIN,
|
||||
)
|
||||
@@ -34,6 +33,7 @@ from homeassistant.config_entries import (
|
||||
SOURCE_ZEROCONF,
|
||||
ConfigEntryState,
|
||||
)
|
||||
from homeassistant.const import CONF_TOKEN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
from homeassistant.helpers.service_info.hassio import HassioServiceInfo
|
||||
|
||||
Reference in New Issue
Block a user