mirror of
https://github.com/home-assistant/core.git
synced 2025-07-16 17:57:11 +00:00
Migrate fan component to async. (#5723)
* Migrate fan component to async. * Fix lint
This commit is contained in:
parent
574384f446
commit
2fc3dfff67
@ -4,7 +4,9 @@ Provides functionality to interact with fans.
|
|||||||
For more details about this component, please refer to the documentation at
|
For more details about this component, please refer to the documentation at
|
||||||
https://home-assistant.io/components/fan/
|
https://home-assistant.io/components/fan/
|
||||||
"""
|
"""
|
||||||
|
import asyncio
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
import functools as ft
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
@ -24,6 +26,8 @@ import homeassistant.helpers.config_validation as cv
|
|||||||
DOMAIN = 'fan'
|
DOMAIN = 'fan'
|
||||||
SCAN_INTERVAL = timedelta(seconds=30)
|
SCAN_INTERVAL = timedelta(seconds=30)
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
GROUP_NAME_ALL_FANS = 'all fans'
|
GROUP_NAME_ALL_FANS = 'all fans'
|
||||||
ENTITY_ID_ALL_FANS = group.ENTITY_ID_FORMAT.format(GROUP_NAME_ALL_FANS)
|
ENTITY_ID_ALL_FANS = group.ENTITY_ID_FORMAT.format(GROUP_NAME_ALL_FANS)
|
||||||
|
|
||||||
@ -88,7 +92,32 @@ FAN_SET_DIRECTION_SCHEMA = vol.Schema({
|
|||||||
vol.Optional(ATTR_DIRECTION): cv.string
|
vol.Optional(ATTR_DIRECTION): cv.string
|
||||||
}) # type: dict
|
}) # type: dict
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
SERVICE_TO_METHOD = {
|
||||||
|
SERVICE_TURN_ON: {
|
||||||
|
'method': 'async_turn_on',
|
||||||
|
'schema': FAN_TURN_ON_SCHEMA,
|
||||||
|
},
|
||||||
|
SERVICE_TURN_OFF: {
|
||||||
|
'method': 'async_turn_off',
|
||||||
|
'schema': FAN_TURN_OFF_SCHEMA,
|
||||||
|
},
|
||||||
|
SERVICE_TOGGLE: {
|
||||||
|
'method': 'async_toggle',
|
||||||
|
'schema': FAN_TOGGLE_SCHEMA,
|
||||||
|
},
|
||||||
|
SERVICE_SET_SPEED: {
|
||||||
|
'method': 'async_set_speed',
|
||||||
|
'schema': FAN_SET_SPEED_SCHEMA,
|
||||||
|
},
|
||||||
|
SERVICE_OSCILLATE: {
|
||||||
|
'method': 'async_oscillate',
|
||||||
|
'schema': FAN_OSCILLATE_SCHEMA,
|
||||||
|
},
|
||||||
|
SERVICE_SET_DIRECTION: {
|
||||||
|
'method': 'async_set_direction',
|
||||||
|
'schema': FAN_SET_DIRECTION_SCHEMA,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def is_on(hass, entity_id: str=None) -> bool:
|
def is_on(hass, entity_id: str=None) -> bool:
|
||||||
@ -164,60 +193,53 @@ def set_direction(hass, entity_id: str=None, direction: str=None) -> None:
|
|||||||
hass.services.call(DOMAIN, SERVICE_SET_DIRECTION, data)
|
hass.services.call(DOMAIN, SERVICE_SET_DIRECTION, data)
|
||||||
|
|
||||||
|
|
||||||
def setup(hass, config: dict) -> None:
|
@asyncio.coroutine
|
||||||
|
def async_setup(hass, config: dict):
|
||||||
"""Expose fan control via statemachine and services."""
|
"""Expose fan control via statemachine and services."""
|
||||||
component = EntityComponent(
|
component = EntityComponent(
|
||||||
_LOGGER, DOMAIN, hass, SCAN_INTERVAL, GROUP_NAME_ALL_FANS)
|
_LOGGER, DOMAIN, hass, SCAN_INTERVAL, GROUP_NAME_ALL_FANS)
|
||||||
component.setup(config)
|
|
||||||
|
|
||||||
def handle_fan_service(service: str) -> None:
|
yield from component.async_setup(config)
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def async_handle_fan_service(service):
|
||||||
"""Hande service call for fans."""
|
"""Hande service call for fans."""
|
||||||
# Get the validated data
|
method = SERVICE_TO_METHOD.get(service.service)
|
||||||
params = service.data.copy()
|
params = service.data.copy()
|
||||||
|
|
||||||
# Convert the entity ids to valid fan ids
|
# Convert the entity ids to valid fan ids
|
||||||
target_fans = component.extract_from_service(service)
|
target_fans = component.async_extract_from_service(service)
|
||||||
params.pop(ATTR_ENTITY_ID, None)
|
params.pop(ATTR_ENTITY_ID, None)
|
||||||
|
|
||||||
service_fun = None
|
|
||||||
for service_def in [SERVICE_TURN_ON, SERVICE_TURN_OFF,
|
|
||||||
SERVICE_SET_SPEED, SERVICE_OSCILLATE,
|
|
||||||
SERVICE_SET_DIRECTION]:
|
|
||||||
if service_def == service.service:
|
|
||||||
service_fun = service_def
|
|
||||||
break
|
|
||||||
|
|
||||||
if service_fun:
|
|
||||||
for fan in target_fans:
|
for fan in target_fans:
|
||||||
getattr(fan, service_fun)(**params)
|
yield from getattr(fan, method['method'])(**params)
|
||||||
|
|
||||||
|
update_tasks = []
|
||||||
|
|
||||||
for fan in target_fans:
|
for fan in target_fans:
|
||||||
if fan.should_poll:
|
if not fan.should_poll:
|
||||||
fan.update_ha_state(True)
|
continue
|
||||||
return
|
|
||||||
|
update_coro = hass.loop.create_task(
|
||||||
|
fan.async_update_ha_state(True))
|
||||||
|
if hasattr(fan, 'async_update'):
|
||||||
|
update_tasks.append(update_coro)
|
||||||
|
else:
|
||||||
|
yield from update_coro
|
||||||
|
|
||||||
|
if update_tasks:
|
||||||
|
yield from asyncio.wait(update_tasks, loop=hass.loop)
|
||||||
|
|
||||||
# Listen for fan service calls.
|
# Listen for fan service calls.
|
||||||
descriptions = load_yaml_config_file(
|
descriptions = yield from hass.loop.run_in_executor(
|
||||||
os.path.join(os.path.dirname(__file__), 'services.yaml'))
|
None, load_yaml_config_file, os.path.join(
|
||||||
hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_fan_service,
|
os.path.dirname(__file__), 'services.yaml'))
|
||||||
descriptions.get(SERVICE_TURN_ON),
|
|
||||||
schema=FAN_TURN_ON_SCHEMA)
|
|
||||||
|
|
||||||
hass.services.register(DOMAIN, SERVICE_TURN_OFF, handle_fan_service,
|
for service_name in SERVICE_TO_METHOD:
|
||||||
descriptions.get(SERVICE_TURN_OFF),
|
schema = SERVICE_TO_METHOD[service_name].get('schema')
|
||||||
schema=FAN_TURN_OFF_SCHEMA)
|
hass.services.async_register(
|
||||||
|
DOMAIN, service_name, async_handle_fan_service,
|
||||||
hass.services.register(DOMAIN, SERVICE_SET_SPEED, handle_fan_service,
|
descriptions.get(service_name), schema=schema)
|
||||||
descriptions.get(SERVICE_SET_SPEED),
|
|
||||||
schema=FAN_SET_SPEED_SCHEMA)
|
|
||||||
|
|
||||||
hass.services.register(DOMAIN, SERVICE_OSCILLATE, handle_fan_service,
|
|
||||||
descriptions.get(SERVICE_OSCILLATE),
|
|
||||||
schema=FAN_OSCILLATE_SCHEMA)
|
|
||||||
|
|
||||||
hass.services.register(DOMAIN, SERVICE_SET_DIRECTION, handle_fan_service,
|
|
||||||
descriptions.get(SERVICE_SET_DIRECTION),
|
|
||||||
schema=FAN_SET_DIRECTION_SCHEMA)
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -225,34 +247,57 @@ def setup(hass, config: dict) -> None:
|
|||||||
class FanEntity(ToggleEntity):
|
class FanEntity(ToggleEntity):
|
||||||
"""Representation of a fan."""
|
"""Representation of a fan."""
|
||||||
|
|
||||||
# pylint: disable=no-self-use
|
|
||||||
|
|
||||||
def set_speed(self: ToggleEntity, speed: str) -> None:
|
def set_speed(self: ToggleEntity, speed: str) -> None:
|
||||||
"""Set the speed of the fan."""
|
"""Set the speed of the fan."""
|
||||||
if speed is SPEED_OFF:
|
|
||||||
self.turn_off()
|
|
||||||
return
|
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def async_set_speed(self: ToggleEntity, speed: str):
|
||||||
|
"""Set the speed of the fan.
|
||||||
|
|
||||||
|
This method must be run in the event loop and returns a coroutine.
|
||||||
|
"""
|
||||||
|
if speed is SPEED_OFF:
|
||||||
|
return self.async_turn_off()
|
||||||
|
return self.hass.loop.run_in_executor(None, self.set_speed, speed)
|
||||||
|
|
||||||
def set_direction(self: ToggleEntity, direction: str) -> None:
|
def set_direction(self: ToggleEntity, direction: str) -> None:
|
||||||
"""Set the direction of the fan."""
|
"""Set the direction of the fan."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def async_set_direction(self: ToggleEntity, direction: str):
|
||||||
|
"""Set the direction of the fan.
|
||||||
|
|
||||||
|
This method must be run in the event loop and returns a coroutine.
|
||||||
|
"""
|
||||||
|
return self.hass.loop.run_in_executor(
|
||||||
|
None, self.set_direction, direction)
|
||||||
|
|
||||||
def turn_on(self: ToggleEntity, speed: str=None, **kwargs) -> None:
|
def turn_on(self: ToggleEntity, speed: str=None, **kwargs) -> None:
|
||||||
"""Turn on the fan."""
|
"""Turn on the fan."""
|
||||||
if speed is SPEED_OFF:
|
|
||||||
self.turn_off()
|
|
||||||
return
|
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def turn_off(self: ToggleEntity, **kwargs) -> None:
|
def async_turn_on(self: ToggleEntity, speed: str=None, **kwargs):
|
||||||
"""Turn off the fan."""
|
"""Turn on the fan.
|
||||||
raise NotImplementedError()
|
|
||||||
|
This method must be run in the event loop and returns a coroutine.
|
||||||
|
"""
|
||||||
|
if speed is SPEED_OFF:
|
||||||
|
return self.async_turn_off()
|
||||||
|
return self.hass.loop.run_in_executor(
|
||||||
|
None, ft.partial(self.turn_on, speed, **kwargs))
|
||||||
|
|
||||||
def oscillate(self: ToggleEntity, oscillating: bool) -> None:
|
def oscillate(self: ToggleEntity, oscillating: bool) -> None:
|
||||||
"""Oscillate the fan."""
|
"""Oscillate the fan."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def async_oscillate(self: ToggleEntity, oscillating: bool):
|
||||||
|
"""Oscillate the fan.
|
||||||
|
|
||||||
|
This method must be run in the event loop and returns a coroutine.
|
||||||
|
"""
|
||||||
|
return self.hass.loop.run_in_executor(
|
||||||
|
None, self.oscillate, oscillating)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self):
|
def is_on(self):
|
||||||
"""Return true if the entity is on."""
|
"""Return true if the entity is on."""
|
||||||
|
@ -56,8 +56,10 @@ class DemoFan(FanEntity):
|
|||||||
"""Get the list of available speeds."""
|
"""Get the list of available speeds."""
|
||||||
return [STATE_OFF, SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH]
|
return [STATE_OFF, SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH]
|
||||||
|
|
||||||
def turn_on(self, speed: str=SPEED_MEDIUM) -> None:
|
def turn_on(self, speed: str=None) -> None:
|
||||||
"""Turn on the entity."""
|
"""Turn on the entity."""
|
||||||
|
if speed is None:
|
||||||
|
speed = SPEED_MEDIUM
|
||||||
self.set_speed(speed)
|
self.set_speed(speed)
|
||||||
|
|
||||||
def turn_off(self) -> None:
|
def turn_off(self) -> None:
|
||||||
@ -68,17 +70,17 @@ class DemoFan(FanEntity):
|
|||||||
def set_speed(self, speed: str) -> None:
|
def set_speed(self, speed: str) -> None:
|
||||||
"""Set the speed of the fan."""
|
"""Set the speed of the fan."""
|
||||||
self._speed = speed
|
self._speed = speed
|
||||||
self.update_ha_state()
|
self.schedule_update_ha_state()
|
||||||
|
|
||||||
def set_direction(self, direction: str) -> None:
|
def set_direction(self, direction: str) -> None:
|
||||||
"""Set the direction of the fan."""
|
"""Set the direction of the fan."""
|
||||||
self.direction = direction
|
self.direction = direction
|
||||||
self.update_ha_state()
|
self.schedule_update_ha_state()
|
||||||
|
|
||||||
def oscillate(self, oscillating: bool) -> None:
|
def oscillate(self, oscillating: bool) -> None:
|
||||||
"""Set oscillation."""
|
"""Set oscillation."""
|
||||||
self.oscillating = oscillating
|
self.oscillating = oscillating
|
||||||
self.update_ha_state()
|
self.schedule_update_ha_state()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def current_direction(self) -> str:
|
def current_direction(self) -> str:
|
||||||
|
@ -150,7 +150,7 @@ class MqttFan(FanEntity):
|
|||||||
elif payload == self._payload[STATE_OFF]:
|
elif payload == self._payload[STATE_OFF]:
|
||||||
self._state = False
|
self._state = False
|
||||||
|
|
||||||
self.update_ha_state()
|
self.schedule_update_ha_state()
|
||||||
|
|
||||||
if self._topic[CONF_STATE_TOPIC] is not None:
|
if self._topic[CONF_STATE_TOPIC] is not None:
|
||||||
mqtt.subscribe(self._hass, self._topic[CONF_STATE_TOPIC],
|
mqtt.subscribe(self._hass, self._topic[CONF_STATE_TOPIC],
|
||||||
@ -165,7 +165,7 @@ class MqttFan(FanEntity):
|
|||||||
self._speed = SPEED_MEDIUM
|
self._speed = SPEED_MEDIUM
|
||||||
elif payload == self._payload[SPEED_HIGH]:
|
elif payload == self._payload[SPEED_HIGH]:
|
||||||
self._speed = SPEED_HIGH
|
self._speed = SPEED_HIGH
|
||||||
self.update_ha_state()
|
self.schedule_update_ha_state()
|
||||||
|
|
||||||
if self._topic[CONF_SPEED_STATE_TOPIC] is not None:
|
if self._topic[CONF_SPEED_STATE_TOPIC] is not None:
|
||||||
mqtt.subscribe(self._hass, self._topic[CONF_SPEED_STATE_TOPIC],
|
mqtt.subscribe(self._hass, self._topic[CONF_SPEED_STATE_TOPIC],
|
||||||
@ -183,7 +183,7 @@ class MqttFan(FanEntity):
|
|||||||
self._oscillation = True
|
self._oscillation = True
|
||||||
elif payload == self._payload[OSCILLATE_OFF_PAYLOAD]:
|
elif payload == self._payload[OSCILLATE_OFF_PAYLOAD]:
|
||||||
self._oscillation = False
|
self._oscillation = False
|
||||||
self.update_ha_state()
|
self.schedule_update_ha_state()
|
||||||
|
|
||||||
if self._topic[CONF_OSCILLATION_STATE_TOPIC] is not None:
|
if self._topic[CONF_OSCILLATION_STATE_TOPIC] is not None:
|
||||||
mqtt.subscribe(self._hass,
|
mqtt.subscribe(self._hass,
|
||||||
@ -262,7 +262,7 @@ class MqttFan(FanEntity):
|
|||||||
self._speed = speed
|
self._speed = speed
|
||||||
mqtt.publish(self._hass, self._topic[CONF_SPEED_COMMAND_TOPIC],
|
mqtt.publish(self._hass, self._topic[CONF_SPEED_COMMAND_TOPIC],
|
||||||
mqtt_payload, self._qos, self._retain)
|
mqtt_payload, self._qos, self._retain)
|
||||||
self.update_ha_state()
|
self.schedule_update_ha_state()
|
||||||
|
|
||||||
def oscillate(self, oscillating: bool) -> None:
|
def oscillate(self, oscillating: bool) -> None:
|
||||||
"""Set oscillation."""
|
"""Set oscillation."""
|
||||||
@ -274,4 +274,4 @@ class MqttFan(FanEntity):
|
|||||||
mqtt.publish(self._hass,
|
mqtt.publish(self._hass,
|
||||||
self._topic[CONF_OSCILLATION_COMMAND_TOPIC],
|
self._topic[CONF_OSCILLATION_COMMAND_TOPIC],
|
||||||
payload, self._qos, self._retain)
|
payload, self._qos, self._retain)
|
||||||
self.update_ha_state()
|
self.schedule_update_ha_state()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user