Use runtime_data in xiaomi_miio (#145517)

* Use runtime_data in xiaomi_miio

* Reduce changes
This commit is contained in:
epenet 2025-05-23 17:03:33 +02:00 committed by GitHub
parent 086e97821f
commit 83ec45e4fc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 161 additions and 155 deletions

View File

@ -35,7 +35,6 @@ from miio import (
)
from miio.gateway.gateway import GatewayException
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_DEVICE, CONF_HOST, CONF_MODEL, CONF_TOKEN, Platform
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
@ -47,8 +46,6 @@ from .const import (
CONF_FLOW_TYPE,
CONF_GATEWAY,
DOMAIN,
KEY_COORDINATOR,
KEY_DEVICE,
MODEL_AIRFRESH_A1,
MODEL_AIRFRESH_T2017,
MODEL_FAN_1C,
@ -75,6 +72,7 @@ from .const import (
SetupException,
)
from .gateway import ConnectXiaomiGateway
from .typing import XiaomiMiioConfigEntry, XiaomiMiioRuntimeData
_LOGGER = logging.getLogger(__name__)
@ -125,9 +123,8 @@ MODEL_TO_CLASS_MAP = {
}
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def async_setup_entry(hass: HomeAssistant, entry: XiaomiMiioConfigEntry) -> bool:
"""Set up the Xiaomi Miio components from a config entry."""
hass.data.setdefault(DOMAIN, {})
if entry.data[CONF_FLOW_TYPE] == CONF_GATEWAY:
await async_setup_gateway_entry(hass, entry)
return True
@ -291,14 +288,13 @@ def _async_update_data_vacuum(
async def async_create_miio_device_and_coordinator(
hass: HomeAssistant, entry: ConfigEntry
hass: HomeAssistant, entry: XiaomiMiioConfigEntry
) -> None:
"""Set up a data coordinator and one miio device to service multiple entities."""
model: str = entry.data[CONF_MODEL]
host = entry.data[CONF_HOST]
token = entry.data[CONF_TOKEN]
name = entry.title
device: MiioDevice | None = None
migrate = False
update_method = _async_update_data_default
coordinator_class: type[DataUpdateCoordinator[Any]] = DataUpdateCoordinator
@ -323,6 +319,7 @@ async def async_create_miio_device_and_coordinator(
_LOGGER.debug("Initializing with host %s (token %s...)", host, token[:5])
device: MiioDevice
# Humidifiers
if model in MODELS_HUMIDIFIER_MIOT:
device = AirHumidifierMiot(host, token, lazy_discover=lazy_discover)
@ -394,16 +391,18 @@ async def async_create_miio_device_and_coordinator(
# Polling interval. Will only be polled if there are subscribers.
update_interval=UPDATE_INTERVAL,
)
hass.data[DOMAIN][entry.entry_id] = {
KEY_DEVICE: device,
KEY_COORDINATOR: coordinator,
}
# Trigger first data fetch
await coordinator.async_config_entry_first_refresh()
entry.runtime_data = XiaomiMiioRuntimeData(
device=device, device_coordinator=coordinator
)
async def async_setup_gateway_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
async def async_setup_gateway_entry(
hass: HomeAssistant, entry: XiaomiMiioConfigEntry
) -> None:
"""Set up the Xiaomi Gateway component from a config entry."""
host = entry.data[CONF_HOST]
token = entry.data[CONF_TOKEN]
@ -461,17 +460,18 @@ async def async_setup_gateway_entry(hass: HomeAssistant, entry: ConfigEntry) ->
update_interval=UPDATE_INTERVAL,
)
hass.data[DOMAIN][entry.entry_id] = {
CONF_GATEWAY: gateway.gateway_device,
KEY_COORDINATOR: coordinator_dict,
}
entry.runtime_data = XiaomiMiioRuntimeData(
gateway=gateway.gateway_device, gateway_coordinators=coordinator_dict
)
await hass.config_entries.async_forward_entry_setups(entry, GATEWAY_PLATFORMS)
entry.async_on_unload(entry.add_update_listener(update_listener))
async def async_setup_device_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def async_setup_device_entry(
hass: HomeAssistant, entry: XiaomiMiioConfigEntry
) -> bool:
"""Set up the Xiaomi Miio device component from a config entry."""
platforms = get_platforms(entry)
await async_create_miio_device_and_coordinator(hass, entry)
@ -486,20 +486,17 @@ async def async_setup_device_entry(hass: HomeAssistant, entry: ConfigEntry) -> b
return True
async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
async def async_unload_entry(
hass: HomeAssistant, config_entry: XiaomiMiioConfigEntry
) -> bool:
"""Unload a config entry."""
platforms = get_platforms(config_entry)
unload_ok = await hass.config_entries.async_unload_platforms(
config_entry, platforms
)
if unload_ok:
hass.data[DOMAIN].pop(config_entry.entry_id)
return unload_ok
return await hass.config_entries.async_unload_platforms(config_entry, platforms)
async def update_listener(hass: HomeAssistant, config_entry: ConfigEntry) -> None:
async def update_listener(
hass: HomeAssistant, config_entry: XiaomiMiioConfigEntry
) -> None:
"""Handle options update."""
await hass.config_entries.async_reload(config_entry.entry_id)

View File

@ -6,7 +6,6 @@ import logging
from miio import AirQualityMonitor, AirQualityMonitorCGDN1, DeviceException
from homeassistant.components.air_quality import AirQualityEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_DEVICE, CONF_HOST, CONF_MODEL, CONF_TOKEN
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
@ -19,6 +18,7 @@ from .const import (
MODEL_AIRQUALITYMONITOR_V1,
)
from .entity import XiaomiMiioEntity
from .typing import XiaomiMiioConfigEntry
_LOGGER = logging.getLogger(__name__)
@ -241,7 +241,7 @@ DEVICE_MAP: dict[str, dict[str, Callable]] = {
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
config_entry: XiaomiMiioConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the Xiaomi Air Quality from a config entry."""

View File

@ -12,12 +12,12 @@ from homeassistant.components.alarm_control_panel import (
AlarmControlPanelEntityFeature,
AlarmControlPanelState,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import CONF_GATEWAY, DOMAIN
from .const import DOMAIN
from .typing import XiaomiMiioConfigEntry
_LOGGER = logging.getLogger(__name__)
@ -28,12 +28,12 @@ XIAOMI_STATE_ARMING_VALUE = "oning"
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
config_entry: XiaomiMiioConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the Xiaomi Gateway Alarm from a config entry."""
entities = []
gateway = hass.data[DOMAIN][config_entry.entry_id][CONF_GATEWAY]
gateway = config_entry.runtime_data.gateway
entity = XiaomiGatewayAlarm(
gateway,
f"{config_entry.title} Alarm",

View File

@ -5,13 +5,13 @@ from __future__ import annotations
from collections.abc import Callable, Iterable
from dataclasses import dataclass
import logging
from typing import TYPE_CHECKING
from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass,
BinarySensorEntity,
BinarySensorEntityDescription,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_DEVICE, CONF_MODEL, EntityCategory
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
@ -19,9 +19,6 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import VacuumCoordinatorDataAttributes
from .const import (
CONF_FLOW_TYPE,
DOMAIN,
KEY_COORDINATOR,
KEY_DEVICE,
MODEL_AIRFRESH_A1,
MODEL_AIRFRESH_T2017,
MODEL_FAN_ZA5,
@ -33,6 +30,7 @@ from .const import (
MODELS_VACUUM_WITH_SEPARATE_MOP,
)
from .entity import XiaomiCoordinatedMiioEntity
from .typing import XiaomiMiioConfigEntry
_LOGGER = logging.getLogger(__name__)
@ -133,13 +131,17 @@ HUMIDIFIER_MIOT_BINARY_SENSORS = (ATTR_WATER_TANK_DETACHED,)
HUMIDIFIER_MJJSQ_BINARY_SENSORS = (ATTR_NO_WATER, ATTR_WATER_TANK_DETACHED)
def _setup_vacuum_sensors(hass, config_entry, async_add_entities):
def _setup_vacuum_sensors(
hass: HomeAssistant,
config_entry: XiaomiMiioConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Only vacuums with mop should have binary sensor registered."""
if config_entry.data[CONF_MODEL] not in MODELS_VACUUM_WITH_MOP:
return
device = hass.data[DOMAIN][config_entry.entry_id].get(KEY_DEVICE)
coordinator = hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR]
device = config_entry.runtime_data.device
coordinator = config_entry.runtime_data.device_coordinator
entities = []
sensors = VACUUM_SENSORS
@ -147,6 +149,8 @@ def _setup_vacuum_sensors(hass, config_entry, async_add_entities):
sensors = VACUUM_SENSORS_SEPARATE_MOP
for sensor, description in sensors.items():
if TYPE_CHECKING:
assert description.parent_key is not None
parent_key_data = getattr(coordinator.data, description.parent_key)
if getattr(parent_key_data, description.key, None) is None:
_LOGGER.debug(
@ -170,7 +174,7 @@ def _setup_vacuum_sensors(hass, config_entry, async_add_entities):
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
config_entry: XiaomiMiioConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the Xiaomi sensor from a config entry."""
@ -198,10 +202,10 @@ async def async_setup_entry(
continue
entities.append(
XiaomiGenericBinarySensor(
hass.data[DOMAIN][config_entry.entry_id][KEY_DEVICE],
config_entry.runtime_data.device,
config_entry,
f"{description.key}_{config_entry.unique_id}",
hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR],
config_entry.runtime_data.device_coordinator,
description,
)
)

View File

@ -11,20 +11,13 @@ from homeassistant.components.button import (
ButtonEntity,
ButtonEntityDescription,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_MODEL, EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import (
DOMAIN,
KEY_COORDINATOR,
KEY_DEVICE,
MODEL_AIRFRESH_A1,
MODEL_AIRFRESH_T2017,
MODELS_VACUUM,
)
from .const import MODEL_AIRFRESH_A1, MODEL_AIRFRESH_T2017, MODELS_VACUUM
from .entity import XiaomiCoordinatedMiioEntity
from .typing import XiaomiMiioConfigEntry
# Fans
ATTR_RESET_DUST_FILTER = "reset_dust_filter"
@ -123,7 +116,7 @@ MODEL_TO_BUTTON_MAP: dict[str, tuple[str, ...]] = {
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
config_entry: XiaomiMiioConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the button from a config entry."""
@ -135,8 +128,8 @@ async def async_setup_entry(
entities = []
buttons = MODEL_TO_BUTTON_MAP[model]
unique_id = config_entry.unique_id
device = hass.data[DOMAIN][config_entry.entry_id][KEY_DEVICE]
coordinator = hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR]
device = config_entry.runtime_data.device
coordinator = config_entry.runtime_data.device_coordinator
for description in BUTTON_TYPES:
if description.key not in buttons:

View File

@ -11,12 +11,7 @@ from micloud import MiCloud
from micloud.micloudexception import MiCloudAccessDenied
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_DEVICE, CONF_HOST, CONF_MAC, CONF_MODEL, CONF_TOKEN
from homeassistant.core import callback
from homeassistant.helpers.device_registry import format_mac
@ -40,6 +35,7 @@ from .const import (
SetupException,
)
from .device import ConnectXiaomiDevice
from .typing import XiaomiMiioConfigEntry
_LOGGER = logging.getLogger(__name__)
@ -116,7 +112,9 @@ class XiaomiMiioFlowHandler(ConfigFlow, domain=DOMAIN):
@staticmethod
@callback
def async_get_options_flow(config_entry: ConfigEntry) -> OptionsFlowHandler:
def async_get_options_flow(
config_entry: XiaomiMiioConfigEntry,
) -> OptionsFlowHandler:
"""Get the options flow."""
return OptionsFlowHandler()

View File

@ -27,9 +27,6 @@ CONF_MANUAL = "manual"
# Options flow
CONF_CLOUD_SUBDEVICES = "cloud_subdevices"
# Keys
KEY_COORDINATOR = "coordinator"
KEY_DEVICE = "device"
# Attributes
ATTR_AVAILABLE = "available"

View File

@ -5,11 +5,11 @@ from __future__ import annotations
from typing import Any
from homeassistant.components.diagnostics import async_redact_data
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_MAC, CONF_TOKEN, CONF_UNIQUE_ID
from homeassistant.const import CONF_DEVICE, CONF_MAC, CONF_TOKEN, CONF_UNIQUE_ID
from homeassistant.core import HomeAssistant
from .const import CONF_CLOUD_PASSWORD, CONF_CLOUD_USERNAME, DOMAIN, KEY_COORDINATOR
from .const import CONF_CLOUD_PASSWORD, CONF_CLOUD_USERNAME, CONF_FLOW_TYPE
from .typing import XiaomiMiioConfigEntry
TO_REDACT = {
CONF_CLOUD_PASSWORD,
@ -21,18 +21,17 @@ TO_REDACT = {
async def async_get_config_entry_diagnostics(
hass: HomeAssistant, config_entry: ConfigEntry
hass: HomeAssistant, config_entry: XiaomiMiioConfigEntry
) -> dict[str, Any]:
"""Return diagnostics for a config entry."""
diagnostics_data: dict[str, Any] = {
"config_entry": async_redact_data(config_entry.as_dict(), TO_REDACT)
}
# not every device uses DataUpdateCoordinator
if coordinator := hass.data[DOMAIN][config_entry.entry_id].get(KEY_COORDINATOR):
if config_entry.data[CONF_FLOW_TYPE] == CONF_DEVICE:
coordinator = config_entry.runtime_data.device_coordinator
if isinstance(coordinator.data, dict):
diagnostics_data["coordinator_data"] = coordinator.data
else:
diagnostics_data["coordinator_data"] = repr(coordinator.data)
return diagnostics_data

View File

@ -30,7 +30,6 @@ from miio.integrations.fan.zhimi.zhimi_miot import (
import voluptuous as vol
from homeassistant.components.fan import FanEntity, FanEntityFeature
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_ENTITY_ID, CONF_DEVICE, CONF_MODEL
from homeassistant.core import HomeAssistant, ServiceCall, callback
from homeassistant.helpers import config_validation as cv
@ -64,8 +63,6 @@ from .const import (
FEATURE_FLAGS_FAN_ZA5,
FEATURE_RESET_FILTER,
FEATURE_SET_EXTRA_FEATURES,
KEY_COORDINATOR,
KEY_DEVICE,
MODEL_AIRFRESH_A1,
MODEL_AIRFRESH_T2017,
MODEL_AIRPURIFIER_2H,
@ -94,7 +91,7 @@ from .const import (
SERVICE_SET_EXTRA_FEATURES,
)
from .entity import XiaomiCoordinatedMiioEntity
from .typing import ServiceMethodDetails
from .typing import ServiceMethodDetails, XiaomiMiioConfigEntry
_LOGGER = logging.getLogger(__name__)
@ -204,7 +201,7 @@ FAN_DIRECTIONS_MAP = {
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
config_entry: XiaomiMiioConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the Fan from a config entry."""
@ -218,8 +215,8 @@ async def async_setup_entry(
model = config_entry.data[CONF_MODEL]
unique_id = config_entry.unique_id
coordinator = hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR]
device = hass.data[DOMAIN][config_entry.entry_id][KEY_DEVICE]
device = config_entry.runtime_data.device
coordinator = config_entry.runtime_data.device_coordinator
if model in (MODEL_AIRPURIFIER_3C, MODEL_AIRPURIFIER_3C_REV_A):
entity = XiaomiAirPurifierMB4(

View File

@ -20,7 +20,6 @@ from homeassistant.components.humidifier import (
HumidifierEntity,
HumidifierEntityFeature,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_MODE, CONF_DEVICE, CONF_MODEL
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
@ -28,9 +27,6 @@ from homeassistant.util.percentage import percentage_to_ranged_value
from .const import (
CONF_FLOW_TYPE,
DOMAIN,
KEY_COORDINATOR,
KEY_DEVICE,
MODEL_AIRHUMIDIFIER_CA1,
MODEL_AIRHUMIDIFIER_CA4,
MODEL_AIRHUMIDIFIER_CB1,
@ -38,6 +34,7 @@ from .const import (
MODELS_HUMIDIFIER_MJJSQ,
)
from .entity import XiaomiCoordinatedMiioEntity
from .typing import XiaomiMiioConfigEntry
_LOGGER = logging.getLogger(__name__)
@ -70,7 +67,7 @@ AVAILABLE_MODES_OTHER = [
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
config_entry: XiaomiMiioConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the Humidifier from a config entry."""
@ -81,28 +78,26 @@ async def async_setup_entry(
entity: HumidifierEntity
model = config_entry.data[CONF_MODEL]
unique_id = config_entry.unique_id
coordinator = hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR]
device = config_entry.runtime_data.device
coordinator = config_entry.runtime_data.device_coordinator
if model in MODELS_HUMIDIFIER_MIOT:
air_humidifier = hass.data[DOMAIN][config_entry.entry_id][KEY_DEVICE]
entity = XiaomiAirHumidifierMiot(
air_humidifier,
device,
config_entry,
unique_id,
coordinator,
)
elif model in MODELS_HUMIDIFIER_MJJSQ:
air_humidifier = hass.data[DOMAIN][config_entry.entry_id][KEY_DEVICE]
entity = XiaomiAirHumidifierMjjsq(
air_humidifier,
device,
config_entry,
unique_id,
coordinator,
)
else:
air_humidifier = hass.data[DOMAIN][config_entry.entry_id][KEY_DEVICE]
entity = XiaomiAirHumidifier(
air_humidifier,
device,
config_entry,
unique_id,
coordinator,

View File

@ -33,7 +33,6 @@ from homeassistant.components.light import (
ColorMode,
LightEntity,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
ATTR_ENTITY_ID,
CONF_DEVICE,
@ -51,7 +50,6 @@ from .const import (
CONF_FLOW_TYPE,
CONF_GATEWAY,
DOMAIN,
KEY_COORDINATOR,
MODELS_LIGHT_BULB,
MODELS_LIGHT_CEILING,
MODELS_LIGHT_EYECARE,
@ -67,7 +65,7 @@ from .const import (
SERVICE_SET_SCENE,
)
from .entity import XiaomiGatewayDevice, XiaomiMiioEntity
from .typing import ServiceMethodDetails
from .typing import ServiceMethodDetails, XiaomiMiioConfigEntry
_LOGGER = logging.getLogger(__name__)
@ -131,7 +129,7 @@ SERVICE_TO_METHOD = {
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
config_entry: XiaomiMiioConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the Xiaomi light from a config entry."""
@ -140,7 +138,7 @@ async def async_setup_entry(
light: MiioDevice
if config_entry.data[CONF_FLOW_TYPE] == CONF_GATEWAY:
gateway = hass.data[DOMAIN][config_entry.entry_id][CONF_GATEWAY]
gateway = config_entry.runtime_data.gateway
# Gateway light
if gateway.model not in [
GATEWAY_MODEL_AC_V1,
@ -154,7 +152,7 @@ async def async_setup_entry(
sub_devices = gateway.devices
for sub_device in sub_devices.values():
if sub_device.device_type == "LightBulb":
coordinator = hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR][
coordinator = config_entry.runtime_data.gateway_coordinators[
sub_device.sid
]
entities.append(

View File

@ -12,7 +12,6 @@ from homeassistant.components.number import (
NumberEntity,
NumberEntityDescription,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONF_DEVICE,
CONF_MODEL,
@ -61,8 +60,6 @@ from .const import (
FEATURE_SET_MOTOR_SPEED,
FEATURE_SET_OSCILLATION_ANGLE,
FEATURE_SET_VOLUME,
KEY_COORDINATOR,
KEY_DEVICE,
MODEL_AIRFRESH_A1,
MODEL_AIRFRESH_T2017,
MODEL_AIRFRESH_VA2,
@ -99,6 +96,7 @@ from .const import (
MODELS_PURIFIER_MIOT,
)
from .entity import XiaomiCoordinatedMiioEntity
from .typing import XiaomiMiioConfigEntry
ATTR_DELAY_OFF_COUNTDOWN = "delay_off_countdown"
ATTR_FAN_LEVEL = "fan_level"
@ -288,7 +286,7 @@ FAVORITE_LEVEL_VALUES = {
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
config_entry: XiaomiMiioConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the Selectors from a config entry."""
@ -296,7 +294,8 @@ async def async_setup_entry(
if config_entry.data[CONF_FLOW_TYPE] != CONF_DEVICE:
return
model = config_entry.data[CONF_MODEL]
device = hass.data[DOMAIN][config_entry.entry_id][KEY_DEVICE]
device = config_entry.runtime_data.device
coordinator = config_entry.runtime_data.device_coordinator
if model in MODEL_TO_FEATURES_MAP:
features = MODEL_TO_FEATURES_MAP[model]
@ -343,7 +342,7 @@ async def async_setup_entry(
device,
config_entry,
f"{description.key}_{config_entry.unique_id}",
hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR],
coordinator,
description,
)
)
@ -359,7 +358,7 @@ class XiaomiNumberEntity(XiaomiCoordinatedMiioEntity, NumberEntity):
def __init__(
self,
device: Device,
entry: ConfigEntry,
entry: XiaomiMiioConfigEntry,
unique_id: str,
coordinator: DataUpdateCoordinator,
description: XiaomiMiioNumberDescription,

View File

@ -29,16 +29,12 @@ from miio.integrations.humidifier.zhimi.airhumidifier_miot import (
)
from homeassistant.components.select import SelectEntity, SelectEntityDescription
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_DEVICE, CONF_MODEL, EntityCategory
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import (
CONF_FLOW_TYPE,
DOMAIN,
KEY_COORDINATOR,
KEY_DEVICE,
MODEL_AIRFRESH_T2017,
MODEL_AIRFRESH_VA2,
MODEL_AIRFRESH_VA4,
@ -64,6 +60,7 @@ from .const import (
MODEL_FAN_ZA4,
)
from .entity import XiaomiCoordinatedMiioEntity
from .typing import XiaomiMiioConfigEntry
ATTR_DISPLAY_ORIENTATION = "display_orientation"
ATTR_LED_BRIGHTNESS = "led_brightness"
@ -204,7 +201,7 @@ SELECTOR_TYPES = (
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
config_entry: XiaomiMiioConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the Selectors from a config entry."""
@ -216,8 +213,8 @@ async def async_setup_entry(
return
unique_id = config_entry.unique_id
device = hass.data[DOMAIN][config_entry.entry_id][KEY_DEVICE]
coordinator = hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR]
device = config_entry.runtime_data.device
coordinator = config_entry.runtime_data.device_coordinator
attributes = MODEL_TO_ATTR_MAP[model]
async_add_entities(

View File

@ -5,8 +5,9 @@ from __future__ import annotations
from collections.abc import Iterable
from dataclasses import dataclass
import logging
from typing import TYPE_CHECKING
from miio import AirQualityMonitor, DeviceException
from miio import AirQualityMonitor, Device as MiioDevice, DeviceException
from miio.gateway.gateway import (
GATEWAY_MODEL_AC_V1,
GATEWAY_MODEL_AC_V2,
@ -22,7 +23,6 @@ from homeassistant.components.sensor import (
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
ATTR_BATTERY_LEVEL,
ATTR_TEMPERATURE,
@ -53,8 +53,6 @@ from .const import (
CONF_FLOW_TYPE,
CONF_GATEWAY,
DOMAIN,
KEY_COORDINATOR,
KEY_DEVICE,
MODEL_AIRFRESH_A1,
MODEL_AIRFRESH_T2017,
MODEL_AIRFRESH_VA2,
@ -91,6 +89,7 @@ from .const import (
ROCKROBO_GENERIC,
)
from .entity import XiaomiCoordinatedMiioEntity, XiaomiGatewayDevice, XiaomiMiioEntity
from .typing import XiaomiMiioConfigEntry
_LOGGER = logging.getLogger(__name__)
@ -724,13 +723,19 @@ VACUUM_SENSORS = {
}
def _setup_vacuum_sensors(hass, config_entry, async_add_entities):
def _setup_vacuum_sensors(
hass: HomeAssistant,
config_entry: XiaomiMiioConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the Xiaomi vacuum sensors."""
device = hass.data[DOMAIN][config_entry.entry_id].get(KEY_DEVICE)
coordinator = hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR]
device = config_entry.runtime_data.device
coordinator = config_entry.runtime_data.device_coordinator
entities = []
for sensor, description in VACUUM_SENSORS.items():
if TYPE_CHECKING:
assert description.parent_key is not None
parent_key_data = getattr(coordinator.data, description.parent_key)
if getattr(parent_key_data, description.key, None) is None:
_LOGGER.debug(
@ -754,14 +759,14 @@ def _setup_vacuum_sensors(hass, config_entry, async_add_entities):
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
config_entry: XiaomiMiioConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the Xiaomi sensor from a config entry."""
entities: list[SensorEntity] = []
if config_entry.data[CONF_FLOW_TYPE] == CONF_GATEWAY:
gateway = hass.data[DOMAIN][config_entry.entry_id][CONF_GATEWAY]
gateway = config_entry.runtime_data.gateway
# Gateway illuminance sensor
if gateway.model not in [
GATEWAY_MODEL_AC_V1,
@ -779,9 +784,7 @@ async def async_setup_entry(
# Gateway sub devices
sub_devices = gateway.devices
for sub_device in sub_devices.values():
coordinator = hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR][
sub_device.sid
]
coordinator = config_entry.runtime_data.gateway_coordinators[sub_device.sid]
for sensor, description in SENSOR_TYPES.items():
if sensor not in sub_device.status:
continue
@ -791,6 +794,7 @@ async def async_setup_entry(
)
)
elif config_entry.data[CONF_FLOW_TYPE] == CONF_DEVICE:
device: MiioDevice
host = config_entry.data[CONF_HOST]
token = config_entry.data[CONF_TOKEN]
model: str = config_entry.data[CONF_MODEL]
@ -811,7 +815,8 @@ async def async_setup_entry(
)
)
else:
device = hass.data[DOMAIN][config_entry.entry_id][KEY_DEVICE]
device = config_entry.runtime_data.device
coordinator = config_entry.runtime_data.device_coordinator
sensors: Iterable[str] = []
if model in MODEL_TO_SENSORS_MAP:
sensors = MODEL_TO_SENSORS_MAP[model]
@ -839,7 +844,7 @@ async def async_setup_entry(
device,
config_entry,
f"{sensor}_{config_entry.unique_id}",
hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR],
coordinator,
description,
)
)

View File

@ -17,7 +17,6 @@ from homeassistant.components.switch import (
SwitchEntity,
SwitchEntityDescription,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
ATTR_ENTITY_ID,
ATTR_MODE,
@ -72,8 +71,6 @@ from .const import (
FEATURE_SET_LEARN_MODE,
FEATURE_SET_LED,
FEATURE_SET_PTC,
KEY_COORDINATOR,
KEY_DEVICE,
MODEL_AIRFRESH_A1,
MODEL_AIRFRESH_T2017,
MODEL_AIRFRESH_VA2,
@ -116,7 +113,7 @@ from .const import (
SUCCESS,
)
from .entity import XiaomiCoordinatedMiioEntity, XiaomiGatewayDevice, XiaomiMiioEntity
from .typing import ServiceMethodDetails
from .typing import ServiceMethodDetails, XiaomiMiioConfigEntry
_LOGGER = logging.getLogger(__name__)
@ -340,7 +337,7 @@ SWITCH_TYPES = (
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
config_entry: XiaomiMiioConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the switch from a config entry."""
@ -351,12 +348,16 @@ async def async_setup_entry(
await async_setup_other_entry(hass, config_entry, async_add_entities)
async def async_setup_coordinated_entry(hass, config_entry, async_add_entities):
async def async_setup_coordinated_entry(
hass: HomeAssistant,
config_entry: XiaomiMiioConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the coordinated switch from a config entry."""
model = config_entry.data[CONF_MODEL]
unique_id = config_entry.unique_id
device = hass.data[DOMAIN][config_entry.entry_id][KEY_DEVICE]
coordinator = hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR]
device = config_entry.runtime_data.device
coordinator = config_entry.runtime_data.device_coordinator
if DATA_KEY not in hass.data:
hass.data[DATA_KEY] = {}
@ -387,24 +388,26 @@ async def async_setup_coordinated_entry(hass, config_entry, async_add_entities):
)
async def async_setup_other_entry(hass, config_entry, async_add_entities):
async def async_setup_other_entry(
hass: HomeAssistant,
config_entry: XiaomiMiioConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the other type switch from a config entry."""
entities = []
entities: list[SwitchEntity] = []
host = config_entry.data[CONF_HOST]
token = config_entry.data[CONF_TOKEN]
name = config_entry.title
model = config_entry.data[CONF_MODEL]
unique_id = config_entry.unique_id
if config_entry.data[CONF_FLOW_TYPE] == CONF_GATEWAY:
gateway = hass.data[DOMAIN][config_entry.entry_id][CONF_GATEWAY]
gateway = config_entry.runtime_data.gateway
# Gateway sub devices
sub_devices = gateway.devices
for sub_device in sub_devices.values():
if sub_device.device_type != "Switch":
continue
coordinator = hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR][
sub_device.sid
]
coordinator = config_entry.runtime_data.gateway_coordinators[sub_device.sid]
switch_variables = set(sub_device.status) & set(GATEWAY_SWITCH_VARS)
if switch_variables:
entities.extend(
@ -420,13 +423,14 @@ async def async_setup_other_entry(hass, config_entry, async_add_entities):
config_entry.data[CONF_FLOW_TYPE] == CONF_GATEWAY
and model == "lumi.acpartner.v3"
):
device: SwitchEntity
if DATA_KEY not in hass.data:
hass.data[DATA_KEY] = {}
_LOGGER.debug("Initializing with host %s (token %s...)", host, token[:5])
if model in ["chuangmi.plug.v1", "chuangmi.plug.v3", "chuangmi.plug.hmi208"]:
plug = ChuangmiPlug(host, token, model=model)
chuangmi_plug = ChuangmiPlug(host, token, model=model)
# The device has two switchable channels (mains and a USB port).
# A switch device per channel will be created.
@ -436,13 +440,13 @@ async def async_setup_other_entry(hass, config_entry, async_add_entities):
else:
unique_id_ch = f"{unique_id}-mains"
device = ChuangMiPlugSwitch(
name, plug, config_entry, unique_id_ch, channel_usb
name, chuangmi_plug, config_entry, unique_id_ch, channel_usb
)
entities.append(device)
hass.data[DATA_KEY][host] = device
elif model in ["qmi.powerstrip.v1", "zimi.powerstrip.v2"]:
plug = PowerStrip(host, token, model=model)
device = XiaomiPowerStripSwitch(name, plug, config_entry, unique_id)
power_strip = PowerStrip(host, token, model=model)
device = XiaomiPowerStripSwitch(name, power_strip, config_entry, unique_id)
entities.append(device)
hass.data[DATA_KEY][host] = device
elif model in [
@ -452,14 +456,16 @@ async def async_setup_other_entry(hass, config_entry, async_add_entities):
"chuangmi.plug.hmi205",
"chuangmi.plug.hmi206",
]:
plug = ChuangmiPlug(host, token, model=model)
device = XiaomiPlugGenericSwitch(name, plug, config_entry, unique_id)
chuangmi_plug = ChuangmiPlug(host, token, model=model)
device = XiaomiPlugGenericSwitch(
name, chuangmi_plug, config_entry, unique_id
)
entities.append(device)
hass.data[DATA_KEY][host] = device
elif model in ["lumi.acpartner.v3"]:
plug = AirConditioningCompanionV3(host, token)
ac_companion = AirConditioningCompanionV3(host, token)
device = XiaomiAirConditioningCompanionSwitch(
name, plug, config_entry, unique_id
name, ac_companion, config_entry, unique_id
)
entities.append(device)
hass.data[DATA_KEY][host] = device

View File

@ -1,12 +1,36 @@
"""Typings for the xiaomi_miio integration."""
from typing import NamedTuple
from dataclasses import dataclass
from typing import Any, NamedTuple
from miio import Device as MiioDevice
from miio.gateway.gateway import Gateway
import voluptuous as vol
from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
class ServiceMethodDetails(NamedTuple):
"""Details for SERVICE_TO_METHOD mapping."""
method: str
schema: vol.Schema | None = None
@dataclass
class XiaomiMiioRuntimeData:
"""Runtime data for Xiaomi Miio config entry.
Either device/device_coordinator or gateway/gateway_coordinators
must be set, based on CONF_FLOW_TYPE (CONF_DEVICE or CONF_GATEWAY)
"""
device: MiioDevice = None # type: ignore[assignment]
device_coordinator: DataUpdateCoordinator[Any] = None # type: ignore[assignment]
gateway: Gateway = None # type: ignore[assignment]
gateway_coordinators: dict[str, DataUpdateCoordinator[dict[str, bool]]] = None # type: ignore[assignment]
type XiaomiMiioConfigEntry = ConfigEntry[XiaomiMiioRuntimeData]

View File

@ -14,7 +14,6 @@ from homeassistant.components.vacuum import (
VacuumActivity,
VacuumEntityFeature,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_DEVICE
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import config_validation as cv, entity_platform
@ -25,9 +24,6 @@ from homeassistant.util.dt import as_utc
from . import VacuumCoordinatorData
from .const import (
CONF_FLOW_TYPE,
DOMAIN,
KEY_COORDINATOR,
KEY_DEVICE,
SERVICE_CLEAN_SEGMENT,
SERVICE_CLEAN_ZONE,
SERVICE_GOTO,
@ -37,6 +33,7 @@ from .const import (
SERVICE_STOP_REMOTE_CONTROL,
)
from .entity import XiaomiCoordinatedMiioEntity
from .typing import XiaomiMiioConfigEntry
_LOGGER = logging.getLogger(__name__)
@ -78,7 +75,7 @@ STATE_CODE_TO_STATE = {
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
config_entry: XiaomiMiioConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the Xiaomi vacuum cleaner robot from a config entry."""
@ -88,10 +85,10 @@ async def async_setup_entry(
unique_id = config_entry.unique_id
mirobo = MiroboVacuum(
hass.data[DOMAIN][config_entry.entry_id][KEY_DEVICE],
config_entry.runtime_data.device,
config_entry,
unique_id,
hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR],
config_entry.runtime_data.device_coordinator,
)
entities.append(mirobo)