mirror of
https://github.com/home-assistant/core.git
synced 2026-04-14 21:56:16 +02:00
Compare commits
54 Commits
adjust_dep
...
flic2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d292aa2e90 | ||
|
|
87f44a67be | ||
|
|
efb0e80577 | ||
|
|
11c34c7ddf | ||
|
|
4b820a0204 | ||
|
|
0c98f01b07 | ||
|
|
aa50822a82 | ||
|
|
f634525798 | ||
|
|
047500af42 | ||
|
|
db589f7318 | ||
|
|
3ea15f2743 | ||
|
|
8e430d9f26 | ||
|
|
075b47b5f9 | ||
|
|
949c907407 | ||
|
|
326799209c | ||
|
|
65bc7c9ea7 | ||
|
|
86d443f8c6 | ||
|
|
19ae7e722e | ||
|
|
57568fdc2c | ||
|
|
4c8a660b2d | ||
|
|
b0511519a1 | ||
|
|
038b583888 | ||
|
|
018c130988 | ||
|
|
462e9965d7 | ||
|
|
ea4d85f96c | ||
|
|
1a4d518ef2 | ||
|
|
a48a770ca4 | ||
|
|
e4aeee9d85 | ||
|
|
726edf3a3b | ||
|
|
b98aa0ad91 | ||
|
|
f82b8cb7c7 | ||
|
|
d6342d51cc | ||
|
|
1eead15c24 | ||
|
|
2e6137325c | ||
|
|
8d3d4a1b5c | ||
|
|
3e5132bf85 | ||
|
|
65e4b26006 | ||
|
|
13f1a42d69 | ||
|
|
5be48affcf | ||
|
|
8994f501f1 | ||
|
|
7f49ecffd3 | ||
|
|
a560967861 | ||
|
|
82202ee1c2 | ||
|
|
b697b3a54e | ||
|
|
6cf5bbe2f5 | ||
|
|
c0c61533e6 | ||
|
|
15e434431d | ||
|
|
0452bb91c7 | ||
|
|
5620fc9e96 | ||
|
|
1a5ef199da | ||
|
|
e98eec113e | ||
|
|
c74d4047d8 | ||
|
|
f5ae250720 | ||
|
|
bea4eea871 |
2
.github/workflows/builder.yml
vendored
2
.github/workflows/builder.yml
vendored
@@ -499,7 +499,7 @@ jobs:
|
||||
python -m build
|
||||
|
||||
- name: Upload package to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
|
||||
uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
|
||||
with:
|
||||
skip-existing: true
|
||||
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
"""The Actron Air integration."""
|
||||
|
||||
from actron_neo_api import (
|
||||
ActronAirACSystem,
|
||||
ActronAirAPI,
|
||||
ActronAirAPIError,
|
||||
ActronAirAuthError,
|
||||
)
|
||||
from actron_neo_api import ActronAirAPI, ActronAirAPIError, ActronAirAuthError
|
||||
from actron_neo_api.models.system import ActronAirSystemInfo
|
||||
|
||||
from homeassistant.const import CONF_API_TOKEN, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
@@ -25,7 +21,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ActronAirConfigEntry) ->
|
||||
"""Set up Actron Air integration from a config entry."""
|
||||
|
||||
api = ActronAirAPI(refresh_token=entry.data[CONF_API_TOKEN])
|
||||
systems: list[ActronAirACSystem] = []
|
||||
systems: list[ActronAirSystemInfo] = []
|
||||
|
||||
try:
|
||||
systems = await api.get_ac_systems()
|
||||
@@ -44,9 +40,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ActronAirConfigEntry) ->
|
||||
system_coordinators: dict[str, ActronAirSystemCoordinator] = {}
|
||||
for system in systems:
|
||||
coordinator = ActronAirSystemCoordinator(hass, entry, api, system)
|
||||
_LOGGER.debug("Setting up coordinator for system: %s", system["serial"])
|
||||
_LOGGER.debug("Setting up coordinator for system: %s", system.serial)
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
system_coordinators[system["serial"]] = coordinator
|
||||
system_coordinators[system.serial] = coordinator
|
||||
|
||||
entry.runtime_data = ActronAirRuntimeData(
|
||||
api=api,
|
||||
|
||||
@@ -18,7 +18,7 @@ from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .coordinator import ActronAirConfigEntry, ActronAirSystemCoordinator
|
||||
from .entity import ActronAirAcEntity, ActronAirZoneEntity, handle_actron_api_errors
|
||||
from .entity import ActronAirAcEntity, ActronAirZoneEntity, actron_air_command
|
||||
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
@@ -136,19 +136,19 @@ class ActronSystemClimate(ActronAirAcEntity, ActronAirClimateEntity):
|
||||
"""Return the target temperature."""
|
||||
return self._status.user_aircon_settings.temperature_setpoint_cool_c
|
||||
|
||||
@handle_actron_api_errors
|
||||
@actron_air_command
|
||||
async def async_set_fan_mode(self, fan_mode: str) -> None:
|
||||
"""Set a new fan mode."""
|
||||
api_fan_mode = FAN_MODE_MAPPING_HA_TO_ACTRONAIR.get(fan_mode)
|
||||
await self._status.user_aircon_settings.set_fan_mode(api_fan_mode)
|
||||
|
||||
@handle_actron_api_errors
|
||||
@actron_air_command
|
||||
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
|
||||
"""Set the HVAC mode."""
|
||||
ac_mode = HVAC_MODE_MAPPING_HA_TO_ACTRONAIR.get(hvac_mode)
|
||||
await self._status.ac_system.set_system_mode(ac_mode)
|
||||
|
||||
@handle_actron_api_errors
|
||||
@actron_air_command
|
||||
async def async_set_temperature(self, **kwargs: Any) -> None:
|
||||
"""Set the temperature."""
|
||||
temp = kwargs.get(ATTR_TEMPERATURE)
|
||||
@@ -212,13 +212,13 @@ class ActronZoneClimate(ActronAirZoneEntity, ActronAirClimateEntity):
|
||||
"""Return the target temperature."""
|
||||
return self._zone.temperature_setpoint_cool_c
|
||||
|
||||
@handle_actron_api_errors
|
||||
@actron_air_command
|
||||
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
|
||||
"""Set the HVAC mode."""
|
||||
is_enabled = hvac_mode != HVACMode.OFF
|
||||
await self._zone.enable(is_enabled)
|
||||
|
||||
@handle_actron_api_errors
|
||||
@actron_air_command
|
||||
async def async_set_temperature(self, **kwargs: Any) -> None:
|
||||
"""Set the temperature."""
|
||||
await self._zone.set_temperature(temperature=kwargs.get(ATTR_TEMPERATURE))
|
||||
|
||||
@@ -38,10 +38,10 @@ class ActronAirConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
_LOGGER.error("OAuth2 flow failed: %s", err)
|
||||
return self.async_abort(reason="oauth2_error")
|
||||
|
||||
self._device_code = device_code_response["device_code"]
|
||||
self._user_code = device_code_response["user_code"]
|
||||
self._verification_uri = device_code_response["verification_uri_complete"]
|
||||
self._expires_minutes = str(device_code_response["expires_in"] // 60)
|
||||
self._device_code = device_code_response.device_code
|
||||
self._user_code = device_code_response.user_code
|
||||
self._verification_uri = device_code_response.verification_uri_complete
|
||||
self._expires_minutes = str(device_code_response.expires_in // 60)
|
||||
|
||||
async def _wait_for_authorization() -> None:
|
||||
"""Wait for the user to authorize the device."""
|
||||
|
||||
@@ -6,12 +6,12 @@ from dataclasses import dataclass
|
||||
from datetime import timedelta
|
||||
|
||||
from actron_neo_api import (
|
||||
ActronAirACSystem,
|
||||
ActronAirAPI,
|
||||
ActronAirAPIError,
|
||||
ActronAirAuthError,
|
||||
ActronAirStatus,
|
||||
)
|
||||
from actron_neo_api.models.system import ActronAirSystemInfo
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
@@ -38,7 +38,7 @@ class ActronAirRuntimeData:
|
||||
type ActronAirConfigEntry = ConfigEntry[ActronAirRuntimeData]
|
||||
|
||||
|
||||
class ActronAirSystemCoordinator(DataUpdateCoordinator[ActronAirACSystem]):
|
||||
class ActronAirSystemCoordinator(DataUpdateCoordinator[ActronAirStatus]):
|
||||
"""System coordinator for Actron Air integration."""
|
||||
|
||||
def __init__(
|
||||
@@ -46,7 +46,7 @@ class ActronAirSystemCoordinator(DataUpdateCoordinator[ActronAirACSystem]):
|
||||
hass: HomeAssistant,
|
||||
entry: ActronAirConfigEntry,
|
||||
api: ActronAirAPI,
|
||||
system: ActronAirACSystem,
|
||||
system: ActronAirSystemInfo,
|
||||
) -> None:
|
||||
"""Initialize the coordinator."""
|
||||
super().__init__(
|
||||
@@ -57,7 +57,7 @@ class ActronAirSystemCoordinator(DataUpdateCoordinator[ActronAirACSystem]):
|
||||
config_entry=entry,
|
||||
)
|
||||
self.system = system
|
||||
self.serial_number = system["serial"]
|
||||
self.serial_number = system.serial
|
||||
self.api = api
|
||||
self.status = self.api.state_manager.get_status(self.serial_number)
|
||||
self.last_seen = dt_util.utcnow()
|
||||
|
||||
35
homeassistant/components/actron_air/diagnostics.py
Normal file
35
homeassistant/components/actron_air/diagnostics.py
Normal file
@@ -0,0 +1,35 @@
|
||||
"""Diagnostics support for Actron Air."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.diagnostics import async_redact_data
|
||||
from homeassistant.const import CONF_API_TOKEN
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .coordinator import ActronAirConfigEntry
|
||||
|
||||
TO_REDACT = {CONF_API_TOKEN, "master_serial", "serial_number", "serial"}
|
||||
|
||||
|
||||
async def async_get_config_entry_diagnostics(
|
||||
hass: HomeAssistant,
|
||||
entry: ActronAirConfigEntry,
|
||||
) -> dict[str, Any]:
|
||||
"""Return diagnostics for a config entry."""
|
||||
coordinators: dict[int, Any] = {}
|
||||
for idx, coordinator in enumerate(entry.runtime_data.system_coordinators.values()):
|
||||
coordinators[idx] = {
|
||||
"system": async_redact_data(
|
||||
coordinator.system.model_dump(mode="json"), TO_REDACT
|
||||
),
|
||||
"status": async_redact_data(
|
||||
coordinator.data.model_dump(mode="json", exclude={"last_known_state"}),
|
||||
TO_REDACT,
|
||||
),
|
||||
}
|
||||
return {
|
||||
"entry_data": async_redact_data(entry.data, TO_REDACT),
|
||||
"coordinators": coordinators,
|
||||
}
|
||||
@@ -14,10 +14,14 @@ from .const import DOMAIN
|
||||
from .coordinator import ActronAirSystemCoordinator
|
||||
|
||||
|
||||
def handle_actron_api_errors[_EntityT: ActronAirEntity, **_P](
|
||||
def actron_air_command[_EntityT: ActronAirEntity, **_P](
|
||||
func: Callable[Concatenate[_EntityT, _P], Coroutine[Any, Any, Any]],
|
||||
) -> Callable[Concatenate[_EntityT, _P], Coroutine[Any, Any, None]]:
|
||||
"""Decorate Actron Air API calls to handle ActronAirAPIError exceptions."""
|
||||
"""Decorator for Actron Air API calls.
|
||||
|
||||
Handles ActronAirAPIError exceptions, and requests a coordinator update
|
||||
to update the status of the devices as soon as possible.
|
||||
"""
|
||||
|
||||
@wraps(func)
|
||||
async def wrapper(self: _EntityT, *args: _P.args, **kwargs: _P.kwargs) -> None:
|
||||
@@ -30,6 +34,7 @@ def handle_actron_api_errors[_EntityT: ActronAirEntity, **_P](
|
||||
translation_key="api_error",
|
||||
translation_placeholders={"error": str(err)},
|
||||
) from err
|
||||
self.coordinator.async_set_updated_data(self.coordinator.data)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
@@ -13,5 +13,5 @@
|
||||
"integration_type": "hub",
|
||||
"iot_class": "cloud_polling",
|
||||
"quality_scale": "silver",
|
||||
"requirements": ["actron-neo-api==0.4.1"]
|
||||
"requirements": ["actron-neo-api==0.5.0"]
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ rules:
|
||||
|
||||
# Gold
|
||||
devices: done
|
||||
diagnostics: todo
|
||||
diagnostics: done
|
||||
discovery-update-info:
|
||||
status: exempt
|
||||
comment: This integration uses DHCP discovery, however is cloud polling. Therefore there is no information to update.
|
||||
|
||||
@@ -10,7 +10,7 @@ from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .coordinator import ActronAirConfigEntry, ActronAirSystemCoordinator
|
||||
from .entity import ActronAirAcEntity, handle_actron_api_errors
|
||||
from .entity import ActronAirAcEntity, actron_air_command
|
||||
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
@@ -105,12 +105,12 @@ class ActronAirSwitch(ActronAirAcEntity, SwitchEntity):
|
||||
"""Return true if the switch is on."""
|
||||
return self.entity_description.is_on_fn(self.coordinator)
|
||||
|
||||
@handle_actron_api_errors
|
||||
@actron_air_command
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn the switch on."""
|
||||
await self.entity_description.set_fn(self.coordinator, True)
|
||||
|
||||
@handle_actron_api_errors
|
||||
@actron_air_command
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn the switch off."""
|
||||
await self.entity_description.set_fn(self.coordinator, False)
|
||||
|
||||
@@ -5,7 +5,6 @@ from __future__ import annotations
|
||||
from collections.abc import Mapping
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
from typing import TYPE_CHECKING, Any, cast
|
||||
|
||||
import anthropic
|
||||
@@ -71,6 +70,7 @@ from .const import (
|
||||
WEB_SEARCH_UNSUPPORTED_MODELS,
|
||||
PromptCaching,
|
||||
)
|
||||
from .coordinator import model_alias
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from . import AnthropicConfigEntry
|
||||
@@ -112,25 +112,13 @@ async def get_model_list(client: anthropic.AsyncAnthropic) -> list[SelectOptionD
|
||||
except anthropic.AnthropicError:
|
||||
models = []
|
||||
_LOGGER.debug("Available models: %s", models)
|
||||
model_options: list[SelectOptionDict] = []
|
||||
short_form = re.compile(r"[^\d]-\d$")
|
||||
for model_info in models:
|
||||
# Resolve alias from versioned model name:
|
||||
model_alias = (
|
||||
model_info.id[:-9]
|
||||
if model_info.id != "claude-3-haiku-20240307"
|
||||
and model_info.id[-2:-1] != "-"
|
||||
else model_info.id
|
||||
return [
|
||||
SelectOptionDict(
|
||||
label=model_info.display_name,
|
||||
value=model_alias(model_info.id),
|
||||
)
|
||||
if short_form.search(model_alias):
|
||||
model_alias += "-0"
|
||||
model_options.append(
|
||||
SelectOptionDict(
|
||||
label=model_info.display_name,
|
||||
value=model_alias,
|
||||
)
|
||||
)
|
||||
return model_options
|
||||
for model_info in models
|
||||
]
|
||||
|
||||
|
||||
class AnthropicConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import timedelta
|
||||
import datetime
|
||||
import re
|
||||
|
||||
import anthropic
|
||||
|
||||
@@ -15,13 +16,28 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda
|
||||
|
||||
from .const import DOMAIN, LOGGER
|
||||
|
||||
UPDATE_INTERVAL_CONNECTED = timedelta(hours=12)
|
||||
UPDATE_INTERVAL_DISCONNECTED = timedelta(minutes=1)
|
||||
UPDATE_INTERVAL_CONNECTED = datetime.timedelta(hours=12)
|
||||
UPDATE_INTERVAL_DISCONNECTED = datetime.timedelta(minutes=1)
|
||||
|
||||
type AnthropicConfigEntry = ConfigEntry[AnthropicCoordinator]
|
||||
|
||||
|
||||
class AnthropicCoordinator(DataUpdateCoordinator[None]):
|
||||
_model_short_form = re.compile(r"[^\d]-\d$")
|
||||
|
||||
|
||||
@callback
|
||||
def model_alias(model_id: str) -> str:
|
||||
"""Resolve alias from versioned model name."""
|
||||
if model_id == "claude-3-haiku-20240307" or model_id.endswith("-preview"):
|
||||
return model_id
|
||||
if model_id[-2:-1] != "-":
|
||||
model_id = model_id[:-9]
|
||||
if _model_short_form.search(model_id):
|
||||
return model_id + "-0"
|
||||
return model_id
|
||||
|
||||
|
||||
class AnthropicCoordinator(DataUpdateCoordinator[list[anthropic.types.ModelInfo]]):
|
||||
"""DataUpdateCoordinator which uses different intervals after successful and unsuccessful updates."""
|
||||
|
||||
client: anthropic.AsyncAnthropic
|
||||
@@ -42,16 +58,16 @@ class AnthropicCoordinator(DataUpdateCoordinator[None]):
|
||||
)
|
||||
|
||||
@callback
|
||||
def async_set_updated_data(self, data: None) -> None:
|
||||
def async_set_updated_data(self, data: list[anthropic.types.ModelInfo]) -> None:
|
||||
"""Manually update data, notify listeners and update refresh interval."""
|
||||
self.update_interval = UPDATE_INTERVAL_CONNECTED
|
||||
super().async_set_updated_data(data)
|
||||
|
||||
async def async_update_data(self) -> None:
|
||||
async def async_update_data(self) -> list[anthropic.types.ModelInfo]:
|
||||
"""Fetch data from the API."""
|
||||
try:
|
||||
self.update_interval = UPDATE_INTERVAL_DISCONNECTED
|
||||
await self.client.models.list(timeout=10.0)
|
||||
result = await self.client.models.list(timeout=10.0)
|
||||
self.update_interval = UPDATE_INTERVAL_CONNECTED
|
||||
except anthropic.APITimeoutError as err:
|
||||
raise TimeoutError(err.message or str(err)) from err
|
||||
@@ -67,6 +83,7 @@ class AnthropicCoordinator(DataUpdateCoordinator[None]):
|
||||
translation_key="api_error",
|
||||
translation_placeholders={"message": err.message},
|
||||
) from err
|
||||
return result.data
|
||||
|
||||
def mark_connection_error(self) -> None:
|
||||
"""Mark the connection as having an error and reschedule background check."""
|
||||
@@ -76,3 +93,23 @@ class AnthropicCoordinator(DataUpdateCoordinator[None]):
|
||||
self.async_update_listeners()
|
||||
if self._listeners and not self.hass.is_stopping:
|
||||
self._schedule_refresh()
|
||||
|
||||
@callback
|
||||
def get_model_info(self, model_id: str) -> anthropic.types.ModelInfo:
|
||||
"""Get model info for a given model ID."""
|
||||
# First try: exact name match
|
||||
for model in self.data or []:
|
||||
if model.id == model_id:
|
||||
return model
|
||||
# Second try: match by alias
|
||||
alias = model_alias(model_id)
|
||||
for model in self.data or []:
|
||||
if model_alias(model.id) == alias:
|
||||
return model
|
||||
# Model not found, return safe defaults
|
||||
return anthropic.types.ModelInfo(
|
||||
type="model",
|
||||
id=model_id,
|
||||
created_at=datetime.datetime(1970, 1, 1, tzinfo=datetime.UTC),
|
||||
display_name=model_id,
|
||||
)
|
||||
|
||||
@@ -689,12 +689,17 @@ class AnthropicBaseLLMEntity(CoordinatorEntity[AnthropicCoordinator]):
|
||||
super().__init__(entry.runtime_data)
|
||||
self.entry = entry
|
||||
self.subentry = subentry
|
||||
coordinator = entry.runtime_data
|
||||
self.model_info = coordinator.get_model_info(
|
||||
subentry.data.get(CONF_CHAT_MODEL, DEFAULT[CONF_CHAT_MODEL])
|
||||
)
|
||||
self._attr_unique_id = subentry.subentry_id
|
||||
self._attr_device_info = dr.DeviceInfo(
|
||||
identifiers={(DOMAIN, subentry.subentry_id)},
|
||||
name=subentry.title,
|
||||
manufacturer="Anthropic",
|
||||
model=subentry.data.get(CONF_CHAT_MODEL, DEFAULT[CONF_CHAT_MODEL]),
|
||||
model=self.model_info.display_name,
|
||||
model_id=self.model_info.id,
|
||||
entry_type=dr.DeviceEntryType.SERVICE,
|
||||
)
|
||||
|
||||
@@ -969,7 +974,7 @@ class AnthropicBaseLLMEntity(CoordinatorEntity[AnthropicCoordinator]):
|
||||
) from err
|
||||
except anthropic.AnthropicError as err:
|
||||
# Non-connection error, mark connection as healthy
|
||||
coordinator.async_set_updated_data(None)
|
||||
coordinator.async_set_updated_data(coordinator.data)
|
||||
LOGGER.error("Error while talking to Anthropic: %s", err)
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
@@ -982,7 +987,7 @@ class AnthropicBaseLLMEntity(CoordinatorEntity[AnthropicCoordinator]):
|
||||
) from err
|
||||
|
||||
if not chat_log.unresponded_tool_results:
|
||||
coordinator.async_set_updated_data(None)
|
||||
coordinator.async_set_updated_data(coordinator.data)
|
||||
break
|
||||
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["brother", "pyasn1", "pysmi", "pysnmp"],
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["brother==6.0.0"],
|
||||
"requirements": ["brother==6.1.0"],
|
||||
"zeroconf": [
|
||||
{
|
||||
"name": "brother*",
|
||||
|
||||
@@ -112,7 +112,7 @@ class ComelitAlarmEntity(
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return True if alarm is available."""
|
||||
if self._area.human_status in [AlarmAreaState.ANOMALY, AlarmAreaState.UNKNOWN]:
|
||||
if self._area.human_status == AlarmAreaState.UNKNOWN:
|
||||
return False
|
||||
return super().available
|
||||
|
||||
@@ -151,7 +151,7 @@ class ComelitAlarmEntity(
|
||||
if code != str(self.coordinator.api.device_pin):
|
||||
return
|
||||
await self.coordinator.api.set_zone_status(
|
||||
self._area.index, ALARM_ACTIONS[DISABLE]
|
||||
self._area.index, ALARM_ACTIONS[DISABLE], self._area.anomaly
|
||||
)
|
||||
await self._async_update_state(
|
||||
AlarmAreaState.DISARMED, ALARM_AREA_ARMED_STATUS[DISABLE]
|
||||
@@ -160,7 +160,7 @@ class ComelitAlarmEntity(
|
||||
async def async_alarm_arm_away(self, code: str | None = None) -> None:
|
||||
"""Send arm away command."""
|
||||
await self.coordinator.api.set_zone_status(
|
||||
self._area.index, ALARM_ACTIONS[AWAY]
|
||||
self._area.index, ALARM_ACTIONS[AWAY], self._area.anomaly
|
||||
)
|
||||
await self._async_update_state(
|
||||
AlarmAreaState.ARMED, ALARM_AREA_ARMED_STATUS[AWAY]
|
||||
@@ -169,7 +169,7 @@ class ComelitAlarmEntity(
|
||||
async def async_alarm_arm_home(self, code: str | None = None) -> None:
|
||||
"""Send arm home command."""
|
||||
await self.coordinator.api.set_zone_status(
|
||||
self._area.index, ALARM_ACTIONS[HOME]
|
||||
self._area.index, ALARM_ACTIONS[HOME], self._area.anomaly
|
||||
)
|
||||
await self._async_update_state(
|
||||
AlarmAreaState.ARMED, ALARM_AREA_ARMED_STATUS[HOME_P1]
|
||||
@@ -178,7 +178,7 @@ class ComelitAlarmEntity(
|
||||
async def async_alarm_arm_night(self, code: str | None = None) -> None:
|
||||
"""Send arm night command."""
|
||||
await self.coordinator.api.set_zone_status(
|
||||
self._area.index, ALARM_ACTIONS[NIGHT]
|
||||
self._area.index, ALARM_ACTIONS[NIGHT], self._area.anomaly
|
||||
)
|
||||
await self._async_update_state(
|
||||
AlarmAreaState.ARMED, ALARM_AREA_ARMED_STATUS[NIGHT]
|
||||
|
||||
@@ -20,7 +20,7 @@ from homeassistant.helpers.typing import ConfigType
|
||||
from homeassistant.util import dt as dt_util
|
||||
from homeassistant.util.hass_dict import HassKey
|
||||
|
||||
from .const import ATTR_EVENT_TYPE, ATTR_EVENT_TYPES, DOMAIN
|
||||
from .const import ATTR_EVENT_TYPE, ATTR_EVENT_TYPES, DOMAIN, DoorbellEventType
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
DATA_COMPONENT: HassKey[EntityComponent[EventEntity]] = HassKey(DOMAIN)
|
||||
@@ -44,6 +44,7 @@ __all__ = [
|
||||
"DOMAIN",
|
||||
"PLATFORM_SCHEMA",
|
||||
"PLATFORM_SCHEMA_BASE",
|
||||
"DoorbellEventType",
|
||||
"EventDeviceClass",
|
||||
"EventEntity",
|
||||
"EventEntityDescription",
|
||||
@@ -189,6 +190,21 @@ class EventEntity(RestoreEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_)
|
||||
async def async_internal_added_to_hass(self) -> None:
|
||||
"""Call when the event entity is added to hass."""
|
||||
await super().async_internal_added_to_hass()
|
||||
|
||||
if (
|
||||
self.device_class == EventDeviceClass.DOORBELL
|
||||
and DoorbellEventType.RING not in self.event_types
|
||||
):
|
||||
report_issue = self._suggest_report_issue()
|
||||
_LOGGER.warning(
|
||||
"Entity %s is a doorbell event entity but does not support "
|
||||
"the '%s' event type. This will stop working in "
|
||||
"Home Assistant 2027.4, please %s",
|
||||
self.entity_id,
|
||||
DoorbellEventType.RING,
|
||||
report_issue,
|
||||
)
|
||||
|
||||
if (
|
||||
(state := await self.async_get_last_state())
|
||||
and state.state is not None
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
"""Provides the constants needed for the component."""
|
||||
|
||||
from enum import StrEnum
|
||||
|
||||
DOMAIN = "event"
|
||||
ATTR_EVENT_TYPE = "event_type"
|
||||
ATTR_EVENT_TYPES = "event_types"
|
||||
|
||||
|
||||
class DoorbellEventType(StrEnum):
|
||||
"""Standard event types for doorbell device class."""
|
||||
|
||||
RING = "ring"
|
||||
|
||||
@@ -15,7 +15,14 @@
|
||||
"name": "Button"
|
||||
},
|
||||
"doorbell": {
|
||||
"name": "Doorbell"
|
||||
"name": "Doorbell",
|
||||
"state_attributes": {
|
||||
"event_type": {
|
||||
"state": {
|
||||
"ring": "Ring"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"motion": {
|
||||
"name": "Motion"
|
||||
|
||||
@@ -2,18 +2,14 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_API_KEY, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .coordinator import FlussDataUpdateCoordinator
|
||||
from .coordinator import FlussConfigEntry, FlussDataUpdateCoordinator
|
||||
|
||||
PLATFORMS: list[Platform] = [Platform.BUTTON]
|
||||
|
||||
|
||||
type FlussConfigEntry = ConfigEntry[FlussDataUpdateCoordinator]
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: FlussConfigEntry,
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
"""Support for Fluss Devices."""
|
||||
|
||||
from homeassistant.components.button import ButtonEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .coordinator import FlussApiClientError, FlussDataUpdateCoordinator
|
||||
from .coordinator import FlussApiClientError, FlussConfigEntry
|
||||
from .entity import FlussEntity
|
||||
|
||||
type FlussConfigEntry = ConfigEntry[FlussDataUpdateCoordinator]
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
|
||||
@@ -453,10 +453,13 @@ class FritzBoxTools(DataUpdateCoordinator[UpdateCoordinatorDataType]):
|
||||
if not attributes.get("MACAddress"):
|
||||
continue
|
||||
|
||||
wan_access_result = None
|
||||
if (wan_access := attributes.get("X_AVM-DE_WANAccess")) is not None:
|
||||
wan_access_result = "granted" in wan_access
|
||||
else:
|
||||
wan_access_result = None
|
||||
# wan_access can be "granted", "denied", "unknown" or "error"
|
||||
if "granted" in wan_access:
|
||||
wan_access_result = True
|
||||
elif "denied" in wan_access:
|
||||
wan_access_result = False
|
||||
|
||||
hosts[attributes["MACAddress"]] = Device(
|
||||
name=attributes["HostName"],
|
||||
|
||||
@@ -11,4 +11,8 @@ CONF_LISTENING_PORT_DEFAULT = 4002
|
||||
CONF_DISCOVERY_INTERVAL_DEFAULT = 60
|
||||
|
||||
SCAN_INTERVAL = timedelta(seconds=30)
|
||||
# A device is considered unavailable if we have not heard a status response
|
||||
# from it for three consecutive poll cycles. This tolerates a single dropped
|
||||
# UDP response plus some jitter before flapping the entity state.
|
||||
DEVICE_TIMEOUT = SCAN_INTERVAL * 3
|
||||
DISCOVERY_TIMEOUT = 5
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
@@ -22,7 +23,7 @@ from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import DOMAIN, MANUFACTURER
|
||||
from .const import DEVICE_TIMEOUT, DOMAIN, MANUFACTURER
|
||||
from .coordinator import GoveeLocalApiCoordinator, GoveeLocalConfigEntry
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -118,6 +119,19 @@ class GoveeLight(CoordinatorEntity[GoveeLocalApiCoordinator], LightEntity):
|
||||
serial_number=device.fingerprint,
|
||||
)
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return if the device is reachable.
|
||||
|
||||
The underlying library updates ``lastseen`` whenever the device
|
||||
replies to a status request. The coordinator polls every
|
||||
``SCAN_INTERVAL``, so if we have not heard back within
|
||||
``DEVICE_TIMEOUT`` we consider the device offline.
|
||||
"""
|
||||
if not super().available:
|
||||
return False
|
||||
return datetime.now() - self._device.lastseen < DEVICE_TIMEOUT
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Return true if device is on (brightness above 0)."""
|
||||
@@ -205,8 +219,8 @@ class GoveeLight(CoordinatorEntity[GoveeLocalApiCoordinator], LightEntity):
|
||||
|
||||
@callback
|
||||
def _update_callback(self, device: GoveeDevice) -> None:
|
||||
if self.hass:
|
||||
self.async_write_ha_state()
|
||||
"""Handle device state updates pushed by the library."""
|
||||
self.async_write_ha_state()
|
||||
|
||||
def _save_last_color_state(self) -> None:
|
||||
color_mode = self.color_mode
|
||||
|
||||
@@ -98,7 +98,9 @@ class HabiticaCalendarEntity(HabiticaBase, CalendarEntity):
|
||||
start_date, end_date - timedelta(days=1), inc=True
|
||||
)
|
||||
# if no end_date is given, return only the next recurrence
|
||||
return [recurrences.after(start_date, inc=True)]
|
||||
if (next_date := recurrences.after(start_date, inc=True)) is None:
|
||||
return []
|
||||
return [next_date]
|
||||
|
||||
|
||||
class HabiticaTodosCalendarEntity(HabiticaCalendarEntity):
|
||||
|
||||
@@ -13,7 +13,7 @@ from homeassistant.components.homeassistant_hardware.util import guess_firmware_
|
||||
from homeassistant.components.usb import (
|
||||
USBDevice,
|
||||
async_register_port_event_callback,
|
||||
scan_serial_ports,
|
||||
async_scan_serial_ports,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
@@ -163,7 +163,7 @@ async def async_migrate_entry(
|
||||
key not in config_entry.data
|
||||
for key in (VID, PID, MANUFACTURER, PRODUCT, SERIAL_NUMBER)
|
||||
):
|
||||
serial_ports = await hass.async_add_executor_job(scan_serial_ports)
|
||||
serial_ports = await async_scan_serial_ports(hass)
|
||||
serial_ports_info = {port.device: port for port in serial_ports}
|
||||
device = config_entry.data[DEVICE]
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
"""Provide info to system health."""
|
||||
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components import system_health
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
|
||||
@@ -14,7 +16,7 @@ def async_register(
|
||||
register.async_register_info(system_health_info)
|
||||
|
||||
|
||||
async def system_health_info(hass):
|
||||
async def system_health_info(hass: HomeAssistant) -> dict[str, Any]:
|
||||
"""Get info for the info page."""
|
||||
return {
|
||||
"api_endpoint_reachable": system_health.async_check_can_reach_url(
|
||||
|
||||
@@ -69,7 +69,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: LunatoneConfigEntry) ->
|
||||
"""Set up Lunatone from a config entry."""
|
||||
auth_api = Auth(async_get_clientsession(hass), entry.data[CONF_URL])
|
||||
info_api = Info(auth_api)
|
||||
devices_api = Devices(auth_api)
|
||||
devices_api = Devices(info_api)
|
||||
|
||||
coordinator_info = LunatoneInfoDataUpdateCoordinator(hass, entry, info_api)
|
||||
await coordinator_info.async_config_entry_first_refresh()
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from typing import Any
|
||||
|
||||
from lunatone_rest_api_client import DALIBroadcast
|
||||
@@ -28,7 +27,6 @@ from .coordinator import (
|
||||
)
|
||||
|
||||
PARALLEL_UPDATES = 0
|
||||
STATUS_UPDATE_DELAY = 0.04
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
@@ -149,8 +147,6 @@ class LunatoneLight(
|
||||
)
|
||||
else:
|
||||
await self._device.switch_on()
|
||||
|
||||
await asyncio.sleep(STATUS_UPDATE_DELAY)
|
||||
await self.coordinator.async_refresh()
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
@@ -161,8 +157,6 @@ class LunatoneLight(
|
||||
await self._device.fade_to_brightness(0)
|
||||
else:
|
||||
await self._device.switch_off()
|
||||
|
||||
await asyncio.sleep(STATUS_UPDATE_DELAY)
|
||||
await self.coordinator.async_refresh()
|
||||
|
||||
|
||||
@@ -221,13 +215,9 @@ class LunatoneLineBroadcastLight(
|
||||
await self._broadcast.fade_to_brightness(
|
||||
brightness_to_value(self.BRIGHTNESS_SCALE, kwargs.get(ATTR_BRIGHTNESS, 255))
|
||||
)
|
||||
|
||||
await asyncio.sleep(STATUS_UPDATE_DELAY)
|
||||
await self._coordinator_devices.async_refresh()
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Instruct the line to turn off."""
|
||||
await self._broadcast.fade_to_brightness(0)
|
||||
|
||||
await asyncio.sleep(STATUS_UPDATE_DELAY)
|
||||
await self._coordinator_devices.async_refresh()
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"integration_type": "hub",
|
||||
"iot_class": "local_polling",
|
||||
"quality_scale": "silver",
|
||||
"requirements": ["lunatone-rest-api-client==0.7.0"]
|
||||
"requirements": ["lunatone-rest-api-client==0.9.0"]
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"integration_type": "hub",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["pylutron_caseta"],
|
||||
"requirements": ["pylutron-caseta==0.27.0"],
|
||||
"requirements": ["pylutron-caseta==0.28.0"],
|
||||
"zeroconf": [
|
||||
{
|
||||
"properties": {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
import pymystrom
|
||||
from pymystrom.exceptions import MyStromConnectionError
|
||||
@@ -11,6 +11,7 @@ import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
||||
from homeassistant.const import CONF_HOST, CONF_NAME
|
||||
from homeassistant.helpers.service_info.dhcp import DhcpServiceInfo
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
@@ -31,6 +32,8 @@ class MyStromConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
VERSION = 1
|
||||
|
||||
_host: str | None = None
|
||||
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
@@ -51,3 +54,38 @@ class MyStromConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
schema = self.add_suggested_values_to_schema(STEP_USER_DATA_SCHEMA, user_input)
|
||||
return self.async_show_form(step_id="user", data_schema=schema, errors=errors)
|
||||
|
||||
async def async_step_dhcp(
|
||||
self, discovery_info: DhcpServiceInfo
|
||||
) -> ConfigFlowResult:
|
||||
"""Handle DHCP discovery."""
|
||||
mac_address = discovery_info.macaddress.upper()
|
||||
await self.async_set_unique_id(mac_address)
|
||||
self._abort_if_unique_id_configured(updates={CONF_HOST: discovery_info.ip})
|
||||
|
||||
try:
|
||||
await pymystrom.get_device_info(discovery_info.ip)
|
||||
except MyStromConnectionError:
|
||||
return self.async_abort(reason="cannot_connect")
|
||||
|
||||
self._host = discovery_info.ip
|
||||
self.context["title_placeholders"] = {"host": discovery_info.ip}
|
||||
return await self.async_step_discovery_confirm()
|
||||
|
||||
async def async_step_discovery_confirm(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Handle discovery confirmation."""
|
||||
if user_input is not None:
|
||||
return self.async_create_entry(
|
||||
title=DEFAULT_NAME,
|
||||
data={CONF_HOST: self._host},
|
||||
)
|
||||
|
||||
self._set_confirm_only()
|
||||
if TYPE_CHECKING:
|
||||
assert self._host is not None
|
||||
return self.async_show_form(
|
||||
step_id="discovery_confirm",
|
||||
description_placeholders={CONF_HOST: self._host},
|
||||
)
|
||||
|
||||
@@ -4,6 +4,14 @@
|
||||
"codeowners": ["@fabaff"],
|
||||
"config_flow": true,
|
||||
"dependencies": ["http"],
|
||||
"dhcp": [
|
||||
{
|
||||
"hostname": "mystrom-*"
|
||||
},
|
||||
{
|
||||
"registered_devices": true
|
||||
}
|
||||
],
|
||||
"documentation": "https://www.home-assistant.io/integrations/mystrom",
|
||||
"integration_type": "device",
|
||||
"iot_class": "local_polling",
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
|
||||
},
|
||||
"step": {
|
||||
"discovery_confirm": {
|
||||
"description": "Do you want to set up the myStrom device at {host}?"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "[%key:common::config_flow::data::host%]",
|
||||
|
||||
@@ -8,5 +8,5 @@
|
||||
"integration_type": "hub",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["pybotvac"],
|
||||
"requirements": ["pybotvac==0.0.28"]
|
||||
"requirements": ["pybotvac==0.0.29"]
|
||||
}
|
||||
|
||||
@@ -189,6 +189,14 @@ async def async_migrate_entry(hass: HomeAssistant, entry: ProxmoxConfigEntry) ->
|
||||
# Migration for additional configuration options added to support API tokens
|
||||
if entry.version < 3:
|
||||
data = dict(entry.data)
|
||||
# If CONF_REALM wasn't there yet, extract from username
|
||||
if CONF_REALM not in data:
|
||||
data[CONF_REALM] = DEFAULT_REALM
|
||||
if "@" in data.get(CONF_USERNAME, ""):
|
||||
username, realm = data[CONF_USERNAME].split("@", 1)
|
||||
data[CONF_USERNAME] = username
|
||||
data[CONF_REALM] = realm.lower()
|
||||
|
||||
realm = data[CONF_REALM].lower()
|
||||
|
||||
# If the realm is one of the base providers, set the provider to match the realm.
|
||||
|
||||
@@ -5,21 +5,17 @@ from __future__ import annotations
|
||||
from rabbitair import Client, UdpClient
|
||||
|
||||
from homeassistant.components import zeroconf
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_HOST, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import RabbitAirDataUpdateCoordinator
|
||||
from .coordinator import RabbitAirConfigEntry, RabbitAirDataUpdateCoordinator
|
||||
|
||||
PLATFORMS: list[Platform] = [Platform.FAN]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: RabbitAirConfigEntry) -> bool:
|
||||
"""Set up Rabbit Air from a config entry."""
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
|
||||
host: str = entry.data[CONF_HOST]
|
||||
token: str = entry.data[CONF_ACCESS_TOKEN]
|
||||
|
||||
@@ -30,7 +26,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
hass.data[DOMAIN][entry.entry_id] = coordinator
|
||||
entry.runtime_data = coordinator
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
@@ -39,14 +35,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: RabbitAirConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
|
||||
async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
async def update_listener(hass: HomeAssistant, entry: RabbitAirConfigEntry) -> None:
|
||||
"""Handle options update."""
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
|
||||
@@ -12,6 +12,8 @@ from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.debounce import Debouncer
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||
|
||||
type RabbitAirConfigEntry = ConfigEntry[RabbitAirDataUpdateCoordinator]
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -43,10 +45,10 @@ class RabbitAirDebouncer(Debouncer[Coroutine[Any, Any, None]]):
|
||||
class RabbitAirDataUpdateCoordinator(DataUpdateCoordinator[State]):
|
||||
"""Class to manage fetching data from single endpoint."""
|
||||
|
||||
config_entry: ConfigEntry
|
||||
config_entry: RabbitAirConfigEntry
|
||||
|
||||
def __init__(
|
||||
self, hass: HomeAssistant, config_entry: ConfigEntry, device: Client
|
||||
self, hass: HomeAssistant, config_entry: RabbitAirConfigEntry, device: Client
|
||||
) -> None:
|
||||
"""Initialize global data updater."""
|
||||
self.device = device
|
||||
|
||||
@@ -7,13 +7,12 @@ from typing import Any
|
||||
|
||||
from rabbitair import Model
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_MAC
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import RabbitAirDataUpdateCoordinator
|
||||
from .coordinator import RabbitAirConfigEntry, RabbitAirDataUpdateCoordinator
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -31,7 +30,7 @@ class RabbitAirBaseEntity(CoordinatorEntity[RabbitAirDataUpdateCoordinator]):
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: RabbitAirDataUpdateCoordinator,
|
||||
entry: ConfigEntry,
|
||||
entry: RabbitAirConfigEntry,
|
||||
) -> None:
|
||||
"""Initialize the entity."""
|
||||
super().__init__(coordinator)
|
||||
|
||||
@@ -7,7 +7,6 @@ from typing import Any
|
||||
from rabbitair import Mode, Model, Speed
|
||||
|
||||
from homeassistant.components.fan import FanEntity, FanEntityFeature
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.util.percentage import (
|
||||
@@ -15,8 +14,7 @@ from homeassistant.util.percentage import (
|
||||
percentage_to_ordered_list_item,
|
||||
)
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import RabbitAirDataUpdateCoordinator
|
||||
from .coordinator import RabbitAirConfigEntry, RabbitAirDataUpdateCoordinator
|
||||
from .entity import RabbitAirBaseEntity
|
||||
|
||||
SPEED_LIST = [
|
||||
@@ -40,12 +38,11 @@ PRESET_MODES = {
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: RabbitAirConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up a config entry."""
|
||||
coordinator: RabbitAirDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
async_add_entities([RabbitAirFanEntity(coordinator, entry)])
|
||||
async_add_entities([RabbitAirFanEntity(entry.runtime_data, entry)])
|
||||
|
||||
|
||||
class RabbitAirFanEntity(RabbitAirBaseEntity, FanEntity):
|
||||
@@ -61,7 +58,7 @@ class RabbitAirFanEntity(RabbitAirBaseEntity, FanEntity):
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: RabbitAirDataUpdateCoordinator,
|
||||
entry: ConfigEntry,
|
||||
entry: RabbitAirConfigEntry,
|
||||
) -> None:
|
||||
"""Initialize the entity."""
|
||||
super().__init__(coordinator, entry)
|
||||
|
||||
@@ -8,13 +8,11 @@ from urllib.error import URLError
|
||||
|
||||
from radiotherm.validate import RadiothermTstatError
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_HOST, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import RadioThermUpdateCoordinator
|
||||
from .coordinator import RadioThermConfigEntry, RadioThermUpdateCoordinator
|
||||
from .data import async_get_init_data
|
||||
from .util import async_set_time
|
||||
|
||||
@@ -38,7 +36,7 @@ async def _async_call_or_raise_not_ready[_T](
|
||||
raise ConfigEntryNotReady(msg) from ex
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: RadioThermConfigEntry) -> bool:
|
||||
"""Set up Radio Thermostat from a config entry."""
|
||||
host = entry.data[CONF_HOST]
|
||||
init_coro = async_get_init_data(hass, host)
|
||||
@@ -54,21 +52,20 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
time_coro = async_set_time(hass, init_data.tstat)
|
||||
await _async_call_or_raise_not_ready(time_coro, host)
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
|
||||
entry.runtime_data = coordinator
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
entry.async_on_unload(entry.add_update_listener(_async_update_listener))
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def _async_update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
async def _async_update_listener(
|
||||
hass: HomeAssistant, entry: RadioThermConfigEntry
|
||||
) -> None:
|
||||
"""Handle options update."""
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: RadioThermConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
@@ -17,13 +17,11 @@ from homeassistant.components.climate import (
|
||||
HVACAction,
|
||||
HVACMode,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_HALVES, UnitOfTemperature
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import DOMAIN
|
||||
from .coordinator import RadioThermUpdateCoordinator
|
||||
from .coordinator import RadioThermConfigEntry, RadioThermUpdateCoordinator
|
||||
from .entity import RadioThermostatEntity
|
||||
|
||||
ATTR_FAN_ACTION = "fan_action"
|
||||
@@ -101,12 +99,11 @@ def round_temp(temperature):
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: RadioThermConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up climate for a radiotherm device."""
|
||||
coordinator: RadioThermUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
async_add_entities([RadioThermostat(coordinator)])
|
||||
async_add_entities([RadioThermostat(entry.runtime_data)])
|
||||
|
||||
|
||||
class RadioThermostat(RadioThermostatEntity, ClimateEntity):
|
||||
|
||||
@@ -14,6 +14,8 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda
|
||||
|
||||
from .data import RadioThermInitData, RadioThermUpdate, async_get_data
|
||||
|
||||
type RadioThermConfigEntry = ConfigEntry[RadioThermUpdateCoordinator]
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
UPDATE_INTERVAL = timedelta(seconds=15)
|
||||
@@ -22,12 +24,12 @@ UPDATE_INTERVAL = timedelta(seconds=15)
|
||||
class RadioThermUpdateCoordinator(DataUpdateCoordinator[RadioThermUpdate]):
|
||||
"""DataUpdateCoordinator to gather data for radio thermostats."""
|
||||
|
||||
config_entry: ConfigEntry
|
||||
config_entry: RadioThermConfigEntry
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: RadioThermConfigEntry,
|
||||
init_data: RadioThermInitData,
|
||||
) -> None:
|
||||
"""Initialize DataUpdateCoordinator."""
|
||||
|
||||
@@ -5,12 +5,10 @@ from __future__ import annotations
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.switch import SwitchEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import RadioThermUpdateCoordinator
|
||||
from .coordinator import RadioThermConfigEntry, RadioThermUpdateCoordinator
|
||||
from .entity import RadioThermostatEntity
|
||||
|
||||
PARALLEL_UPDATES = 1
|
||||
@@ -18,12 +16,11 @@ PARALLEL_UPDATES = 1
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: RadioThermConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up switches for a radiotherm device."""
|
||||
coordinator: RadioThermUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
async_add_entities([RadioThermHoldSwitch(coordinator)])
|
||||
async_add_entities([RadioThermHoldSwitch(entry.runtime_data)])
|
||||
|
||||
|
||||
class RadioThermHoldSwitch(RadioThermostatEntity, SwitchEntity):
|
||||
|
||||
@@ -2,29 +2,27 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import EagleDataCoordinator
|
||||
from .coordinator import EagleDataCoordinator, RainforestEagleConfigEntry
|
||||
|
||||
PLATFORMS = [Platform.SENSOR]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, entry: RainforestEagleConfigEntry
|
||||
) -> bool:
|
||||
"""Set up Rainforest Eagle from a config entry."""
|
||||
coordinator = EagleDataCoordinator(hass, entry)
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
|
||||
entry.runtime_data = coordinator
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(
|
||||
hass: HomeAssistant, entry: RainforestEagleConfigEntry
|
||||
) -> bool:
|
||||
"""Unload a config entry."""
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
if unload_ok:
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
@@ -23,17 +23,21 @@ from .const import (
|
||||
)
|
||||
from .data import UPDATE_100_ERRORS
|
||||
|
||||
type RainforestEagleConfigEntry = ConfigEntry[EagleDataCoordinator]
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class EagleDataCoordinator(DataUpdateCoordinator):
|
||||
"""Get the latest data from the Eagle device."""
|
||||
|
||||
config_entry: ConfigEntry
|
||||
config_entry: RainforestEagleConfigEntry
|
||||
eagle100_reader: Eagle100Reader | None = None
|
||||
eagle200_meter: aioeagle.ElectricMeter | None = None
|
||||
|
||||
def __init__(self, hass: HomeAssistant, config_entry: ConfigEntry) -> None:
|
||||
def __init__(
|
||||
self, hass: HomeAssistant, config_entry: RainforestEagleConfigEntry
|
||||
) -> None:
|
||||
"""Initialize the data object."""
|
||||
if config_entry.data[CONF_TYPE] == TYPE_EAGLE_100:
|
||||
self.model = "EAGLE-100"
|
||||
|
||||
@@ -5,22 +5,19 @@ from __future__ import annotations
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.diagnostics import async_redact_data
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import CONF_CLOUD_ID, CONF_INSTALL_CODE, DOMAIN
|
||||
from .coordinator import EagleDataCoordinator
|
||||
from .const import CONF_CLOUD_ID, CONF_INSTALL_CODE
|
||||
from .coordinator import RainforestEagleConfigEntry
|
||||
|
||||
TO_REDACT = {CONF_CLOUD_ID, CONF_INSTALL_CODE}
|
||||
|
||||
|
||||
async def async_get_config_entry_diagnostics(
|
||||
hass: HomeAssistant, config_entry: ConfigEntry
|
||||
hass: HomeAssistant, config_entry: RainforestEagleConfigEntry
|
||||
) -> dict[str, Any]:
|
||||
"""Return diagnostics for a config entry."""
|
||||
coordinator: EagleDataCoordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||
|
||||
return {
|
||||
"config_entry": async_redact_data(config_entry.as_dict(), TO_REDACT),
|
||||
"data": coordinator.data,
|
||||
"data": config_entry.runtime_data.data,
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ from homeassistant.components.sensor import (
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import UnitOfEnergy, UnitOfPower
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
@@ -17,7 +16,7 @@ from homeassistant.helpers.typing import StateType
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import EagleDataCoordinator
|
||||
from .coordinator import EagleDataCoordinator, RainforestEagleConfigEntry
|
||||
|
||||
SENSORS = (
|
||||
SensorEntityDescription(
|
||||
@@ -46,11 +45,11 @@ SENSORS = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: RainforestEagleConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up a config entry."""
|
||||
coordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
coordinator = entry.runtime_data
|
||||
entities = [EagleSensor(coordinator, description) for description in SENSORS]
|
||||
|
||||
if coordinator.data.get("zigbee:Price") not in (None, "invalid"):
|
||||
|
||||
@@ -2,30 +2,25 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import RDWDataUpdateCoordinator
|
||||
from .coordinator import RDWConfigEntry, RDWDataUpdateCoordinator
|
||||
|
||||
PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: RDWConfigEntry) -> bool:
|
||||
"""Set up RDW from a config entry."""
|
||||
coordinator = RDWDataUpdateCoordinator(hass, entry)
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
|
||||
entry.runtime_data = coordinator
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: RDWConfigEntry) -> bool:
|
||||
"""Unload RDW config entry."""
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
if unload_ok:
|
||||
del hass.data[DOMAIN][entry.entry_id]
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
@@ -12,14 +12,13 @@ from homeassistant.components.binary_sensor import (
|
||||
BinarySensorEntity,
|
||||
BinarySensorEntityDescription,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import RDWDataUpdateCoordinator
|
||||
from .coordinator import RDWConfigEntry, RDWDataUpdateCoordinator
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
@@ -46,11 +45,11 @@ BINARY_SENSORS: tuple[RDWBinarySensorEntityDescription, ...] = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: RDWConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up RDW binary sensors based on a config entry."""
|
||||
coordinator: RDWDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
coordinator = entry.runtime_data
|
||||
async_add_entities(
|
||||
RDWBinarySensorEntity(
|
||||
coordinator=coordinator,
|
||||
|
||||
@@ -11,13 +11,15 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||
|
||||
from .const import CONF_LICENSE_PLATE, DOMAIN, LOGGER, SCAN_INTERVAL
|
||||
|
||||
type RDWConfigEntry = ConfigEntry[RDWDataUpdateCoordinator]
|
||||
|
||||
|
||||
class RDWDataUpdateCoordinator(DataUpdateCoordinator[Vehicle]):
|
||||
"""Class to manage fetching RDW data."""
|
||||
|
||||
config_entry: ConfigEntry
|
||||
config_entry: RDWConfigEntry
|
||||
|
||||
def __init__(self, hass: HomeAssistant, config_entry: ConfigEntry) -> None:
|
||||
def __init__(self, hass: HomeAssistant, config_entry: RDWConfigEntry) -> None:
|
||||
"""Initialize the coordinator."""
|
||||
super().__init__(
|
||||
hass,
|
||||
|
||||
@@ -4,17 +4,14 @@ from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import RDWDataUpdateCoordinator
|
||||
from .coordinator import RDWConfigEntry
|
||||
|
||||
|
||||
async def async_get_config_entry_diagnostics(
|
||||
hass: HomeAssistant, entry: ConfigEntry
|
||||
hass: HomeAssistant, entry: RDWConfigEntry
|
||||
) -> dict[str, Any]:
|
||||
"""Return diagnostics for a config entry."""
|
||||
coordinator: RDWDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
data: dict[str, Any] = coordinator.data.to_dict()
|
||||
data: dict[str, Any] = entry.runtime_data.data.to_dict()
|
||||
return data
|
||||
|
||||
@@ -13,14 +13,13 @@ from homeassistant.components.sensor import (
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import CONF_LICENSE_PLATE, DOMAIN
|
||||
from .coordinator import RDWDataUpdateCoordinator
|
||||
from .coordinator import RDWConfigEntry, RDWDataUpdateCoordinator
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
@@ -48,11 +47,11 @@ SENSORS: tuple[RDWSensorEntityDescription, ...] = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: RDWConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up RDW sensors based on a config entry."""
|
||||
coordinator: RDWDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
coordinator = entry.runtime_data
|
||||
async_add_entities(
|
||||
RDWSensorEntity(
|
||||
coordinator=coordinator,
|
||||
|
||||
@@ -9,19 +9,20 @@ from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .const import CONF_PLACE_ID, CONF_SERVICE_ID, DOMAIN, LOGGER
|
||||
from .coordinator import ReCollectWasteDataUpdateCoordinator
|
||||
from .const import CONF_PLACE_ID, CONF_SERVICE_ID, LOGGER
|
||||
from .coordinator import RecollectWasteConfigEntry, ReCollectWasteDataUpdateCoordinator
|
||||
|
||||
PLATFORMS = [Platform.CALENDAR, Platform.SENSOR]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, entry: RecollectWasteConfigEntry
|
||||
) -> bool:
|
||||
"""Set up ReCollect Waste as config entry."""
|
||||
coordinator = ReCollectWasteDataUpdateCoordinator(hass, entry)
|
||||
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
hass.data[DOMAIN][entry.entry_id] = coordinator
|
||||
entry.runtime_data = coordinator
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
@@ -30,18 +31,18 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
async def async_reload_entry(
|
||||
hass: HomeAssistant, entry: RecollectWasteConfigEntry
|
||||
) -> None:
|
||||
"""Handle an options update."""
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(
|
||||
hass: HomeAssistant, entry: RecollectWasteConfigEntry
|
||||
) -> bool:
|
||||
"""Unload an ReCollect Waste config entry."""
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
if unload_ok:
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
|
||||
async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
@@ -7,19 +7,17 @@ import datetime
|
||||
from aiorecollect.client import PickupEvent
|
||||
|
||||
from homeassistant.components.calendar import CalendarEntity, CalendarEvent
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import ReCollectWasteDataUpdateCoordinator
|
||||
from .coordinator import RecollectWasteConfigEntry, ReCollectWasteDataUpdateCoordinator
|
||||
from .entity import ReCollectWasteEntity
|
||||
from .util import async_get_pickup_type_names
|
||||
|
||||
|
||||
@callback
|
||||
def async_get_calendar_event_from_pickup_event(
|
||||
entry: ConfigEntry, pickup_event: PickupEvent
|
||||
entry: RecollectWasteConfigEntry, pickup_event: PickupEvent
|
||||
) -> CalendarEvent:
|
||||
"""Get a HASS CalendarEvent from an aiorecollect PickupEvent."""
|
||||
pickup_type_string = ", ".join(
|
||||
@@ -36,13 +34,11 @@ def async_get_calendar_event_from_pickup_event(
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: RecollectWasteConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up ReCollect Waste sensors based on a config entry."""
|
||||
coordinator: ReCollectWasteDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
async_add_entities([ReCollectWasteCalendar(coordinator, entry)])
|
||||
async_add_entities([ReCollectWasteCalendar(entry.runtime_data, entry)])
|
||||
|
||||
|
||||
class ReCollectWasteCalendar(ReCollectWasteEntity, CalendarEntity):
|
||||
@@ -54,7 +50,7 @@ class ReCollectWasteCalendar(ReCollectWasteEntity, CalendarEntity):
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: ReCollectWasteDataUpdateCoordinator,
|
||||
entry: ConfigEntry,
|
||||
entry: RecollectWasteConfigEntry,
|
||||
) -> None:
|
||||
"""Initialize the ReCollect Waste entity."""
|
||||
super().__init__(coordinator, entry)
|
||||
|
||||
@@ -8,17 +8,13 @@ from aiorecollect.client import Client
|
||||
from aiorecollect.errors import RecollectError
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import (
|
||||
ConfigEntry,
|
||||
ConfigFlow,
|
||||
ConfigFlowResult,
|
||||
OptionsFlow,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult, OptionsFlow
|
||||
from homeassistant.const import CONF_FRIENDLY_NAME
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers import aiohttp_client
|
||||
|
||||
from .const import CONF_PLACE_ID, CONF_SERVICE_ID, DOMAIN, LOGGER
|
||||
from .coordinator import RecollectWasteConfigEntry
|
||||
|
||||
DATA_SCHEMA = vol.Schema(
|
||||
{vol.Required(CONF_PLACE_ID): str, vol.Required(CONF_SERVICE_ID): str}
|
||||
@@ -33,7 +29,7 @@ class RecollectWasteConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
@staticmethod
|
||||
@callback
|
||||
def async_get_options_flow(
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: RecollectWasteConfigEntry,
|
||||
) -> RecollectWasteOptionsFlowHandler:
|
||||
"""Define the config flow to handle options."""
|
||||
return RecollectWasteOptionsFlowHandler()
|
||||
|
||||
@@ -14,15 +14,19 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda
|
||||
|
||||
from .const import CONF_PLACE_ID, CONF_SERVICE_ID, LOGGER
|
||||
|
||||
type RecollectWasteConfigEntry = ConfigEntry[ReCollectWasteDataUpdateCoordinator]
|
||||
|
||||
DEFAULT_UPDATE_INTERVAL = timedelta(days=1)
|
||||
|
||||
|
||||
class ReCollectWasteDataUpdateCoordinator(DataUpdateCoordinator[list[PickupEvent]]):
|
||||
"""Class to manage fetching ReCollect Waste data."""
|
||||
|
||||
config_entry: ConfigEntry
|
||||
config_entry: RecollectWasteConfigEntry
|
||||
|
||||
def __init__(self, hass: HomeAssistant, config_entry: ConfigEntry) -> None:
|
||||
def __init__(
|
||||
self, hass: HomeAssistant, config_entry: RecollectWasteConfigEntry
|
||||
) -> None:
|
||||
"""Initialize the coordinator."""
|
||||
super().__init__(
|
||||
hass,
|
||||
|
||||
@@ -6,12 +6,11 @@ import dataclasses
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.diagnostics import async_redact_data
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_UNIQUE_ID
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import CONF_PLACE_ID, DOMAIN
|
||||
from .coordinator import ReCollectWasteDataUpdateCoordinator
|
||||
from .const import CONF_PLACE_ID
|
||||
from .coordinator import RecollectWasteConfigEntry
|
||||
|
||||
CONF_AREA_NAME = "area_name"
|
||||
CONF_TITLE = "title"
|
||||
@@ -26,15 +25,13 @@ TO_REDACT = {
|
||||
|
||||
|
||||
async def async_get_config_entry_diagnostics(
|
||||
hass: HomeAssistant, entry: ConfigEntry
|
||||
hass: HomeAssistant, entry: RecollectWasteConfigEntry
|
||||
) -> dict[str, Any]:
|
||||
"""Return diagnostics for a config entry."""
|
||||
coordinator: ReCollectWasteDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
return async_redact_data(
|
||||
{
|
||||
"entry": entry.as_dict(),
|
||||
"data": [dataclasses.asdict(event) for event in coordinator.data],
|
||||
"data": [dataclasses.asdict(event) for event in entry.runtime_data.data],
|
||||
},
|
||||
TO_REDACT,
|
||||
)
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
"""Define a base ReCollect Waste entity."""
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import CONF_PLACE_ID, CONF_SERVICE_ID, DOMAIN
|
||||
from .coordinator import ReCollectWasteDataUpdateCoordinator
|
||||
from .coordinator import RecollectWasteConfigEntry, ReCollectWasteDataUpdateCoordinator
|
||||
|
||||
|
||||
class ReCollectWasteEntity(CoordinatorEntity[ReCollectWasteDataUpdateCoordinator]):
|
||||
@@ -16,7 +15,7 @@ class ReCollectWasteEntity(CoordinatorEntity[ReCollectWasteDataUpdateCoordinator
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: ReCollectWasteDataUpdateCoordinator,
|
||||
entry: ConfigEntry,
|
||||
entry: RecollectWasteConfigEntry,
|
||||
) -> None:
|
||||
"""Initialize the sensor."""
|
||||
super().__init__(coordinator)
|
||||
|
||||
@@ -9,12 +9,11 @@ from homeassistant.components.sensor import (
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN, LOGGER
|
||||
from .coordinator import ReCollectWasteDataUpdateCoordinator
|
||||
from .const import LOGGER
|
||||
from .coordinator import RecollectWasteConfigEntry, ReCollectWasteDataUpdateCoordinator
|
||||
from .entity import ReCollectWasteEntity
|
||||
from .util import async_get_pickup_type_names
|
||||
|
||||
@@ -38,14 +37,12 @@ SENSOR_DESCRIPTIONS = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: RecollectWasteConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up ReCollect Waste sensors based on a config entry."""
|
||||
coordinator: ReCollectWasteDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
async_add_entities(
|
||||
ReCollectWasteSensor(coordinator, entry, description)
|
||||
ReCollectWasteSensor(entry.runtime_data, entry, description)
|
||||
for description in SENSOR_DESCRIPTIONS
|
||||
)
|
||||
|
||||
@@ -63,7 +60,7 @@ class ReCollectWasteSensor(ReCollectWasteEntity, SensorEntity):
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: ReCollectWasteDataUpdateCoordinator,
|
||||
entry: ConfigEntry,
|
||||
entry: RecollectWasteConfigEntry,
|
||||
description: SensorEntityDescription,
|
||||
) -> None:
|
||||
"""Initialize."""
|
||||
|
||||
@@ -3,12 +3,10 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import cast
|
||||
|
||||
from renault_api.kamereon.models import KamereonVehicleDataAttributes
|
||||
|
||||
from homeassistant.helpers.entity import Entity, EntityDescription
|
||||
from homeassistant.helpers.typing import StateType
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .coordinator import RenaultDataUpdateCoordinator
|
||||
@@ -54,10 +52,6 @@ class RenaultDataEntity[T: KamereonVehicleDataAttributes](
|
||||
super().__init__(vehicle.coordinators[description.coordinator])
|
||||
RenaultEntity.__init__(self, vehicle, description)
|
||||
|
||||
def _get_data_attr(self, key: str) -> StateType:
|
||||
"""Return the attribute value from the coordinator data."""
|
||||
return cast(StateType, getattr(self.coordinator.data, key))
|
||||
|
||||
@property
|
||||
def assumed_state(self) -> bool:
|
||||
"""Return True if unable to access real state of the entity."""
|
||||
|
||||
@@ -88,15 +88,6 @@ class RenaultSensor[T: KamereonVehicleDataAttributes](
|
||||
return self.entity_description.value_lambda(self)
|
||||
|
||||
|
||||
def _get_charging_power(
|
||||
entity: RenaultSensor[KamereonVehicleBatteryStatusData],
|
||||
) -> StateType:
|
||||
"""Return the charging_power of this entity."""
|
||||
if (data := entity.coordinator.data.chargingInstantaneousPower) is None:
|
||||
return None
|
||||
return data / 1000
|
||||
|
||||
|
||||
def _get_charge_state_formatted(
|
||||
entity: RenaultSensor[KamereonVehicleBatteryStatusData],
|
||||
) -> str | None:
|
||||
@@ -190,9 +181,10 @@ SENSOR_TYPES: tuple[RenaultSensorEntityDescription[Any], ...] = (
|
||||
condition_lambda=lambda a: a.details.reports_charging_power_in_watts(),
|
||||
coordinator="battery",
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
native_unit_of_measurement=UnitOfPower.KILO_WATT,
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
suggested_unit_of_measurement=UnitOfPower.KILO_WATT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
value_lambda=_get_charging_power,
|
||||
value_lambda=lambda e: e.coordinator.data.chargingInstantaneousPower,
|
||||
translation_key="charging_power",
|
||||
),
|
||||
RenaultSensorEntityDescription[KamereonVehicleBatteryStatusData](
|
||||
|
||||
@@ -2,17 +2,13 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
from renson_endura_delta.renson import RensonVentilation
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_HOST, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import RensonCoordinator
|
||||
from .coordinator import RensonConfigEntry, RensonCoordinator, RensonData
|
||||
|
||||
PLATFORMS = [
|
||||
Platform.BINARY_SENSOR,
|
||||
@@ -25,15 +21,7 @@ PLATFORMS = [
|
||||
]
|
||||
|
||||
|
||||
@dataclass
|
||||
class RensonData:
|
||||
"""Renson data class."""
|
||||
|
||||
api: RensonVentilation
|
||||
coordinator: RensonCoordinator
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: RensonConfigEntry) -> bool:
|
||||
"""Set up Renson from a config entry."""
|
||||
|
||||
api = RensonVentilation(entry.data[CONF_HOST])
|
||||
@@ -44,7 +32,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = RensonData(
|
||||
entry.runtime_data = RensonData(
|
||||
api,
|
||||
coordinator,
|
||||
)
|
||||
@@ -54,9 +42,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: RensonConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
@@ -21,13 +21,11 @@ from homeassistant.components.binary_sensor import (
|
||||
BinarySensorEntity,
|
||||
BinarySensorEntityDescription,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import EntityCategory
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import RensonCoordinator
|
||||
from .coordinator import RensonConfigEntry, RensonCoordinator
|
||||
from .entity import RensonEntity
|
||||
|
||||
|
||||
@@ -85,15 +83,13 @@ BINARY_SENSORS: tuple[RensonBinarySensorEntityDescription, ...] = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: RensonConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Call the Renson integration to setup."""
|
||||
|
||||
api: RensonVentilation = hass.data[DOMAIN][config_entry.entry_id].api
|
||||
coordinator: RensonCoordinator = hass.data[DOMAIN][
|
||||
config_entry.entry_id
|
||||
].coordinator
|
||||
api = config_entry.runtime_data.api
|
||||
coordinator = config_entry.runtime_data.coordinator
|
||||
|
||||
async_add_entities(
|
||||
RensonBinarySensor(description, api, coordinator)
|
||||
|
||||
@@ -12,13 +12,11 @@ from homeassistant.components.button import (
|
||||
ButtonEntity,
|
||||
ButtonEntityDescription,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import EntityCategory
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import RensonCoordinator, RensonData
|
||||
from .const import DOMAIN
|
||||
from .coordinator import RensonConfigEntry, RensonCoordinator
|
||||
from .entity import RensonEntity
|
||||
|
||||
|
||||
@@ -53,12 +51,12 @@ ENTITY_DESCRIPTIONS: tuple[RensonButtonEntityDescription, ...] = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: RensonConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Renson button platform."""
|
||||
|
||||
data: RensonData = hass.data[DOMAIN][config_entry.entry_id]
|
||||
data = config_entry.runtime_data
|
||||
|
||||
entities = [
|
||||
RensonButton(description, data.api, data.coordinator)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from dataclasses import dataclass
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
from typing import Any
|
||||
@@ -15,18 +16,29 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
type RensonConfigEntry = ConfigEntry[RensonData]
|
||||
|
||||
|
||||
@dataclass
|
||||
class RensonData:
|
||||
"""Renson data class."""
|
||||
|
||||
api: RensonVentilation
|
||||
coordinator: RensonCoordinator
|
||||
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class RensonCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
||||
"""Data update coordinator for Renson."""
|
||||
|
||||
config_entry: ConfigEntry
|
||||
config_entry: RensonConfigEntry
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: RensonConfigEntry,
|
||||
api: RensonVentilation,
|
||||
) -> None:
|
||||
"""Initialize my coordinator."""
|
||||
|
||||
@@ -16,7 +16,6 @@ from renson_endura_delta.renson import Level, RensonVentilation
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.fan import FanEntity, FanEntityFeature
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import config_validation as cv, entity_platform
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
@@ -27,8 +26,7 @@ from homeassistant.util.percentage import (
|
||||
)
|
||||
from homeassistant.util.scaling import int_states_in_range
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import RensonCoordinator
|
||||
from .coordinator import RensonConfigEntry, RensonCoordinator
|
||||
from .entity import RensonEntity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -84,15 +82,13 @@ SPEED_RANGE: tuple[float, float] = (1, 4)
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: RensonConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Renson fan platform."""
|
||||
|
||||
api: RensonVentilation = hass.data[DOMAIN][config_entry.entry_id].api
|
||||
coordinator: RensonCoordinator = hass.data[DOMAIN][
|
||||
config_entry.entry_id
|
||||
].coordinator
|
||||
api = config_entry.runtime_data.api
|
||||
coordinator = config_entry.runtime_data.coordinator
|
||||
|
||||
async_add_entities([RensonFan(api, coordinator)])
|
||||
|
||||
|
||||
@@ -12,13 +12,11 @@ from homeassistant.components.number import (
|
||||
NumberEntity,
|
||||
NumberEntityDescription,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import EntityCategory, UnitOfTime
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import RensonCoordinator
|
||||
from .coordinator import RensonConfigEntry, RensonCoordinator
|
||||
from .entity import RensonEntity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -39,15 +37,13 @@ RENSON_NUMBER_DESCRIPTION = NumberEntityDescription(
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: RensonConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Renson number platform."""
|
||||
|
||||
api: RensonVentilation = hass.data[DOMAIN][config_entry.entry_id].api
|
||||
coordinator: RensonCoordinator = hass.data[DOMAIN][
|
||||
config_entry.entry_id
|
||||
].coordinator
|
||||
api = config_entry.runtime_data.api
|
||||
coordinator = config_entry.runtime_data.coordinator
|
||||
|
||||
async_add_entities([RensonNumber(RENSON_NUMBER_DESCRIPTION, api, coordinator)])
|
||||
|
||||
|
||||
@@ -34,7 +34,6 @@ from homeassistant.components.sensor import (
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONCENTRATION_PARTS_PER_MILLION,
|
||||
PERCENTAGE,
|
||||
@@ -45,9 +44,7 @@ from homeassistant.const import (
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import RensonData
|
||||
from .const import DOMAIN
|
||||
from .coordinator import RensonCoordinator
|
||||
from .coordinator import RensonConfigEntry, RensonCoordinator
|
||||
from .entity import RensonEntity
|
||||
|
||||
|
||||
@@ -271,12 +268,12 @@ class RensonSensor(RensonEntity, SensorEntity):
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: RensonConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Renson sensor platform."""
|
||||
|
||||
data: RensonData = hass.data[DOMAIN][config_entry.entry_id]
|
||||
data = config_entry.runtime_data
|
||||
|
||||
entities = [
|
||||
RensonSensor(description, data.api, data.coordinator) for description in SENSORS
|
||||
|
||||
@@ -9,12 +9,10 @@ from renson_endura_delta.field_enum import CURRENT_LEVEL_FIELD, DataType
|
||||
from renson_endura_delta.renson import Level, RensonVentilation
|
||||
|
||||
from homeassistant.components.switch import SwitchDeviceClass, SwitchEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import RensonCoordinator
|
||||
from .const import DOMAIN
|
||||
from .coordinator import RensonConfigEntry, RensonCoordinator
|
||||
from .entity import RensonEntity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -67,14 +65,12 @@ class RensonBreezeSwitch(RensonEntity, SwitchEntity):
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: RensonConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Call the Renson integration to setup."""
|
||||
|
||||
api: RensonVentilation = hass.data[DOMAIN][config_entry.entry_id].api
|
||||
coordinator: RensonCoordinator = hass.data[DOMAIN][
|
||||
config_entry.entry_id
|
||||
].coordinator
|
||||
api = config_entry.runtime_data.api
|
||||
coordinator = config_entry.runtime_data.coordinator
|
||||
|
||||
async_add_entities([RensonBreezeSwitch(api, coordinator)])
|
||||
|
||||
@@ -10,14 +10,11 @@ from renson_endura_delta.field_enum import DAYTIME_FIELD, NIGHTTIME_FIELD, Field
|
||||
from renson_endura_delta.renson import RensonVentilation
|
||||
|
||||
from homeassistant.components.time import TimeEntity, TimeEntityDescription
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import EntityCategory
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import RensonData
|
||||
from .const import DOMAIN
|
||||
from .coordinator import RensonCoordinator
|
||||
from .coordinator import RensonConfigEntry, RensonCoordinator
|
||||
from .entity import RensonEntity
|
||||
|
||||
|
||||
@@ -49,15 +46,14 @@ ENTITY_DESCRIPTIONS: tuple[RensonTimeEntityDescription, ...] = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: RensonConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Renson time platform."""
|
||||
|
||||
data: RensonData = hass.data[DOMAIN][config_entry.entry_id]
|
||||
|
||||
coordinator = config_entry.runtime_data.coordinator
|
||||
entities = [
|
||||
RensonTime(description, data.coordinator) for description in ENTITY_DESCRIPTIONS
|
||||
RensonTime(description, coordinator) for description in ENTITY_DESCRIPTIONS
|
||||
]
|
||||
|
||||
async_add_entities(entities)
|
||||
|
||||
@@ -9,17 +9,17 @@ from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .const import DOMAIN, LOGGER, SENSOR_TYPE_NEXT_PICKUP
|
||||
from .coordinator import RidwellDataUpdateCoordinator
|
||||
from .const import LOGGER, SENSOR_TYPE_NEXT_PICKUP
|
||||
from .coordinator import RidwellConfigEntry, RidwellDataUpdateCoordinator
|
||||
|
||||
PLATFORMS: list[Platform] = [Platform.CALENDAR, Platform.SENSOR, Platform.SWITCH]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: RidwellConfigEntry) -> bool:
|
||||
"""Set up Ridwell from a config entry."""
|
||||
coordinator = RidwellDataUpdateCoordinator(hass, entry)
|
||||
await coordinator.async_initialize()
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
|
||||
entry.runtime_data = coordinator
|
||||
entry.async_on_unload(entry.add_update_listener(options_update_listener))
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
@@ -27,17 +27,16 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
async def options_update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
async def options_update_listener(
|
||||
hass: HomeAssistant, entry: RidwellConfigEntry
|
||||
) -> None:
|
||||
"""Handle options update."""
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: RidwellConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
|
||||
async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
@@ -7,7 +7,6 @@ import datetime
|
||||
from aioridwell.model import PickupCategory, RidwellAccount, RidwellPickupEvent
|
||||
|
||||
from homeassistant.components.calendar import CalendarEntity, CalendarEvent
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
@@ -16,15 +15,14 @@ from .const import (
|
||||
CALENDAR_TITLE_ROTATING,
|
||||
CALENDAR_TITLE_STATUS,
|
||||
CONF_CALENDAR_TITLE,
|
||||
DOMAIN,
|
||||
)
|
||||
from .coordinator import RidwellDataUpdateCoordinator
|
||||
from .coordinator import RidwellConfigEntry, RidwellDataUpdateCoordinator
|
||||
from .entity import RidwellEntity
|
||||
|
||||
|
||||
@callback
|
||||
def async_get_calendar_event_from_pickup_event(
|
||||
pickup_event: RidwellPickupEvent, config_entry: ConfigEntry
|
||||
pickup_event: RidwellPickupEvent, config_entry: RidwellConfigEntry
|
||||
) -> CalendarEvent:
|
||||
"""Get a HASS CalendarEvent from an aioridwell PickupEvent."""
|
||||
pickup_items = []
|
||||
@@ -66,11 +64,11 @@ def async_get_calendar_event_from_pickup_event(
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: RidwellConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Ridwell calendars based on a config entry."""
|
||||
coordinator: RidwellDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
coordinator = entry.runtime_data
|
||||
|
||||
async_add_entities(
|
||||
RidwellCalendar(coordinator, account)
|
||||
|
||||
@@ -9,7 +9,7 @@ from aioridwell import async_get_client
|
||||
from aioridwell.errors import InvalidCredentialsError, RidwellError
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry, ConfigFlow, ConfigFlowResult
|
||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers import aiohttp_client, config_validation as cv, selector
|
||||
@@ -19,6 +19,7 @@ from homeassistant.helpers.schema_config_entry_flow import (
|
||||
)
|
||||
|
||||
from .const import CALENDAR_TITLE_OPTIONS, CONF_CALENDAR_TITLE, DOMAIN, LOGGER
|
||||
from .coordinator import RidwellConfigEntry
|
||||
|
||||
STEP_REAUTH_CONFIRM_DATA_SCHEMA = vol.Schema(
|
||||
{
|
||||
@@ -107,7 +108,7 @@ class RidwellConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
@staticmethod
|
||||
@callback
|
||||
def async_get_options_flow(
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: RidwellConfigEntry,
|
||||
) -> SchemaOptionsFlowHandler:
|
||||
"""Get options flow for this handler."""
|
||||
try:
|
||||
|
||||
@@ -19,6 +19,8 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda
|
||||
|
||||
from .const import LOGGER
|
||||
|
||||
type RidwellConfigEntry = ConfigEntry[RidwellDataUpdateCoordinator]
|
||||
|
||||
UPDATE_INTERVAL = timedelta(hours=1)
|
||||
|
||||
|
||||
@@ -27,9 +29,9 @@ class RidwellDataUpdateCoordinator(
|
||||
):
|
||||
"""Class to manage fetching data from single endpoint."""
|
||||
|
||||
config_entry: ConfigEntry
|
||||
config_entry: RidwellConfigEntry
|
||||
|
||||
def __init__(self, hass: HomeAssistant, config_entry: ConfigEntry) -> None:
|
||||
def __init__(self, hass: HomeAssistant, config_entry: RidwellConfigEntry) -> None:
|
||||
"""Initialize."""
|
||||
# These will be filled in by async_initialize; we give them these defaults to
|
||||
# avoid arduous typing checks down the line:
|
||||
|
||||
@@ -6,12 +6,10 @@ import dataclasses
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.diagnostics import async_redact_data
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_PASSWORD, CONF_UNIQUE_ID, CONF_USERNAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import RidwellDataUpdateCoordinator
|
||||
from .coordinator import RidwellConfigEntry
|
||||
|
||||
CONF_TITLE = "title"
|
||||
|
||||
@@ -25,17 +23,15 @@ TO_REDACT = {
|
||||
|
||||
|
||||
async def async_get_config_entry_diagnostics(
|
||||
hass: HomeAssistant, entry: ConfigEntry
|
||||
hass: HomeAssistant, entry: RidwellConfigEntry
|
||||
) -> dict[str, Any]:
|
||||
"""Return diagnostics for a config entry."""
|
||||
coordinator: RidwellDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
return async_redact_data(
|
||||
{
|
||||
"entry": entry.as_dict(),
|
||||
"data": [
|
||||
dataclasses.asdict(event)
|
||||
for events in coordinator.data.values()
|
||||
for events in entry.runtime_data.data.values()
|
||||
for event in events
|
||||
],
|
||||
},
|
||||
|
||||
@@ -13,12 +13,11 @@ from homeassistant.components.sensor import (
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN, SENSOR_TYPE_NEXT_PICKUP
|
||||
from .coordinator import RidwellDataUpdateCoordinator
|
||||
from .const import SENSOR_TYPE_NEXT_PICKUP
|
||||
from .coordinator import RidwellConfigEntry, RidwellDataUpdateCoordinator
|
||||
from .entity import RidwellEntity
|
||||
|
||||
ATTR_CATEGORY = "category"
|
||||
@@ -35,11 +34,11 @@ SENSOR_DESCRIPTION = SensorEntityDescription(
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: RidwellConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Ridwell sensors based on a config entry."""
|
||||
coordinator: RidwellDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
coordinator = entry.runtime_data
|
||||
|
||||
async_add_entities(
|
||||
RidwellSensor(coordinator, account, SENSOR_DESCRIPTION)
|
||||
|
||||
@@ -8,13 +8,11 @@ from aioridwell.errors import RidwellError
|
||||
from aioridwell.model import EventState, RidwellAccount
|
||||
|
||||
from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import RidwellDataUpdateCoordinator
|
||||
from .coordinator import RidwellConfigEntry, RidwellDataUpdateCoordinator
|
||||
from .entity import RidwellEntity
|
||||
|
||||
SWITCH_DESCRIPTION = SwitchEntityDescription(
|
||||
@@ -25,11 +23,11 @@ SWITCH_DESCRIPTION = SwitchEntityDescription(
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: RidwellConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Ridwell sensors based on a config entry."""
|
||||
coordinator: RidwellDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
coordinator = entry.runtime_data
|
||||
|
||||
async_add_entities(
|
||||
RidwellSwitch(coordinator, account, SWITCH_DESCRIPTION)
|
||||
|
||||
@@ -26,15 +26,13 @@ from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from .const import (
|
||||
CONF_CONCURRENCY,
|
||||
DATA_COORDINATOR,
|
||||
DEFAULT_CONCURRENCY,
|
||||
DOMAIN,
|
||||
EVENTS_COORDINATOR,
|
||||
SYSTEM_UPDATE_SIGNAL,
|
||||
TYPE_LOCAL,
|
||||
)
|
||||
from .coordinator import RiscoDataUpdateCoordinator, RiscoEventsDataUpdateCoordinator
|
||||
from .models import LocalData
|
||||
from .models import CloudData, LocalData, RiscoConfigEntry, RiscoData
|
||||
from .services import async_setup_services
|
||||
|
||||
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
|
||||
@@ -58,7 +56,7 @@ def zone_update_signal(zone_id: int) -> str:
|
||||
return f"risco_zone_update_{zone_id}"
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: RiscoConfigEntry) -> bool:
|
||||
"""Set up Risco from a config entry."""
|
||||
if is_local(entry):
|
||||
return await _async_setup_local_entry(hass, entry)
|
||||
@@ -66,7 +64,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
return await _async_setup_cloud_entry(hass, entry)
|
||||
|
||||
|
||||
async def _async_setup_local_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def _async_setup_local_entry(
|
||||
hass: HomeAssistant, entry: RiscoConfigEntry
|
||||
) -> bool:
|
||||
data = entry.data
|
||||
concurrency = entry.options.get(CONF_CONCURRENCY, DEFAULT_CONCURRENCY)
|
||||
risco = RiscoLocal(
|
||||
@@ -120,14 +120,15 @@ async def _async_setup_local_entry(hass: HomeAssistant, entry: ConfigEntry) -> b
|
||||
|
||||
entry.async_on_unload(entry.add_update_listener(_update_listener))
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
hass.data[DOMAIN][entry.entry_id] = local_data
|
||||
entry.runtime_data = RiscoData(local_data=local_data)
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def _async_setup_cloud_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def _async_setup_cloud_entry(
|
||||
hass: HomeAssistant, entry: RiscoConfigEntry
|
||||
) -> bool:
|
||||
data = entry.data
|
||||
risco = RiscoCloud(data[CONF_USERNAME], data[CONF_PASSWORD], data[CONF_PIN])
|
||||
try:
|
||||
@@ -143,11 +144,12 @@ async def _async_setup_cloud_entry(hass: HomeAssistant, entry: ConfigEntry) -> b
|
||||
|
||||
entry.async_on_unload(entry.add_update_listener(_update_listener))
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
hass.data[DOMAIN][entry.entry_id] = {
|
||||
DATA_COORDINATOR: coordinator,
|
||||
EVENTS_COORDINATOR: events_coordinator,
|
||||
}
|
||||
entry.runtime_data = RiscoData(
|
||||
cloud_data=CloudData(
|
||||
coordinator=coordinator,
|
||||
events_coordinator=events_coordinator,
|
||||
)
|
||||
)
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
await events_coordinator.async_refresh()
|
||||
@@ -155,20 +157,16 @@ async def _async_setup_cloud_entry(hass: HomeAssistant, entry: ConfigEntry) -> b
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: RiscoConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
if unload_ok:
|
||||
if is_local(entry):
|
||||
local_data: LocalData = hass.data[DOMAIN][entry.entry_id]
|
||||
await local_data.system.disconnect()
|
||||
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
if unload_ok and (local_data := entry.runtime_data.local_data):
|
||||
await local_data.system.disconnect()
|
||||
|
||||
return unload_ok
|
||||
|
||||
|
||||
async def _update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
async def _update_listener(hass: HomeAssistant, entry: RiscoConfigEntry) -> None:
|
||||
"""Handle options update."""
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
|
||||
|
||||
@@ -15,19 +15,16 @@ from homeassistant.components.alarm_control_panel import (
|
||||
AlarmControlPanelState,
|
||||
CodeFormat,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_PIN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import LocalData, is_local
|
||||
from .const import (
|
||||
CONF_CODE_ARM_REQUIRED,
|
||||
CONF_CODE_DISARM_REQUIRED,
|
||||
CONF_HA_STATES_TO_RISCO,
|
||||
CONF_RISCO_STATES_TO_HA,
|
||||
DATA_COORDINATOR,
|
||||
DEFAULT_OPTIONS,
|
||||
DOMAIN,
|
||||
RISCO_ARM,
|
||||
@@ -36,6 +33,7 @@ from .const import (
|
||||
)
|
||||
from .coordinator import RiscoDataUpdateCoordinator
|
||||
from .entity import RiscoCloudEntity
|
||||
from .models import RiscoConfigEntry
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -49,13 +47,13 @@ STATES_TO_SUPPORTED_FEATURES = {
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: RiscoConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Risco alarm control panel."""
|
||||
options = {**DEFAULT_OPTIONS, **config_entry.options}
|
||||
if is_local(config_entry):
|
||||
local_data: LocalData = hass.data[DOMAIN][config_entry.entry_id]
|
||||
risco_data = config_entry.runtime_data
|
||||
if local_data := risco_data.local_data:
|
||||
async_add_entities(
|
||||
RiscoLocalAlarm(
|
||||
local_data.system.id,
|
||||
@@ -67,10 +65,8 @@ async def async_setup_entry(
|
||||
)
|
||||
for partition_id, partition in local_data.system.partitions.items()
|
||||
)
|
||||
else:
|
||||
coordinator: RiscoDataUpdateCoordinator = hass.data[DOMAIN][
|
||||
config_entry.entry_id
|
||||
][DATA_COORDINATOR]
|
||||
elif cloud_data := risco_data.cloud_data:
|
||||
coordinator = cloud_data.coordinator
|
||||
async_add_entities(
|
||||
RiscoCloudAlarm(
|
||||
coordinator, partition_id, config_entry.data[CONF_PIN], options
|
||||
|
||||
@@ -15,16 +15,15 @@ from homeassistant.components.binary_sensor import (
|
||||
BinarySensorEntity,
|
||||
BinarySensorEntityDescription,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import LocalData, is_local
|
||||
from .const import DATA_COORDINATOR, DOMAIN, SYSTEM_UPDATE_SIGNAL
|
||||
from .const import DOMAIN, SYSTEM_UPDATE_SIGNAL
|
||||
from .coordinator import RiscoDataUpdateCoordinator
|
||||
from .entity import RiscoCloudZoneEntity, RiscoLocalZoneEntity
|
||||
from .models import RiscoConfigEntry
|
||||
|
||||
SYSTEM_ENTITY_DESCRIPTIONS = [
|
||||
BinarySensorEntityDescription(
|
||||
@@ -72,12 +71,12 @@ SYSTEM_ENTITY_DESCRIPTIONS = [
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: RiscoConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Risco alarm control panel."""
|
||||
if is_local(config_entry):
|
||||
local_data: LocalData = hass.data[DOMAIN][config_entry.entry_id]
|
||||
risco_data = config_entry.runtime_data
|
||||
if local_data := risco_data.local_data:
|
||||
zone_entities = (
|
||||
entity
|
||||
for zone_id, zone in local_data.system.zones.items()
|
||||
@@ -96,10 +95,8 @@ async def async_setup_entry(
|
||||
)
|
||||
|
||||
async_add_entities(chain(system_entities, zone_entities))
|
||||
else:
|
||||
coordinator: RiscoDataUpdateCoordinator = hass.data[DOMAIN][
|
||||
config_entry.entry_id
|
||||
][DATA_COORDINATOR]
|
||||
elif cloud_data := risco_data.cloud_data:
|
||||
coordinator = cloud_data.coordinator
|
||||
async_add_entities(
|
||||
RiscoCloudBinarySensor(coordinator, zone_id, zone)
|
||||
for zone_id, zone in coordinator.data.zones.items()
|
||||
|
||||
@@ -10,12 +10,7 @@ from pyrisco import CannotConnectError, RiscoCloud, RiscoLocal, UnauthorizedErro
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.alarm_control_panel import AlarmControlPanelState
|
||||
from homeassistant.config_entries import (
|
||||
ConfigEntry,
|
||||
ConfigFlow,
|
||||
ConfigFlowResult,
|
||||
OptionsFlow,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult, OptionsFlow
|
||||
from homeassistant.const import (
|
||||
CONF_HOST,
|
||||
CONF_PASSWORD,
|
||||
@@ -42,6 +37,7 @@ from .const import (
|
||||
RISCO_STATES,
|
||||
TYPE_LOCAL,
|
||||
)
|
||||
from .models import RiscoConfigEntry
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -121,12 +117,12 @@ class RiscoConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Init the config flow."""
|
||||
self._reauth_entry: ConfigEntry | None = None
|
||||
self._reauth_entry: RiscoConfigEntry | None = None
|
||||
|
||||
@staticmethod
|
||||
@callback
|
||||
def async_get_options_flow(
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: RiscoConfigEntry,
|
||||
) -> RiscoOptionsFlowHandler:
|
||||
"""Define the config flow to handle options."""
|
||||
return RiscoOptionsFlowHandler(config_entry)
|
||||
@@ -218,7 +214,7 @@ class RiscoConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
class RiscoOptionsFlowHandler(OptionsFlow):
|
||||
"""Handle a Risco options flow."""
|
||||
|
||||
def __init__(self, config_entry: ConfigEntry) -> None:
|
||||
def __init__(self, config_entry: RiscoConfigEntry) -> None:
|
||||
"""Initialize."""
|
||||
self._data = {**DEFAULT_OPTIONS, **config_entry.options}
|
||||
|
||||
|
||||
@@ -1,11 +1,39 @@
|
||||
"""Models for Risco integration."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from pyrisco import RiscoLocal
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .coordinator import (
|
||||
RiscoDataUpdateCoordinator,
|
||||
RiscoEventsDataUpdateCoordinator,
|
||||
)
|
||||
|
||||
type RiscoConfigEntry = ConfigEntry[RiscoData]
|
||||
|
||||
|
||||
@dataclass
|
||||
class RiscoData:
|
||||
"""Runtime data for the Risco integration."""
|
||||
|
||||
local_data: LocalData | None = None
|
||||
cloud_data: CloudData | None = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class CloudData:
|
||||
"""A data class for cloud data passed to the platforms."""
|
||||
|
||||
coordinator: RiscoDataUpdateCoordinator
|
||||
events_coordinator: RiscoEventsDataUpdateCoordinator
|
||||
|
||||
|
||||
@dataclass
|
||||
class LocalData:
|
||||
|
||||
@@ -10,17 +10,16 @@ from pyrisco.cloud.event import Event
|
||||
|
||||
from homeassistant.components.binary_sensor import DOMAIN as BS_DOMAIN
|
||||
from homeassistant.components.sensor import SensorDeviceClass, SensorEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from . import is_local
|
||||
from .const import DOMAIN, EVENTS_COORDINATOR
|
||||
from .const import DOMAIN
|
||||
from .coordinator import RiscoEventsDataUpdateCoordinator
|
||||
from .entity import zone_unique_id
|
||||
from .models import RiscoConfigEntry
|
||||
|
||||
CATEGORIES = {
|
||||
2: "Alarm",
|
||||
@@ -45,17 +44,15 @@ EVENT_ATTRIBUTES = [
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: RiscoConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up sensors for device."""
|
||||
if is_local(config_entry):
|
||||
if not (cloud_data := config_entry.runtime_data.cloud_data):
|
||||
# no events in local comm
|
||||
return
|
||||
|
||||
coordinator: RiscoEventsDataUpdateCoordinator = hass.data[DOMAIN][
|
||||
config_entry.entry_id
|
||||
][EVENTS_COORDINATOR]
|
||||
coordinator = cloud_data.events_coordinator
|
||||
sensors = [
|
||||
RiscoSensor(coordinator, category_id, [], name, config_entry.entry_id)
|
||||
for category_id, name in CATEGORIES.items()
|
||||
|
||||
@@ -4,26 +4,26 @@ from datetime import datetime
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import ATTR_CONFIG_ENTRY_ID, ATTR_TIME, CONF_TYPE
|
||||
from homeassistant.const import ATTR_CONFIG_ENTRY_ID, ATTR_TIME
|
||||
from homeassistant.core import HomeAssistant, ServiceCall
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
from homeassistant.helpers import config_validation as cv, service
|
||||
|
||||
from .const import DOMAIN, SERVICE_SET_TIME, TYPE_LOCAL
|
||||
from .models import LocalData
|
||||
from .const import DOMAIN, SERVICE_SET_TIME
|
||||
from .models import RiscoConfigEntry
|
||||
|
||||
|
||||
async def async_setup_services(hass: HomeAssistant) -> None:
|
||||
"""Create the Risco Services/Actions."""
|
||||
|
||||
async def _set_time(service_call: ServiceCall) -> None:
|
||||
entry = service.async_get_config_entry(
|
||||
entry: RiscoConfigEntry = service.async_get_config_entry(
|
||||
service_call.hass, DOMAIN, service_call.data[ATTR_CONFIG_ENTRY_ID]
|
||||
)
|
||||
time = service_call.data.get(ATTR_TIME)
|
||||
|
||||
# Validate config entry is local (not cloud)
|
||||
if entry.data.get(CONF_TYPE) != TYPE_LOCAL:
|
||||
if not (local_data := entry.runtime_data.local_data):
|
||||
raise ServiceValidationError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="not_local_entry",
|
||||
@@ -33,8 +33,6 @@ async def async_setup_services(hass: HomeAssistant) -> None:
|
||||
if time is None:
|
||||
time_to_send = datetime.now()
|
||||
|
||||
local_data: LocalData = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
await local_data.system.set_time(time_to_send)
|
||||
|
||||
hass.services.async_register(
|
||||
|
||||
@@ -7,33 +7,29 @@ from typing import Any
|
||||
from pyrisco.common import Zone
|
||||
|
||||
from homeassistant.components.switch import SwitchEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import EntityCategory
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import LocalData, is_local
|
||||
from .const import DATA_COORDINATOR, DOMAIN
|
||||
from .coordinator import RiscoDataUpdateCoordinator
|
||||
from .entity import RiscoCloudZoneEntity, RiscoLocalZoneEntity
|
||||
from .models import RiscoConfigEntry
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: RiscoConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Risco switch."""
|
||||
if is_local(config_entry):
|
||||
local_data: LocalData = hass.data[DOMAIN][config_entry.entry_id]
|
||||
risco_data = config_entry.runtime_data
|
||||
if local_data := risco_data.local_data:
|
||||
async_add_entities(
|
||||
RiscoLocalSwitch(local_data.system.id, zone_id, zone)
|
||||
for zone_id, zone in local_data.system.zones.items()
|
||||
)
|
||||
else:
|
||||
coordinator: RiscoDataUpdateCoordinator = hass.data[DOMAIN][
|
||||
config_entry.entry_id
|
||||
][DATA_COORDINATOR]
|
||||
elif cloud_data := risco_data.cloud_data:
|
||||
coordinator = cloud_data.coordinator
|
||||
async_add_entities(
|
||||
RiscoCloudSwitch(coordinator, zone_id, zone)
|
||||
for zone_id, zone in coordinator.data.zones.items()
|
||||
|
||||
@@ -2,15 +2,14 @@
|
||||
|
||||
import romy
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_HOST, CONF_PASSWORD
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import DOMAIN, LOGGER, PLATFORMS
|
||||
from .coordinator import RomyVacuumCoordinator
|
||||
from .const import LOGGER, PLATFORMS
|
||||
from .coordinator import RomyConfigEntry, RomyVacuumCoordinator
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, config_entry: RomyConfigEntry) -> bool:
|
||||
"""Initialize the ROMY platform via config entry."""
|
||||
|
||||
new_romy = await romy.create_romy(
|
||||
@@ -20,7 +19,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
|
||||
coordinator = RomyVacuumCoordinator(hass, config_entry, new_romy)
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[config_entry.entry_id] = coordinator
|
||||
config_entry.runtime_data = coordinator
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)
|
||||
|
||||
@@ -29,14 +28,12 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: RomyConfigEntry) -> bool:
|
||||
"""Handle removal of an entry."""
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
|
||||
async def update_listener(hass: HomeAssistant, config_entry: ConfigEntry) -> None:
|
||||
async def update_listener(hass: HomeAssistant, config_entry: RomyConfigEntry) -> None:
|
||||
"""Handle options update."""
|
||||
LOGGER.debug("update_listener")
|
||||
await hass.config_entries.async_reload(config_entry.entry_id)
|
||||
|
||||
@@ -5,12 +5,10 @@ from homeassistant.components.binary_sensor import (
|
||||
BinarySensorEntity,
|
||||
BinarySensorEntityDescription,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import RomyVacuumCoordinator
|
||||
from .coordinator import RomyConfigEntry, RomyVacuumCoordinator
|
||||
from .entity import RomyEntity
|
||||
|
||||
BINARY_SENSORS: list[BinarySensorEntityDescription] = [
|
||||
@@ -38,12 +36,12 @@ BINARY_SENSORS: list[BinarySensorEntityDescription] = [
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: RomyConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up ROMY vacuum cleaner."""
|
||||
|
||||
coordinator: RomyVacuumCoordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||
coordinator = config_entry.runtime_data
|
||||
|
||||
async_add_entities(
|
||||
RomyBinarySensor(coordinator, entity_description)
|
||||
|
||||
@@ -8,14 +8,16 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||
|
||||
from .const import DOMAIN, LOGGER, UPDATE_INTERVAL
|
||||
|
||||
type RomyConfigEntry = ConfigEntry[RomyVacuumCoordinator]
|
||||
|
||||
|
||||
class RomyVacuumCoordinator(DataUpdateCoordinator[None]):
|
||||
"""ROMY Vacuum Coordinator."""
|
||||
|
||||
config_entry: ConfigEntry
|
||||
config_entry: RomyConfigEntry
|
||||
|
||||
def __init__(
|
||||
self, hass: HomeAssistant, config_entry: ConfigEntry, romy: RomyRobot
|
||||
self, hass: HomeAssistant, config_entry: RomyConfigEntry, romy: RomyRobot
|
||||
) -> None:
|
||||
"""Initialize."""
|
||||
super().__init__(
|
||||
|
||||
@@ -6,7 +6,6 @@ from homeassistant.components.sensor import (
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
PERCENTAGE,
|
||||
SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
|
||||
@@ -18,8 +17,7 @@ from homeassistant.const import (
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import RomyVacuumCoordinator
|
||||
from .coordinator import RomyConfigEntry, RomyVacuumCoordinator
|
||||
from .entity import RomyEntity
|
||||
|
||||
SENSORS: list[SensorEntityDescription] = [
|
||||
@@ -76,12 +74,12 @@ SENSORS: list[SensorEntityDescription] = [
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: RomyConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up ROMY vacuum cleaner."""
|
||||
|
||||
coordinator: RomyVacuumCoordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||
coordinator = config_entry.runtime_data
|
||||
|
||||
async_add_entities(
|
||||
RomySensor(coordinator, entity_description)
|
||||
|
||||
@@ -11,12 +11,11 @@ from homeassistant.components.vacuum import (
|
||||
VacuumActivity,
|
||||
VacuumEntityFeature,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN, LOGGER
|
||||
from .coordinator import RomyVacuumCoordinator
|
||||
from .const import LOGGER
|
||||
from .coordinator import RomyConfigEntry, RomyVacuumCoordinator
|
||||
from .entity import RomyEntity
|
||||
|
||||
FAN_SPEED_NONE = "default"
|
||||
@@ -50,13 +49,11 @@ SUPPORT_ROMY_ROBOT = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: RomyConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up ROMY vacuum cleaner."""
|
||||
|
||||
coordinator: RomyVacuumCoordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||
async_add_entities([RomyVacuumEntity(coordinator)])
|
||||
async_add_entities([RomyVacuumEntity(config_entry.runtime_data)])
|
||||
|
||||
|
||||
class RomyVacuumEntity(RomyEntity, StateVacuumEntity):
|
||||
|
||||
@@ -9,7 +9,6 @@ from typing import Any
|
||||
from roombapy import Roomba, RoombaConnectionError, RoombaFactory
|
||||
|
||||
from homeassistant import exceptions
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONF_DELAY,
|
||||
CONF_HOST,
|
||||
@@ -19,13 +18,15 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import CONF_BLID, CONF_CONTINUOUS, DOMAIN, PLATFORMS, ROOMBA_SESSION
|
||||
from .models import RoombaData
|
||||
from .const import CONF_BLID, CONF_CONTINUOUS, PLATFORMS, ROOMBA_SESSION
|
||||
from .models import RoombaConfigEntry, RoombaData
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, config_entry: RoombaConfigEntry
|
||||
) -> bool:
|
||||
"""Set the config entry up."""
|
||||
# Set up roomba platforms with config entry
|
||||
|
||||
@@ -62,8 +63,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
|
||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _async_disconnect_roomba)
|
||||
)
|
||||
|
||||
domain_data = RoombaData(roomba, config_entry.data[CONF_BLID])
|
||||
hass.data.setdefault(DOMAIN, {})[config_entry.entry_id] = domain_data
|
||||
config_entry.runtime_data = RoombaData(roomba, config_entry.data[CONF_BLID])
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)
|
||||
|
||||
@@ -108,20 +108,22 @@ async def async_disconnect_or_timeout(hass: HomeAssistant, roomba: Roomba) -> No
|
||||
await hass.async_add_executor_job(roomba.disconnect)
|
||||
|
||||
|
||||
async def async_update_options(hass: HomeAssistant, config_entry: ConfigEntry) -> None:
|
||||
async def async_update_options(
|
||||
hass: HomeAssistant, config_entry: RoombaConfigEntry
|
||||
) -> None:
|
||||
"""Update options."""
|
||||
await hass.config_entries.async_reload(config_entry.entry_id)
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(
|
||||
hass: HomeAssistant, config_entry: RoombaConfigEntry
|
||||
) -> bool:
|
||||
"""Unload a config entry."""
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(
|
||||
config_entry, PLATFORMS
|
||||
)
|
||||
if unload_ok:
|
||||
domain_data: RoombaData = hass.data[DOMAIN][config_entry.entry_id]
|
||||
await async_disconnect_or_timeout(hass, roomba=domain_data.roomba)
|
||||
hass.data[DOMAIN].pop(config_entry.entry_id)
|
||||
await async_disconnect_or_timeout(hass, roomba=config_entry.runtime_data.roomba)
|
||||
|
||||
return unload_ok
|
||||
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
"""Roomba binary sensor entities."""
|
||||
|
||||
from homeassistant.components.binary_sensor import BinarySensorEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import roomba_reported_state
|
||||
from .const import DOMAIN
|
||||
from .entity import IRobotEntity
|
||||
from .models import RoombaData
|
||||
from .models import RoombaConfigEntry
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: RoombaConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the iRobot Roomba vacuum cleaner."""
|
||||
domain_data: RoombaData = hass.data[DOMAIN][config_entry.entry_id]
|
||||
domain_data = config_entry.runtime_data
|
||||
roomba = domain_data.roomba
|
||||
blid = domain_data.blid
|
||||
status = roomba_reported_state(roomba).get("bin", {})
|
||||
|
||||
@@ -11,12 +11,7 @@ from roombapy.discovery import RoombaDiscovery
|
||||
from roombapy.getpassword import RoombaPassword
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import (
|
||||
ConfigEntry,
|
||||
ConfigFlow,
|
||||
ConfigFlowResult,
|
||||
OptionsFlow,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult, OptionsFlow
|
||||
from homeassistant.const import CONF_DELAY, CONF_HOST, CONF_NAME, CONF_PASSWORD
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.service_info.dhcp import DhcpServiceInfo
|
||||
@@ -31,6 +26,7 @@ from .const import (
|
||||
DOMAIN,
|
||||
ROOMBA_SESSION,
|
||||
)
|
||||
from .models import RoombaConfigEntry
|
||||
|
||||
ROOMBA_DISCOVERY_LOCK = "roomba_discovery_lock"
|
||||
ALL_ATTEMPTS = 2
|
||||
@@ -90,7 +86,7 @@ class RoombaConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
@staticmethod
|
||||
@callback
|
||||
def async_get_options_flow(
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: RoombaConfigEntry,
|
||||
) -> RoombaOptionsFlowHandler:
|
||||
"""Get the options flow for this handler."""
|
||||
return RoombaOptionsFlowHandler()
|
||||
|
||||
@@ -6,6 +6,10 @@ from dataclasses import dataclass
|
||||
|
||||
from roombapy import Roomba
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
|
||||
type RoombaConfigEntry = ConfigEntry[RoombaData]
|
||||
|
||||
|
||||
@dataclass
|
||||
class RoombaData:
|
||||
|
||||
@@ -11,15 +11,13 @@ from homeassistant.components.sensor import (
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import PERCENTAGE, EntityCategory, UnitOfArea, UnitOfTime
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.typing import StateType
|
||||
|
||||
from .const import DOMAIN
|
||||
from .entity import IRobotEntity, roomba_reported_state
|
||||
from .models import RoombaData
|
||||
from .models import RoombaConfigEntry
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
@@ -142,11 +140,11 @@ SENSORS: list[RoombaSensorEntityDescription] = [
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: RoombaConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the iRobot Roomba vacuum cleaner."""
|
||||
domain_data: RoombaData = hass.data[DOMAIN][config_entry.entry_id]
|
||||
domain_data = config_entry.runtime_data
|
||||
roomba = domain_data.roomba
|
||||
blid = domain_data.blid
|
||||
|
||||
|
||||
@@ -12,16 +12,14 @@ from homeassistant.components.vacuum import (
|
||||
VacuumActivity,
|
||||
VacuumEntityFeature,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.util import dt as dt_util
|
||||
from homeassistant.util.unit_system import METRIC_SYSTEM
|
||||
|
||||
from . import roomba_reported_state
|
||||
from .const import DOMAIN
|
||||
from .entity import IRobotEntity
|
||||
from .models import RoombaData
|
||||
from .models import RoombaConfigEntry
|
||||
|
||||
SUPPORT_IROBOT = (
|
||||
VacuumEntityFeature.PAUSE
|
||||
@@ -87,11 +85,11 @@ SUPPORT_BRAAVA = SUPPORT_IROBOT | VacuumEntityFeature.FAN_SPEED
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: RoombaConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the iRobot Roomba vacuum cleaner."""
|
||||
domain_data: RoombaData = hass.data[DOMAIN][config_entry.entry_id]
|
||||
domain_data = config_entry.runtime_data
|
||||
roomba = domain_data.roomba
|
||||
blid = domain_data.blid
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@ from .const import CONF_ROON_NAME, DOMAIN
|
||||
from .server import RoonServer
|
||||
from .services import async_setup_services
|
||||
|
||||
type RoonConfigEntry = ConfigEntry[RoonServer]
|
||||
|
||||
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
|
||||
PLATFORMS = [Platform.EVENT, Platform.MEDIA_PLAYER]
|
||||
|
||||
@@ -20,10 +22,8 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: RoonConfigEntry) -> bool:
|
||||
"""Set up a roonserver from a config entry."""
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
|
||||
# fallback to using host for compatibility with older configs
|
||||
name = entry.data.get(CONF_ROON_NAME, entry.data[CONF_HOST])
|
||||
|
||||
@@ -32,7 +32,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
if not await roonserver.async_setup():
|
||||
return False
|
||||
|
||||
hass.data[DOMAIN][entry.entry_id] = roonserver
|
||||
entry.runtime_data = roonserver
|
||||
device_registry = dr.async_get(hass)
|
||||
device_registry.async_get_or_create(
|
||||
config_entry_id=entry.entry_id,
|
||||
@@ -47,10 +47,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: RoonConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
if not await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
return False
|
||||
|
||||
roonserver = hass.data[DOMAIN].pop(entry.entry_id)
|
||||
return await roonserver.async_reset()
|
||||
return await entry.runtime_data.async_reset()
|
||||
|
||||
@@ -4,12 +4,12 @@ import logging
|
||||
from typing import cast
|
||||
|
||||
from homeassistant.components.event import EventDeviceClass, EventEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import RoonConfigEntry
|
||||
from .const import DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -17,11 +17,11 @@ _LOGGER = logging.getLogger(__name__)
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: RoonConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Roon Event from Config Entry."""
|
||||
roon_server = hass.data[DOMAIN][config_entry.entry_id]
|
||||
roon_server = config_entry.runtime_data
|
||||
event_entities = set()
|
||||
|
||||
@callback
|
||||
|
||||
@@ -15,7 +15,6 @@ from homeassistant.components.media_player import (
|
||||
MediaType,
|
||||
RepeatMode,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import DEVICE_DEFAULT_NAME
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
@@ -27,6 +26,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.util import convert
|
||||
from homeassistant.util.dt import utcnow
|
||||
|
||||
from . import RoonConfigEntry
|
||||
from .const import DOMAIN
|
||||
from .media_browser import browse_media
|
||||
|
||||
@@ -45,11 +45,11 @@ REPEAT_MODE_MAPPING_TO_ROON = {
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: RoonConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Roon MediaPlayer from Config Entry."""
|
||||
roon_server = hass.data[DOMAIN][config_entry.entry_id]
|
||||
roon_server = config_entry.runtime_data
|
||||
media_players = set()
|
||||
|
||||
@callback
|
||||
|
||||
@@ -5,19 +5,18 @@ from __future__ import annotations
|
||||
from requests.exceptions import ConnectTimeout, HTTPError
|
||||
from rova.rova import Rova
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryError, ConfigEntryNotReady
|
||||
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
|
||||
|
||||
from .const import CONF_HOUSE_NUMBER, CONF_HOUSE_NUMBER_SUFFIX, CONF_ZIP_CODE, DOMAIN
|
||||
from .coordinator import RovaCoordinator
|
||||
from .coordinator import RovaConfigEntry, RovaCoordinator
|
||||
|
||||
PLATFORMS: list[Platform] = [Platform.SENSOR]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: RovaConfigEntry) -> bool:
|
||||
"""Set up ROVA from a config entry."""
|
||||
|
||||
api = Rova(
|
||||
@@ -50,15 +49,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
|
||||
entry.runtime_data = coordinator
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: RovaConfigEntry) -> bool:
|
||||
"""Unload ROVA config entry."""
|
||||
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user