Add coordinator to Daikin (#124394)
* Add coordinator to Daikin * Add coordinator to Daikin * Fix * Add secondspull/125313/head^2
parent
97c55ae6f1
commit
130b6559a6
|
@ -3,9 +3,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from aiohttp import ClientConnectionError
|
||||
from pydaikin.daikin_base import Appliance
|
||||
|
@ -23,15 +21,13 @@ from homeassistant.core import HomeAssistant, callback
|
|||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo
|
||||
from homeassistant.util import Throttle
|
||||
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
|
||||
|
||||
from .const import DOMAIN, KEY_MAC, TIMEOUT
|
||||
from .coordinator import DaikinCoordinator
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
PARALLEL_UPDATES = 0
|
||||
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60)
|
||||
|
||||
PLATFORMS = [Platform.CLIMATE, Platform.SENSOR, Platform.SWITCH]
|
||||
|
||||
|
@ -43,19 +39,32 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
if entry.unique_id is None or ".local" in entry.unique_id:
|
||||
hass.config_entries.async_update_entry(entry, unique_id=conf[KEY_MAC])
|
||||
|
||||
daikin_api = await daikin_api_setup(
|
||||
hass,
|
||||
conf[CONF_HOST],
|
||||
conf.get(CONF_API_KEY),
|
||||
conf.get(CONF_UUID),
|
||||
conf.get(CONF_PASSWORD),
|
||||
)
|
||||
if not daikin_api:
|
||||
return False
|
||||
session = async_get_clientsession(hass)
|
||||
host = conf[CONF_HOST]
|
||||
try:
|
||||
async with asyncio.timeout(TIMEOUT):
|
||||
device: Appliance = await DaikinFactory(
|
||||
host,
|
||||
session,
|
||||
key=entry.data.get(CONF_API_KEY),
|
||||
uuid=entry.data.get(CONF_UUID),
|
||||
password=entry.data.get(CONF_PASSWORD),
|
||||
)
|
||||
_LOGGER.debug("Connection to %s successful", host)
|
||||
except TimeoutError as err:
|
||||
_LOGGER.debug("Connection to %s timed out in 60 seconds", host)
|
||||
raise ConfigEntryNotReady from err
|
||||
except ClientConnectionError as err:
|
||||
_LOGGER.debug("ClientConnectionError to %s", host)
|
||||
raise ConfigEntryNotReady from err
|
||||
|
||||
await async_migrate_unique_id(hass, entry, daikin_api)
|
||||
coordinator = DaikinCoordinator(hass, device)
|
||||
|
||||
hass.data.setdefault(DOMAIN, {}).update({entry.entry_id: daikin_api})
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
await async_migrate_unique_id(hass, entry, device)
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
return True
|
||||
|
||||
|
@ -70,83 +79,16 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
return unload_ok
|
||||
|
||||
|
||||
async def daikin_api_setup(
|
||||
hass: HomeAssistant,
|
||||
host: str,
|
||||
key: str | None,
|
||||
uuid: str | None,
|
||||
password: str | None,
|
||||
) -> DaikinApi | None:
|
||||
"""Create a Daikin instance only once."""
|
||||
|
||||
session = async_get_clientsession(hass)
|
||||
try:
|
||||
async with asyncio.timeout(TIMEOUT):
|
||||
device: Appliance = await DaikinFactory(
|
||||
host, session, key=key, uuid=uuid, password=password
|
||||
)
|
||||
_LOGGER.debug("Connection to %s successful", host)
|
||||
except TimeoutError as err:
|
||||
_LOGGER.debug("Connection to %s timed out", host)
|
||||
raise ConfigEntryNotReady from err
|
||||
except ClientConnectionError as err:
|
||||
_LOGGER.debug("ClientConnectionError to %s", host)
|
||||
raise ConfigEntryNotReady from err
|
||||
except Exception: # noqa: BLE001
|
||||
_LOGGER.error("Unexpected error creating device %s", host)
|
||||
return None
|
||||
|
||||
return DaikinApi(device)
|
||||
|
||||
|
||||
class DaikinApi:
|
||||
"""Keep the Daikin instance in one place and centralize the update."""
|
||||
|
||||
def __init__(self, device: Appliance) -> None:
|
||||
"""Initialize the Daikin Handle."""
|
||||
self.device = device
|
||||
self.name = device.values.get("name", "Daikin AC")
|
||||
self.ip_address = device.device_ip
|
||||
self._available = True
|
||||
|
||||
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||
async def async_update(self, **kwargs: Any) -> None:
|
||||
"""Pull the latest data from Daikin."""
|
||||
try:
|
||||
await self.device.update_status()
|
||||
self._available = True
|
||||
except ClientConnectionError:
|
||||
_LOGGER.warning("Connection failed for %s", self.ip_address)
|
||||
self._available = False
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return True if entity is available."""
|
||||
return self._available
|
||||
|
||||
@property
|
||||
def device_info(self) -> DeviceInfo:
|
||||
"""Return a device description for device registry."""
|
||||
info = self.device.values
|
||||
return DeviceInfo(
|
||||
connections={(CONNECTION_NETWORK_MAC, self.device.mac)},
|
||||
manufacturer="Daikin",
|
||||
model=info.get("model"),
|
||||
name=info.get("name"),
|
||||
sw_version=info.get("ver", "").replace("_", "."),
|
||||
)
|
||||
|
||||
|
||||
async def async_migrate_unique_id(
|
||||
hass: HomeAssistant, config_entry: ConfigEntry, api: DaikinApi
|
||||
hass: HomeAssistant, config_entry: ConfigEntry, device: Appliance
|
||||
) -> None:
|
||||
"""Migrate old entry."""
|
||||
dev_reg = dr.async_get(hass)
|
||||
ent_reg = er.async_get(hass)
|
||||
old_unique_id = config_entry.unique_id
|
||||
new_unique_id = api.device.mac
|
||||
new_unique_id = device.mac
|
||||
new_mac = dr.format_mac(new_unique_id)
|
||||
new_name = api.name
|
||||
new_name = device.values.get("name", "Daikin AC")
|
||||
|
||||
@callback
|
||||
def _update_unique_id(entity_entry: er.RegistryEntry) -> dict[str, str] | None:
|
||||
|
|
|
@ -34,7 +34,7 @@ import homeassistant.helpers.config_validation as cv
|
|||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
|
||||
from . import DOMAIN as DAIKIN_DOMAIN, DaikinApi
|
||||
from . import DOMAIN as DAIKIN_DOMAIN
|
||||
from .const import (
|
||||
ATTR_INSIDE_TEMPERATURE,
|
||||
ATTR_OUTSIDE_TEMPERATURE,
|
||||
|
@ -42,6 +42,8 @@ from .const import (
|
|||
ATTR_STATE_ON,
|
||||
ATTR_TARGET_TEMPERATURE,
|
||||
)
|
||||
from .coordinator import DaikinCoordinator
|
||||
from .entity import DaikinEntity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -111,7 +113,7 @@ async def async_setup_entry(
|
|||
) -> None:
|
||||
"""Set up Daikin climate based on config_entry."""
|
||||
daikin_api = hass.data[DAIKIN_DOMAIN].get(entry.entry_id)
|
||||
async_add_entities([DaikinClimate(daikin_api)], update_before_add=True)
|
||||
async_add_entities([DaikinClimate(daikin_api)])
|
||||
|
||||
|
||||
def format_target_temperature(target_temperature: float) -> str:
|
||||
|
@ -119,11 +121,10 @@ def format_target_temperature(target_temperature: float) -> str:
|
|||
return str(round(float(target_temperature) * 2, 0) / 2).rstrip("0").rstrip(".")
|
||||
|
||||
|
||||
class DaikinClimate(ClimateEntity):
|
||||
class DaikinClimate(DaikinEntity, ClimateEntity):
|
||||
"""Representation of a Daikin HVAC."""
|
||||
|
||||
_attr_name = None
|
||||
_attr_has_entity_name = True
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_attr_hvac_modes = list(HA_STATE_TO_DAIKIN)
|
||||
_attr_target_temperature_step = 1
|
||||
|
@ -131,13 +132,11 @@ class DaikinClimate(ClimateEntity):
|
|||
_attr_swing_modes: list[str]
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(self, api: DaikinApi) -> None:
|
||||
def __init__(self, coordinator: DaikinCoordinator) -> None:
|
||||
"""Initialize the climate device."""
|
||||
|
||||
self._api = api
|
||||
self._attr_fan_modes = api.device.fan_rate
|
||||
self._attr_swing_modes = api.device.swing_modes
|
||||
self._attr_device_info = api.device_info
|
||||
super().__init__(coordinator)
|
||||
self._attr_fan_modes = self.device.fan_rate
|
||||
self._attr_swing_modes = self.device.swing_modes
|
||||
self._list: dict[str, list[Any]] = {
|
||||
ATTR_HVAC_MODE: self._attr_hvac_modes,
|
||||
ATTR_FAN_MODE: self._attr_fan_modes,
|
||||
|
@ -150,13 +149,13 @@ class DaikinClimate(ClimateEntity):
|
|||
| ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
)
|
||||
|
||||
if api.device.support_away_mode or api.device.support_advanced_modes:
|
||||
if self.device.support_away_mode or self.device.support_advanced_modes:
|
||||
self._attr_supported_features |= ClimateEntityFeature.PRESET_MODE
|
||||
|
||||
if api.device.support_fan_rate:
|
||||
if self.device.support_fan_rate:
|
||||
self._attr_supported_features |= ClimateEntityFeature.FAN_MODE
|
||||
|
||||
if api.device.support_swing_mode:
|
||||
if self.device.support_swing_mode:
|
||||
self._attr_supported_features |= ClimateEntityFeature.SWING_MODE
|
||||
|
||||
async def _set(self, settings: dict[str, Any]) -> None:
|
||||
|
@ -185,22 +184,22 @@ class DaikinClimate(ClimateEntity):
|
|||
_LOGGER.error("Invalid temperature %s", value)
|
||||
|
||||
if values:
|
||||
await self._api.device.set(values)
|
||||
await self.device.set(values)
|
||||
|
||||
@property
|
||||
def unique_id(self) -> str:
|
||||
"""Return a unique ID."""
|
||||
return self._api.device.mac
|
||||
return self.device.mac
|
||||
|
||||
@property
|
||||
def current_temperature(self) -> float | None:
|
||||
"""Return the current temperature."""
|
||||
return self._api.device.inside_temperature
|
||||
return self.device.inside_temperature
|
||||
|
||||
@property
|
||||
def target_temperature(self) -> float | None:
|
||||
"""Return the temperature we try to reach."""
|
||||
return self._api.device.target_temperature
|
||||
return self.device.target_temperature
|
||||
|
||||
async def async_set_temperature(self, **kwargs: Any) -> None:
|
||||
"""Set new target temperature."""
|
||||
|
@ -212,8 +211,8 @@ class DaikinClimate(ClimateEntity):
|
|||
ret = HA_STATE_TO_CURRENT_HVAC.get(self.hvac_mode)
|
||||
if (
|
||||
ret in (HVACAction.COOLING, HVACAction.HEATING)
|
||||
and self._api.device.support_compressor_frequency
|
||||
and self._api.device.compressor_frequency == 0
|
||||
and self.device.support_compressor_frequency
|
||||
and self.device.compressor_frequency == 0
|
||||
):
|
||||
return HVACAction.IDLE
|
||||
return ret
|
||||
|
@ -221,7 +220,7 @@ class DaikinClimate(ClimateEntity):
|
|||
@property
|
||||
def hvac_mode(self) -> HVACMode:
|
||||
"""Return current operation ie. heat, cool, idle."""
|
||||
daikin_mode = self._api.device.represent(HA_ATTR_TO_DAIKIN[ATTR_HVAC_MODE])[1]
|
||||
daikin_mode = self.device.represent(HA_ATTR_TO_DAIKIN[ATTR_HVAC_MODE])[1]
|
||||
return DAIKIN_TO_HA_STATE.get(daikin_mode, HVACMode.HEAT_COOL)
|
||||
|
||||
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
|
||||
|
@ -231,7 +230,7 @@ class DaikinClimate(ClimateEntity):
|
|||
@property
|
||||
def fan_mode(self) -> str:
|
||||
"""Return the fan setting."""
|
||||
return self._api.device.represent(HA_ATTR_TO_DAIKIN[ATTR_FAN_MODE])[1].title()
|
||||
return self.device.represent(HA_ATTR_TO_DAIKIN[ATTR_FAN_MODE])[1].title()
|
||||
|
||||
async def async_set_fan_mode(self, fan_mode: str) -> None:
|
||||
"""Set fan mode."""
|
||||
|
@ -240,7 +239,7 @@ class DaikinClimate(ClimateEntity):
|
|||
@property
|
||||
def swing_mode(self) -> str:
|
||||
"""Return the fan setting."""
|
||||
return self._api.device.represent(HA_ATTR_TO_DAIKIN[ATTR_SWING_MODE])[1].title()
|
||||
return self.device.represent(HA_ATTR_TO_DAIKIN[ATTR_SWING_MODE])[1].title()
|
||||
|
||||
async def async_set_swing_mode(self, swing_mode: str) -> None:
|
||||
"""Set new target temperature."""
|
||||
|
@ -250,18 +249,18 @@ class DaikinClimate(ClimateEntity):
|
|||
def preset_mode(self) -> str:
|
||||
"""Return the preset_mode."""
|
||||
if (
|
||||
self._api.device.represent(HA_ATTR_TO_DAIKIN[ATTR_PRESET_MODE])[1]
|
||||
self.device.represent(HA_ATTR_TO_DAIKIN[ATTR_PRESET_MODE])[1]
|
||||
== HA_PRESET_TO_DAIKIN[PRESET_AWAY]
|
||||
):
|
||||
return PRESET_AWAY
|
||||
if (
|
||||
HA_PRESET_TO_DAIKIN[PRESET_BOOST]
|
||||
in self._api.device.represent(DAIKIN_ATTR_ADVANCED)[1]
|
||||
in self.device.represent(DAIKIN_ATTR_ADVANCED)[1]
|
||||
):
|
||||
return PRESET_BOOST
|
||||
if (
|
||||
HA_PRESET_TO_DAIKIN[PRESET_ECO]
|
||||
in self._api.device.represent(DAIKIN_ATTR_ADVANCED)[1]
|
||||
in self.device.represent(DAIKIN_ATTR_ADVANCED)[1]
|
||||
):
|
||||
return PRESET_ECO
|
||||
return PRESET_NONE
|
||||
|
@ -269,23 +268,23 @@ class DaikinClimate(ClimateEntity):
|
|||
async def async_set_preset_mode(self, preset_mode: str) -> None:
|
||||
"""Set preset mode."""
|
||||
if preset_mode == PRESET_AWAY:
|
||||
await self._api.device.set_holiday(ATTR_STATE_ON)
|
||||
await self.device.set_holiday(ATTR_STATE_ON)
|
||||
elif preset_mode == PRESET_BOOST:
|
||||
await self._api.device.set_advanced_mode(
|
||||
await self.device.set_advanced_mode(
|
||||
HA_PRESET_TO_DAIKIN[PRESET_BOOST], ATTR_STATE_ON
|
||||
)
|
||||
elif preset_mode == PRESET_ECO:
|
||||
await self._api.device.set_advanced_mode(
|
||||
await self.device.set_advanced_mode(
|
||||
HA_PRESET_TO_DAIKIN[PRESET_ECO], ATTR_STATE_ON
|
||||
)
|
||||
elif self.preset_mode == PRESET_AWAY:
|
||||
await self._api.device.set_holiday(ATTR_STATE_OFF)
|
||||
await self.device.set_holiday(ATTR_STATE_OFF)
|
||||
elif self.preset_mode == PRESET_BOOST:
|
||||
await self._api.device.set_advanced_mode(
|
||||
await self.device.set_advanced_mode(
|
||||
HA_PRESET_TO_DAIKIN[PRESET_BOOST], ATTR_STATE_OFF
|
||||
)
|
||||
elif self.preset_mode == PRESET_ECO:
|
||||
await self._api.device.set_advanced_mode(
|
||||
await self.device.set_advanced_mode(
|
||||
HA_PRESET_TO_DAIKIN[PRESET_ECO], ATTR_STATE_OFF
|
||||
)
|
||||
|
||||
|
@ -293,22 +292,18 @@ class DaikinClimate(ClimateEntity):
|
|||
def preset_modes(self) -> list[str]:
|
||||
"""List of available preset modes."""
|
||||
ret = [PRESET_NONE]
|
||||
if self._api.device.support_away_mode:
|
||||
if self.device.support_away_mode:
|
||||
ret.append(PRESET_AWAY)
|
||||
if self._api.device.support_advanced_modes:
|
||||
if self.device.support_advanced_modes:
|
||||
ret += [PRESET_ECO, PRESET_BOOST]
|
||||
return ret
|
||||
|
||||
async def async_update(self) -> None:
|
||||
"""Retrieve latest state."""
|
||||
await self._api.async_update()
|
||||
|
||||
async def async_turn_on(self) -> None:
|
||||
"""Turn device on."""
|
||||
await self._api.device.set({})
|
||||
await self.device.set({})
|
||||
|
||||
async def async_turn_off(self) -> None:
|
||||
"""Turn device off."""
|
||||
await self._api.device.set(
|
||||
await self.device.set(
|
||||
{HA_ATTR_TO_DAIKIN[ATTR_HVAC_MODE]: HA_STATE_TO_DAIKIN[HVACMode.OFF]}
|
||||
)
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
"""Coordinator for Daikin integration."""
|
||||
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
from pydaikin.daikin_base import Appliance
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DaikinCoordinator(DataUpdateCoordinator[None]):
|
||||
"""Class to manage fetching Daikin data."""
|
||||
|
||||
def __init__(self, hass: HomeAssistant, device: Appliance) -> None:
|
||||
"""Initialize global Daikin data updater."""
|
||||
super().__init__(
|
||||
hass,
|
||||
_LOGGER,
|
||||
name=device.values.get("name", DOMAIN),
|
||||
update_interval=timedelta(seconds=60),
|
||||
)
|
||||
self.device = device
|
||||
|
||||
async def _async_update_data(self) -> None:
|
||||
await self.device.update_status()
|
|
@ -0,0 +1,25 @@
|
|||
"""Base entity for Daikin."""
|
||||
|
||||
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .coordinator import DaikinCoordinator
|
||||
|
||||
|
||||
class DaikinEntity(CoordinatorEntity[DaikinCoordinator]):
|
||||
"""Base entity for Daikin."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(self, coordinator: DaikinCoordinator) -> None:
|
||||
"""Initialize the entity."""
|
||||
super().__init__(coordinator)
|
||||
self.device = coordinator.device
|
||||
info = self.device.values
|
||||
self._attr_device_info = DeviceInfo(
|
||||
connections={(CONNECTION_NETWORK_MAC, self.device.mac)},
|
||||
manufacturer="Daikin",
|
||||
model=info.get("model"),
|
||||
name=info.get("name"),
|
||||
sw_version=info.get("ver", "").replace("_", "."),
|
||||
)
|
|
@ -25,7 +25,7 @@ from homeassistant.core import HomeAssistant
|
|||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
|
||||
from . import DOMAIN as DAIKIN_DOMAIN, DaikinApi
|
||||
from . import DOMAIN as DAIKIN_DOMAIN
|
||||
from .const import (
|
||||
ATTR_COMPRESSOR_FREQUENCY,
|
||||
ATTR_COOL_ENERGY,
|
||||
|
@ -38,6 +38,8 @@ from .const import (
|
|||
ATTR_TOTAL_ENERGY_TODAY,
|
||||
ATTR_TOTAL_POWER,
|
||||
)
|
||||
from .coordinator import DaikinCoordinator
|
||||
from .entity import DaikinEntity
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
|
@ -173,26 +175,20 @@ async def async_setup_entry(
|
|||
async_add_entities(entities)
|
||||
|
||||
|
||||
class DaikinSensor(SensorEntity):
|
||||
class DaikinSensor(DaikinEntity, SensorEntity):
|
||||
"""Representation of a Sensor."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
entity_description: DaikinSensorEntityDescription
|
||||
|
||||
def __init__(
|
||||
self, api: DaikinApi, description: DaikinSensorEntityDescription
|
||||
self, coordinator: DaikinCoordinator, description: DaikinSensorEntityDescription
|
||||
) -> None:
|
||||
"""Initialize the sensor."""
|
||||
super().__init__(coordinator)
|
||||
self.entity_description = description
|
||||
self._attr_device_info = api.device_info
|
||||
self._attr_unique_id = f"{api.device.mac}-{description.key}"
|
||||
self._api = api
|
||||
self._attr_unique_id = f"{self.device.mac}-{description.key}"
|
||||
|
||||
@property
|
||||
def native_value(self) -> float | None:
|
||||
"""Return the state of the sensor."""
|
||||
return self.entity_description.value_func(self._api.device)
|
||||
|
||||
async def async_update(self) -> None:
|
||||
"""Retrieve latest state."""
|
||||
await self._api.async_update()
|
||||
return self.entity_description.value_func(self.device)
|
||||
|
|
|
@ -10,7 +10,9 @@ from homeassistant.core import HomeAssistant
|
|||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
|
||||
from . import DOMAIN as DAIKIN_DOMAIN, DaikinApi
|
||||
from . import DOMAIN
|
||||
from .coordinator import DaikinCoordinator
|
||||
from .entity import DaikinEntity
|
||||
|
||||
DAIKIN_ATTR_ADVANCED = "adv"
|
||||
DAIKIN_ATTR_STREAMER = "streamer"
|
||||
|
@ -34,15 +36,13 @@ async def async_setup_entry(
|
|||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
) -> None:
|
||||
"""Set up Daikin climate based on config_entry."""
|
||||
daikin_api: DaikinApi = hass.data[DAIKIN_DOMAIN][entry.entry_id]
|
||||
switches: list[DaikinZoneSwitch | DaikinStreamerSwitch | DaikinToggleSwitch] = []
|
||||
daikin_api: DaikinCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
switches: list[SwitchEntity] = []
|
||||
if zones := daikin_api.device.zones:
|
||||
switches.extend(
|
||||
[
|
||||
DaikinZoneSwitch(daikin_api, zone_id)
|
||||
for zone_id, zone in enumerate(zones)
|
||||
if zone[0] != "-"
|
||||
]
|
||||
DaikinZoneSwitch(daikin_api, zone_id)
|
||||
for zone_id, zone in enumerate(zones)
|
||||
if zone[0] != "-"
|
||||
)
|
||||
if daikin_api.device.support_advanced_modes:
|
||||
# It isn't possible to find out from the API responses if a specific
|
||||
|
@ -53,100 +53,80 @@ async def async_setup_entry(
|
|||
async_add_entities(switches)
|
||||
|
||||
|
||||
class DaikinZoneSwitch(SwitchEntity):
|
||||
class DaikinZoneSwitch(DaikinEntity, SwitchEntity):
|
||||
"""Representation of a zone."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
_attr_translation_key = "zone"
|
||||
|
||||
def __init__(self, api: DaikinApi, zone_id: int) -> None:
|
||||
def __init__(self, coordinator: DaikinCoordinator, zone_id: int) -> None:
|
||||
"""Initialize the zone."""
|
||||
self._api = api
|
||||
super().__init__(coordinator)
|
||||
self._zone_id = zone_id
|
||||
self._attr_device_info = api.device_info
|
||||
self._attr_unique_id = f"{api.device.mac}-zone{zone_id}"
|
||||
self._attr_unique_id = f"{self.device.mac}-zone{zone_id}"
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""Return the name of the sensor."""
|
||||
return self._api.device.zones[self._zone_id][0]
|
||||
return self.device.zones[self._zone_id][0]
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Return the state of the sensor."""
|
||||
return self._api.device.zones[self._zone_id][1] == "1"
|
||||
|
||||
async def async_update(self) -> None:
|
||||
"""Retrieve latest state."""
|
||||
await self._api.async_update()
|
||||
return self.device.zones[self._zone_id][1] == "1"
|
||||
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn the zone on."""
|
||||
await self._api.device.set_zone(self._zone_id, "zone_onoff", "1")
|
||||
await self.device.set_zone(self._zone_id, "zone_onoff", "1")
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn the zone off."""
|
||||
await self._api.device.set_zone(self._zone_id, "zone_onoff", "0")
|
||||
await self.device.set_zone(self._zone_id, "zone_onoff", "0")
|
||||
|
||||
|
||||
class DaikinStreamerSwitch(SwitchEntity):
|
||||
class DaikinStreamerSwitch(DaikinEntity, SwitchEntity):
|
||||
"""Streamer state."""
|
||||
|
||||
_attr_name = "Streamer"
|
||||
_attr_has_entity_name = True
|
||||
_attr_translation_key = "streamer"
|
||||
|
||||
def __init__(self, api: DaikinApi) -> None:
|
||||
"""Initialize streamer switch."""
|
||||
self._api = api
|
||||
self._attr_device_info = api.device_info
|
||||
self._attr_unique_id = f"{api.device.mac}-streamer"
|
||||
def __init__(self, coordinator: DaikinCoordinator) -> None:
|
||||
"""Initialize switch."""
|
||||
super().__init__(coordinator)
|
||||
self._attr_unique_id = f"{self.device.mac}-streamer"
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Return the state of the sensor."""
|
||||
return (
|
||||
DAIKIN_ATTR_STREAMER in self._api.device.represent(DAIKIN_ATTR_ADVANCED)[1]
|
||||
)
|
||||
|
||||
async def async_update(self) -> None:
|
||||
"""Retrieve latest state."""
|
||||
await self._api.async_update()
|
||||
return DAIKIN_ATTR_STREAMER in self.device.represent(DAIKIN_ATTR_ADVANCED)[1]
|
||||
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn the zone on."""
|
||||
await self._api.device.set_streamer("on")
|
||||
await self.device.set_streamer("on")
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn the zone off."""
|
||||
await self._api.device.set_streamer("off")
|
||||
await self.device.set_streamer("off")
|
||||
|
||||
|
||||
class DaikinToggleSwitch(SwitchEntity):
|
||||
class DaikinToggleSwitch(DaikinEntity, SwitchEntity):
|
||||
"""Switch state."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
_attr_translation_key = "toggle"
|
||||
|
||||
def __init__(self, api: DaikinApi) -> None:
|
||||
def __init__(self, coordinator: DaikinCoordinator) -> None:
|
||||
"""Initialize switch."""
|
||||
self._api = api
|
||||
self._attr_device_info = api.device_info
|
||||
self._attr_unique_id = f"{self._api.device.mac}-toggle"
|
||||
super().__init__(coordinator)
|
||||
self._attr_unique_id = f"{self.device.mac}-toggle"
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Return the state of the sensor."""
|
||||
return "off" not in self._api.device.represent(DAIKIN_ATTR_MODE)
|
||||
|
||||
async def async_update(self) -> None:
|
||||
"""Retrieve latest state."""
|
||||
await self._api.async_update()
|
||||
return "off" not in self.device.represent(DAIKIN_ATTR_MODE)
|
||||
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn the zone on."""
|
||||
await self._api.device.set({})
|
||||
await self.device.set({})
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn the zone off."""
|
||||
await self._api.device.set({DAIKIN_ATTR_MODE: "off"})
|
||||
await self.device.set({DAIKIN_ATTR_MODE: "off"})
|
||||
|
|
|
@ -7,10 +7,10 @@ from aiohttp import ClientConnectionError
|
|||
from freezegun.api import FrozenDateTimeFactory
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.daikin import DaikinApi, update_unique_id
|
||||
from homeassistant.components.daikin import update_unique_id
|
||||
from homeassistant.components.daikin.const import DOMAIN, KEY_MAC
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import CONF_HOST
|
||||
from homeassistant.const import CONF_HOST, STATE_UNAVAILABLE
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
|
||||
|
@ -183,18 +183,15 @@ async def test_client_update_connection_error(
|
|||
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
|
||||
api: DaikinApi = hass.data[DOMAIN][config_entry.entry_id]
|
||||
|
||||
assert api.available is True
|
||||
assert hass.states.get("climate.daikinap00000").state != STATE_UNAVAILABLE
|
||||
|
||||
type(mock_daikin).update_status.side_effect = ClientConnectionError
|
||||
|
||||
freezer.tick(timedelta(seconds=90))
|
||||
freezer.tick(timedelta(seconds=60))
|
||||
async_fire_time_changed(hass)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert api.available is False
|
||||
assert hass.states.get("climate.daikinap00000").state == STATE_UNAVAILABLE
|
||||
|
||||
assert mock_daikin.update_status.call_count == 2
|
||||
|
||||
|
|
Loading…
Reference in New Issue