Xiaomi miio add coordinator to fan platform (#54366)
* init coordinator for airpurifiers and airfresh * Update fan entities with coordinator * cache mode and fan_level at user update * pylint define attributes in _init * Update homeassistant/components/xiaomi_miio/fan.py Co-authored-by: Maciej Bieniek <bieniu@users.noreply.github.com> * Update homeassistant/components/xiaomi_miio/fan.py Co-authored-by: Maciej Bieniek <bieniu@users.noreply.github.com> * Update homeassistant/components/xiaomi_miio/fan.py Co-authored-by: Maciej Bieniek <bieniu@users.noreply.github.com> * cleanup code * Set hass.data[DATA_KEY] to enable * rename to filtered_entities in service handler * Update homeassistant/components/xiaomi_miio/fan.py Co-authored-by: Joakim Sørensen <hi@ludeeus.dev> * flake Co-authored-by: Maciej Bieniek <bieniu@users.noreply.github.com> Co-authored-by: Joakim Sørensen <hi@ludeeus.dev>
This commit is contained in:
parent
a7c08fff81
commit
5de1adacf7
2 changed files with 176 additions and 198 deletions
|
@ -3,7 +3,15 @@ from datetime import timedelta
|
|||
import logging
|
||||
|
||||
import async_timeout
|
||||
from miio import AirHumidifier, AirHumidifierMiot, AirHumidifierMjjsq, DeviceException
|
||||
from miio import (
|
||||
AirFresh,
|
||||
AirHumidifier,
|
||||
AirHumidifierMiot,
|
||||
AirHumidifierMjjsq,
|
||||
AirPurifier,
|
||||
AirPurifierMiot,
|
||||
DeviceException,
|
||||
)
|
||||
from miio.gateway.gateway import GatewayException
|
||||
|
||||
from homeassistant import config_entries, core
|
||||
|
@ -23,10 +31,13 @@ from .const import (
|
|||
KEY_DEVICE,
|
||||
MODELS_AIR_MONITOR,
|
||||
MODELS_FAN,
|
||||
MODELS_FAN_MIIO,
|
||||
MODELS_HUMIDIFIER,
|
||||
MODELS_HUMIDIFIER_MIIO,
|
||||
MODELS_HUMIDIFIER_MIOT,
|
||||
MODELS_HUMIDIFIER_MJJSQ,
|
||||
MODELS_LIGHT,
|
||||
MODELS_PURIFIER_MIOT,
|
||||
MODELS_SWITCH,
|
||||
MODELS_VACUUM,
|
||||
)
|
||||
|
@ -107,27 +118,52 @@ async def async_create_miio_device_and_coordinator(
|
|||
token = entry.data[CONF_TOKEN]
|
||||
name = entry.title
|
||||
device = None
|
||||
migrate = False
|
||||
|
||||
if model not in MODELS_HUMIDIFIER:
|
||||
if (
|
||||
model not in MODELS_HUMIDIFIER
|
||||
and model not in MODELS_PURIFIER_MIOT
|
||||
and model not in MODELS_FAN_MIIO
|
||||
):
|
||||
return
|
||||
|
||||
_LOGGER.debug("Initializing with host %s (token %s...)", host, token[:5])
|
||||
|
||||
# Humidifiers
|
||||
if model in MODELS_HUMIDIFIER_MIOT:
|
||||
device = AirHumidifierMiot(host, token)
|
||||
migrate = True
|
||||
elif model in MODELS_HUMIDIFIER_MJJSQ:
|
||||
device = AirHumidifierMjjsq(host, token, model=model)
|
||||
else:
|
||||
migrate = True
|
||||
elif model in MODELS_HUMIDIFIER_MIIO:
|
||||
device = AirHumidifier(host, token, model=model)
|
||||
migrate = True
|
||||
# Airpurifiers and Airfresh
|
||||
elif model in MODELS_PURIFIER_MIOT:
|
||||
device = AirPurifierMiot(host, token)
|
||||
elif model.startswith("zhimi.airpurifier."):
|
||||
device = AirPurifier(host, token)
|
||||
elif model.startswith("zhimi.airfresh."):
|
||||
device = AirFresh(host, token)
|
||||
else:
|
||||
_LOGGER.error(
|
||||
"Unsupported device found! Please create an issue at "
|
||||
"https://github.com/syssi/xiaomi_airpurifier/issues "
|
||||
"and provide the following data: %s",
|
||||
model,
|
||||
)
|
||||
return
|
||||
|
||||
# Removing fan platform entity for humidifiers and migrate the name to the config entry for migration
|
||||
entity_registry = er.async_get(hass)
|
||||
entity_id = entity_registry.async_get_entity_id("fan", DOMAIN, entry.unique_id)
|
||||
if entity_id:
|
||||
# This check is entities that have a platform migration only and should be removed in the future
|
||||
if migrate_entity_name := entity_registry.async_get(entity_id).name:
|
||||
hass.config_entries.async_update_entry(entry, title=migrate_entity_name)
|
||||
entity_registry.async_remove(entity_id)
|
||||
if migrate:
|
||||
# Removing fan platform entity for humidifiers and migrate the name to the config entry for migration
|
||||
entity_registry = er.async_get(hass)
|
||||
entity_id = entity_registry.async_get_entity_id("fan", DOMAIN, entry.unique_id)
|
||||
if entity_id:
|
||||
# This check is entities that have a platform migration only and should be removed in the future
|
||||
if migrate_entity_name := entity_registry.async_get(entity_id).name:
|
||||
hass.config_entries.async_update_entry(entry, title=migrate_entity_name)
|
||||
entity_registry.async_remove(entity_id)
|
||||
|
||||
async def async_update_data():
|
||||
"""Fetch data from the device using async_add_executor_job."""
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
"""Support for Xiaomi Mi Air Purifier and Xiaomi Mi Air Humidifier."""
|
||||
import asyncio
|
||||
from enum import Enum
|
||||
from functools import partial
|
||||
import logging
|
||||
import math
|
||||
|
||||
from miio import AirFresh, AirPurifier, AirPurifierMiot, DeviceException
|
||||
from miio.airfresh import (
|
||||
LedBrightness as AirfreshLedBrightness,
|
||||
OperationMode as AirfreshOperationMode,
|
||||
|
@ -35,6 +33,7 @@ from homeassistant.const import (
|
|||
CONF_NAME,
|
||||
CONF_TOKEN,
|
||||
)
|
||||
from homeassistant.core import callback
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.util.percentage import (
|
||||
percentage_to_ranged_value,
|
||||
|
@ -56,6 +55,8 @@ from .const import (
|
|||
FEATURE_SET_LED,
|
||||
FEATURE_SET_LED_BRIGHTNESS,
|
||||
FEATURE_SET_VOLUME,
|
||||
KEY_COORDINATOR,
|
||||
KEY_DEVICE,
|
||||
MODEL_AIRPURIFIER_2H,
|
||||
MODEL_AIRPURIFIER_2S,
|
||||
MODEL_AIRPURIFIER_PRO,
|
||||
|
@ -79,9 +80,8 @@ from .const import (
|
|||
SERVICE_SET_LEARN_MODE_ON,
|
||||
SERVICE_SET_LED_BRIGHTNESS,
|
||||
SERVICE_SET_VOLUME,
|
||||
SUCCESS,
|
||||
)
|
||||
from .device import XiaomiMiioEntity
|
||||
from .device import XiaomiCoordinatedMiioEntity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -430,94 +430,89 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
|||
"""Set up the Fan from a config entry."""
|
||||
entities = []
|
||||
|
||||
if config_entry.data[CONF_FLOW_TYPE] == CONF_DEVICE:
|
||||
if DATA_KEY not in hass.data:
|
||||
hass.data[DATA_KEY] = {}
|
||||
if not config_entry.data[CONF_FLOW_TYPE] == CONF_DEVICE:
|
||||
return
|
||||
|
||||
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
|
||||
hass.data.setdefault(DATA_KEY, {})
|
||||
|
||||
_LOGGER.debug("Initializing with host %s (token %s...)", host, token[:5])
|
||||
name = config_entry.title
|
||||
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]
|
||||
|
||||
if model in MODELS_PURIFIER_MIOT:
|
||||
air_purifier = AirPurifierMiot(host, token)
|
||||
entity = XiaomiAirPurifierMiot(
|
||||
name, air_purifier, config_entry, unique_id, allowed_failures=2
|
||||
)
|
||||
elif model.startswith("zhimi.airpurifier."):
|
||||
air_purifier = AirPurifier(host, token)
|
||||
entity = XiaomiAirPurifier(name, air_purifier, config_entry, unique_id)
|
||||
elif model.startswith("zhimi.airfresh."):
|
||||
air_fresh = AirFresh(host, token)
|
||||
entity = XiaomiAirFresh(name, air_fresh, config_entry, unique_id)
|
||||
if model in MODELS_PURIFIER_MIOT:
|
||||
entity = XiaomiAirPurifierMiot(
|
||||
name,
|
||||
device,
|
||||
config_entry,
|
||||
unique_id,
|
||||
coordinator,
|
||||
)
|
||||
elif model.startswith("zhimi.airpurifier."):
|
||||
entity = XiaomiAirPurifier(name, device, config_entry, unique_id, coordinator)
|
||||
elif model.startswith("zhimi.airfresh."):
|
||||
entity = XiaomiAirFresh(name, device, config_entry, unique_id, coordinator)
|
||||
else:
|
||||
return
|
||||
|
||||
hass.data[DATA_KEY][unique_id] = entity
|
||||
|
||||
entities.append(entity)
|
||||
|
||||
async def async_service_handler(service):
|
||||
"""Map services to methods on XiaomiAirPurifier."""
|
||||
method = SERVICE_TO_METHOD[service.service]
|
||||
params = {
|
||||
key: value for key, value in service.data.items() if key != ATTR_ENTITY_ID
|
||||
}
|
||||
entity_ids = service.data.get(ATTR_ENTITY_ID)
|
||||
if entity_ids:
|
||||
filtered_entities = [
|
||||
entity
|
||||
for entity in hass.data[DATA_KEY].values()
|
||||
if entity.entity_id in entity_ids
|
||||
]
|
||||
else:
|
||||
_LOGGER.error(
|
||||
"Unsupported device found! Please create an issue at "
|
||||
"https://github.com/syssi/xiaomi_airpurifier/issues "
|
||||
"and provide the following data: %s",
|
||||
model,
|
||||
)
|
||||
return
|
||||
filtered_entities = hass.data[DATA_KEY].values()
|
||||
|
||||
hass.data[DATA_KEY][host] = entity
|
||||
entities.append(entity)
|
||||
update_tasks = []
|
||||
|
||||
async def async_service_handler(service):
|
||||
"""Map services to methods on XiaomiAirPurifier."""
|
||||
method = SERVICE_TO_METHOD[service.service]
|
||||
params = {
|
||||
key: value
|
||||
for key, value in service.data.items()
|
||||
if key != ATTR_ENTITY_ID
|
||||
}
|
||||
entity_ids = service.data.get(ATTR_ENTITY_ID)
|
||||
if entity_ids:
|
||||
entities = [
|
||||
entity
|
||||
for entity in hass.data[DATA_KEY].values()
|
||||
if entity.entity_id in entity_ids
|
||||
]
|
||||
else:
|
||||
entities = hass.data[DATA_KEY].values()
|
||||
|
||||
update_tasks = []
|
||||
|
||||
for entity in entities:
|
||||
entity_method = getattr(entity, method["method"], None)
|
||||
if not entity_method:
|
||||
continue
|
||||
await entity_method(**params)
|
||||
update_tasks.append(
|
||||
hass.async_create_task(entity.async_update_ha_state(True))
|
||||
)
|
||||
|
||||
if update_tasks:
|
||||
await asyncio.wait(update_tasks)
|
||||
|
||||
for air_purifier_service, method in SERVICE_TO_METHOD.items():
|
||||
schema = method.get("schema", AIRPURIFIER_SERVICE_SCHEMA)
|
||||
hass.services.async_register(
|
||||
DOMAIN, air_purifier_service, async_service_handler, schema=schema
|
||||
for entity in filtered_entities:
|
||||
entity_method = getattr(entity, method["method"], None)
|
||||
if not entity_method:
|
||||
continue
|
||||
await entity_method(**params)
|
||||
update_tasks.append(
|
||||
hass.async_create_task(entity.async_update_ha_state(True))
|
||||
)
|
||||
|
||||
async_add_entities(entities, update_before_add=True)
|
||||
if update_tasks:
|
||||
await asyncio.wait(update_tasks)
|
||||
|
||||
for air_purifier_service, method in SERVICE_TO_METHOD.items():
|
||||
schema = method.get("schema", AIRPURIFIER_SERVICE_SCHEMA)
|
||||
hass.services.async_register(
|
||||
DOMAIN, air_purifier_service, async_service_handler, schema=schema
|
||||
)
|
||||
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
class XiaomiGenericDevice(XiaomiMiioEntity, FanEntity):
|
||||
class XiaomiGenericDevice(XiaomiCoordinatedMiioEntity, FanEntity):
|
||||
"""Representation of a generic Xiaomi device."""
|
||||
|
||||
def __init__(self, name, device, entry, unique_id):
|
||||
def __init__(self, name, device, entry, unique_id, coordinator):
|
||||
"""Initialize the generic Xiaomi device."""
|
||||
super().__init__(name, device, entry, unique_id)
|
||||
super().__init__(name, device, entry, unique_id, coordinator)
|
||||
|
||||
self._available = False
|
||||
self._available_attributes = {}
|
||||
self._state = None
|
||||
self._mode = None
|
||||
self._fan_level = None
|
||||
self._state_attrs = {ATTR_MODEL: self._model}
|
||||
self._device_features = FEATURE_SET_CHILD_LOCK
|
||||
self._skip_update = False
|
||||
self._supported_features = 0
|
||||
self._speed_count = 100
|
||||
self._preset_modes = []
|
||||
|
@ -583,22 +578,20 @@ class XiaomiGenericDevice(XiaomiMiioEntity, FanEntity):
|
|||
|
||||
return value
|
||||
|
||||
async def _try_command(self, mask_error, func, *args, **kwargs):
|
||||
"""Call a miio device command handling error messages."""
|
||||
try:
|
||||
result = await self.hass.async_add_executor_job(
|
||||
partial(func, *args, **kwargs)
|
||||
)
|
||||
|
||||
_LOGGER.debug("Response received from miio device: %s", result)
|
||||
|
||||
return result == SUCCESS
|
||||
except DeviceException as exc:
|
||||
if self._available:
|
||||
_LOGGER.error(mask_error, exc)
|
||||
self._available = False
|
||||
|
||||
return False
|
||||
@callback
|
||||
def _handle_coordinator_update(self):
|
||||
"""Fetch state from the device."""
|
||||
self._available = True
|
||||
self._state = self.coordinator.data.is_on
|
||||
self._state_attrs.update(
|
||||
{
|
||||
key: self._extract_value_from_attribute(self.coordinator.data, value)
|
||||
for key, value in self._available_attributes.items()
|
||||
}
|
||||
)
|
||||
self._mode = self._state_attrs.get(ATTR_MODE)
|
||||
self._fan_level = self._state_attrs.get(ATTR_FAN_LEVEL)
|
||||
self.async_write_ha_state()
|
||||
|
||||
#
|
||||
# The fan entity model has changed to use percentages and preset_modes
|
||||
|
@ -630,7 +623,7 @@ class XiaomiGenericDevice(XiaomiMiioEntity, FanEntity):
|
|||
|
||||
if result:
|
||||
self._state = True
|
||||
self._skip_update = True
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_turn_off(self, **kwargs) -> None:
|
||||
"""Turn the device off."""
|
||||
|
@ -640,7 +633,7 @@ class XiaomiGenericDevice(XiaomiMiioEntity, FanEntity):
|
|||
|
||||
if result:
|
||||
self._state = False
|
||||
self._skip_update = True
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_set_buzzer_on(self):
|
||||
"""Turn the buzzer on."""
|
||||
|
@ -706,11 +699,9 @@ class XiaomiAirPurifier(XiaomiGenericDevice):
|
|||
|
||||
REVERSE_SPEED_MODE_MAPPING = {v: k for k, v in SPEED_MODE_MAPPING.items()}
|
||||
|
||||
def __init__(self, name, device, entry, unique_id, allowed_failures=0):
|
||||
def __init__(self, name, device, entry, unique_id, coordinator):
|
||||
"""Initialize the plug switch."""
|
||||
super().__init__(name, device, entry, unique_id)
|
||||
self._allowed_failures = allowed_failures
|
||||
self._failure = 0
|
||||
super().__init__(name, device, entry, unique_id, coordinator)
|
||||
|
||||
if self._model == MODEL_AIRPURIFIER_PRO:
|
||||
self._device_features = FEATURE_FLAGS_AIRPURIFIER_PRO
|
||||
|
@ -774,45 +765,8 @@ class XiaomiAirPurifier(XiaomiGenericDevice):
|
|||
self._state_attrs.update(
|
||||
{attribute: None for attribute in self._available_attributes}
|
||||
)
|
||||
|
||||
async def async_update(self):
|
||||
"""Fetch state from the device."""
|
||||
# On state change the device doesn't provide the new state immediately.
|
||||
if self._skip_update:
|
||||
self._skip_update = False
|
||||
return
|
||||
|
||||
try:
|
||||
state = await self.hass.async_add_executor_job(self._device.status)
|
||||
_LOGGER.debug("Got new state: %s", state)
|
||||
|
||||
self._available = True
|
||||
self._state = state.is_on
|
||||
self._state_attrs.update(
|
||||
{
|
||||
key: self._extract_value_from_attribute(state, value)
|
||||
for key, value in self._available_attributes.items()
|
||||
}
|
||||
)
|
||||
|
||||
self._failure = 0
|
||||
|
||||
except DeviceException as ex:
|
||||
self._failure += 1
|
||||
if self._failure < self._allowed_failures:
|
||||
_LOGGER.info(
|
||||
"Got exception while fetching the state: %s, failure: %d",
|
||||
ex,
|
||||
self._failure,
|
||||
)
|
||||
else:
|
||||
if self._available:
|
||||
self._available = False
|
||||
_LOGGER.error(
|
||||
"Got exception while fetching the state: %s, failure: %d",
|
||||
ex,
|
||||
self._failure,
|
||||
)
|
||||
self._mode = self._state_attrs.get(ATTR_MODE)
|
||||
self._fan_level = self._state_attrs.get(ATTR_FAN_LEVEL)
|
||||
|
||||
@property
|
||||
def preset_mode(self):
|
||||
|
@ -1032,8 +986,7 @@ class XiaomiAirPurifierMiot(XiaomiAirPurifier):
|
|||
def percentage(self):
|
||||
"""Return the current percentage based speed."""
|
||||
if self._state:
|
||||
fan_level = self._state_attrs[ATTR_FAN_LEVEL]
|
||||
return ranged_value_to_percentage((1, 3), fan_level)
|
||||
return ranged_value_to_percentage((1, 3), self._fan_level)
|
||||
|
||||
return None
|
||||
|
||||
|
@ -1041,9 +994,7 @@ class XiaomiAirPurifierMiot(XiaomiAirPurifier):
|
|||
def preset_mode(self):
|
||||
"""Get the active preset mode."""
|
||||
if self._state:
|
||||
preset_mode = AirpurifierMiotOperationMode(
|
||||
self._state_attrs[ATTR_MODE]
|
||||
).name
|
||||
preset_mode = AirpurifierMiotOperationMode(self._mode).name
|
||||
return preset_mode if preset_mode in self._preset_modes else None
|
||||
|
||||
return None
|
||||
|
@ -1053,7 +1004,7 @@ class XiaomiAirPurifierMiot(XiaomiAirPurifier):
|
|||
def speed(self):
|
||||
"""Return the current speed."""
|
||||
if self._state:
|
||||
return AirpurifierMiotOperationMode(self._state_attrs[ATTR_MODE]).name
|
||||
return AirpurifierMiotOperationMode(self._mode).name
|
||||
|
||||
return None
|
||||
|
||||
|
@ -1063,12 +1014,15 @@ class XiaomiAirPurifierMiot(XiaomiAirPurifier):
|
|||
This method is a coroutine.
|
||||
"""
|
||||
fan_level = math.ceil(percentage_to_ranged_value((1, 3), percentage))
|
||||
if fan_level:
|
||||
await self._try_command(
|
||||
"Setting fan level of the miio device failed.",
|
||||
self._device.set_fan_level,
|
||||
fan_level,
|
||||
)
|
||||
if not fan_level:
|
||||
return
|
||||
if await self._try_command(
|
||||
"Setting fan level of the miio device failed.",
|
||||
self._device.set_fan_level,
|
||||
fan_level,
|
||||
):
|
||||
self._fan_level = fan_level
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_set_preset_mode(self, preset_mode: str) -> None:
|
||||
"""Set the preset mode of the fan.
|
||||
|
@ -1078,11 +1032,13 @@ class XiaomiAirPurifierMiot(XiaomiAirPurifier):
|
|||
if preset_mode not in self.preset_modes:
|
||||
_LOGGER.warning("'%s'is not a valid preset mode", preset_mode)
|
||||
return
|
||||
await self._try_command(
|
||||
if await self._try_command(
|
||||
"Setting operation mode of the miio device failed.",
|
||||
self._device.set_mode,
|
||||
self.PRESET_MODE_MAPPING[preset_mode],
|
||||
)
|
||||
):
|
||||
self._mode = self.PRESET_MODE_MAPPING[preset_mode].value
|
||||
self.async_write_ha_state()
|
||||
|
||||
# the async_set_speed function is deprecated, support will end with release 2021.7
|
||||
# it is added here only for compatibility with legacy speeds
|
||||
|
@ -1093,11 +1049,13 @@ class XiaomiAirPurifierMiot(XiaomiAirPurifier):
|
|||
|
||||
_LOGGER.debug("Setting the operation mode to: %s", speed)
|
||||
|
||||
await self._try_command(
|
||||
if await self._try_command(
|
||||
"Setting operation mode of the miio device failed.",
|
||||
self._device.set_mode,
|
||||
AirpurifierMiotOperationMode[speed.title()],
|
||||
)
|
||||
):
|
||||
self._mode = AirpurifierMiotOperationMode[speed.title()].value
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_set_led_brightness(self, brightness: int = 2):
|
||||
"""Set the led brightness."""
|
||||
|
@ -1128,9 +1086,9 @@ class XiaomiAirFresh(XiaomiGenericDevice):
|
|||
"Interval": AirfreshOperationMode.Interval,
|
||||
}
|
||||
|
||||
def __init__(self, name, device, entry, unique_id):
|
||||
def __init__(self, name, device, entry, unique_id, coordinator):
|
||||
"""Initialize the miio device."""
|
||||
super().__init__(name, device, entry, unique_id)
|
||||
super().__init__(name, device, entry, unique_id, coordinator)
|
||||
|
||||
self._device_features = FEATURE_FLAGS_AIRFRESH
|
||||
self._available_attributes = AVAILABLE_ATTRIBUTES_AIRFRESH
|
||||
|
@ -1142,37 +1100,13 @@ class XiaomiAirFresh(XiaomiGenericDevice):
|
|||
self._state_attrs.update(
|
||||
{attribute: None for attribute in self._available_attributes}
|
||||
)
|
||||
|
||||
async def async_update(self):
|
||||
"""Fetch state from the device."""
|
||||
# On state change the device doesn't provide the new state immediately.
|
||||
if self._skip_update:
|
||||
self._skip_update = False
|
||||
return
|
||||
|
||||
try:
|
||||
state = await self.hass.async_add_executor_job(self._device.status)
|
||||
_LOGGER.debug("Got new state: %s", state)
|
||||
|
||||
self._available = True
|
||||
self._state = state.is_on
|
||||
self._state_attrs.update(
|
||||
{
|
||||
key: self._extract_value_from_attribute(state, value)
|
||||
for key, value in self._available_attributes.items()
|
||||
}
|
||||
)
|
||||
|
||||
except DeviceException as ex:
|
||||
if self._available:
|
||||
self._available = False
|
||||
_LOGGER.error("Got exception while fetching the state: %s", ex)
|
||||
self._mode = self._state_attrs.get(ATTR_MODE)
|
||||
|
||||
@property
|
||||
def preset_mode(self):
|
||||
"""Get the active preset mode."""
|
||||
if self._state:
|
||||
preset_mode = AirfreshOperationMode(self._state_attrs[ATTR_MODE]).name
|
||||
preset_mode = AirfreshOperationMode(self._mode).name
|
||||
return preset_mode if preset_mode in self._preset_modes else None
|
||||
|
||||
return None
|
||||
|
@ -1181,7 +1115,7 @@ class XiaomiAirFresh(XiaomiGenericDevice):
|
|||
def percentage(self):
|
||||
"""Return the current percentage based speed."""
|
||||
if self._state:
|
||||
mode = AirfreshOperationMode(self._state_attrs[ATTR_MODE])
|
||||
mode = AirfreshOperationMode(self._mode)
|
||||
if mode in self.REVERSE_SPEED_MODE_MAPPING:
|
||||
return ranged_value_to_percentage(
|
||||
(1, self._speed_count), self.REVERSE_SPEED_MODE_MAPPING[mode]
|
||||
|
@ -1194,7 +1128,7 @@ class XiaomiAirFresh(XiaomiGenericDevice):
|
|||
def speed(self):
|
||||
"""Return the current speed."""
|
||||
if self._state:
|
||||
return AirfreshOperationMode(self._state_attrs[ATTR_MODE]).name
|
||||
return AirfreshOperationMode(self._mode).name
|
||||
|
||||
return None
|
||||
|
||||
|
@ -1207,11 +1141,15 @@ class XiaomiAirFresh(XiaomiGenericDevice):
|
|||
percentage_to_ranged_value((1, self._speed_count), percentage)
|
||||
)
|
||||
if speed_mode:
|
||||
await self._try_command(
|
||||
if await self._try_command(
|
||||
"Setting operation mode of the miio device failed.",
|
||||
self._device.set_mode,
|
||||
AirfreshOperationMode(self.SPEED_MODE_MAPPING[speed_mode]),
|
||||
)
|
||||
):
|
||||
self._mode = AirfreshOperationMode(
|
||||
self.SPEED_MODE_MAPPING[speed_mode]
|
||||
).value
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_set_preset_mode(self, preset_mode: str) -> None:
|
||||
"""Set the preset mode of the fan.
|
||||
|
@ -1221,11 +1159,13 @@ class XiaomiAirFresh(XiaomiGenericDevice):
|
|||
if preset_mode not in self.preset_modes:
|
||||
_LOGGER.warning("'%s'is not a valid preset mode", preset_mode)
|
||||
return
|
||||
await self._try_command(
|
||||
if await self._try_command(
|
||||
"Setting operation mode of the miio device failed.",
|
||||
self._device.set_mode,
|
||||
self.PRESET_MODE_MAPPING[preset_mode],
|
||||
)
|
||||
):
|
||||
self._mode = self.PRESET_MODE_MAPPING[preset_mode].value
|
||||
self.async_write_ha_state()
|
||||
|
||||
# the async_set_speed function is deprecated, support will end with release 2021.7
|
||||
# it is added here only for compatibility with legacy speeds
|
||||
|
@ -1236,11 +1176,13 @@ class XiaomiAirFresh(XiaomiGenericDevice):
|
|||
|
||||
_LOGGER.debug("Setting the operation mode to: %s", speed)
|
||||
|
||||
await self._try_command(
|
||||
if await self._try_command(
|
||||
"Setting operation mode of the miio device failed.",
|
||||
self._device.set_mode,
|
||||
AirfreshOperationMode[speed.title()],
|
||||
)
|
||||
):
|
||||
self._mode = AirfreshOperationMode[speed.title()].value
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_set_led_on(self):
|
||||
"""Turn the led on."""
|
||||
|
|
Loading…
Add table
Reference in a new issue