Fix callback and async (#31281)

* Fix callback and async

* Fix a return

* Fix test

* Fix mqtt tests

* Fix some more callbacks
This commit is contained in:
Paulus Schoutsen 2020-01-29 13:59:45 -08:00 committed by GitHub
parent ee602e40a6
commit e9e44dbd97
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
90 changed files with 627 additions and 883 deletions

View file

@ -121,67 +121,49 @@ class AlarmControlPanel(Entity):
"""Send disarm command.""" """Send disarm command."""
raise NotImplementedError() raise NotImplementedError()
def async_alarm_disarm(self, code=None): async def async_alarm_disarm(self, code=None):
"""Send disarm command. """Send disarm command."""
await self.hass.async_add_executor_job(self.alarm_disarm, code)
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_executor_job(self.alarm_disarm, code)
def alarm_arm_home(self, code=None): def alarm_arm_home(self, code=None):
"""Send arm home command.""" """Send arm home command."""
raise NotImplementedError() raise NotImplementedError()
def async_alarm_arm_home(self, code=None): async def async_alarm_arm_home(self, code=None):
"""Send arm home command. """Send arm home command."""
await self.hass.async_add_executor_job(self.alarm_arm_home, code)
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_executor_job(self.alarm_arm_home, code)
def alarm_arm_away(self, code=None): def alarm_arm_away(self, code=None):
"""Send arm away command.""" """Send arm away command."""
raise NotImplementedError() raise NotImplementedError()
def async_alarm_arm_away(self, code=None): async def async_alarm_arm_away(self, code=None):
"""Send arm away command. """Send arm away command."""
await self.hass.async_add_executor_job(self.alarm_arm_away, code)
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_executor_job(self.alarm_arm_away, code)
def alarm_arm_night(self, code=None): def alarm_arm_night(self, code=None):
"""Send arm night command.""" """Send arm night command."""
raise NotImplementedError() raise NotImplementedError()
def async_alarm_arm_night(self, code=None): async def async_alarm_arm_night(self, code=None):
"""Send arm night command. """Send arm night command."""
await self.hass.async_add_executor_job(self.alarm_arm_night, code)
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_executor_job(self.alarm_arm_night, code)
def alarm_trigger(self, code=None): def alarm_trigger(self, code=None):
"""Send alarm trigger command.""" """Send alarm trigger command."""
raise NotImplementedError() raise NotImplementedError()
def async_alarm_trigger(self, code=None): async def async_alarm_trigger(self, code=None):
"""Send alarm trigger command. """Send alarm trigger command."""
await self.hass.async_add_executor_job(self.alarm_trigger, code)
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_executor_job(self.alarm_trigger, code)
def alarm_arm_custom_bypass(self, code=None): def alarm_arm_custom_bypass(self, code=None):
"""Send arm custom bypass command.""" """Send arm custom bypass command."""
raise NotImplementedError() raise NotImplementedError()
def async_alarm_arm_custom_bypass(self, code=None): async def async_alarm_arm_custom_bypass(self, code=None):
"""Send arm custom bypass command. """Send arm custom bypass command."""
await self.hass.async_add_executor_job(self.alarm_arm_custom_bypass, code)
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_executor_job(self.alarm_arm_custom_bypass, code)
@property @property
@abstractmethod @abstractmethod

View file

@ -20,6 +20,7 @@ from homeassistant.const import (
STATE_OFF, STATE_OFF,
STATE_ON, STATE_ON,
) )
from homeassistant.core import callback
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -55,9 +56,10 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
_LOGGER.info("Provisioning Anthem AVR device at %s:%d", host, port) _LOGGER.info("Provisioning Anthem AVR device at %s:%d", host, port)
@callback
def async_anthemav_update_callback(message): def async_anthemav_update_callback(message):
"""Receive notification from transport that new data exists.""" """Receive notification from transport that new data exists."""
_LOGGER.info("Received update callback from AVR: %s", message) _LOGGER.debug("Received update callback from AVR: %s", message)
hass.async_create_task(device.async_update_ha_state()) hass.async_create_task(device.async_update_ha_state())
avr = await anthemav.Connection.create( avr = await anthemav.Connection.create(

View file

@ -411,6 +411,7 @@ async def async_services_json(hass):
return [{"domain": key, "services": value} for key, value in descriptions.items()] return [{"domain": key, "services": value} for key, value in descriptions.items()]
@ha.callback
def async_events_json(hass): def async_events_json(hass):
"""Generate event data to JSONify.""" """Generate event data to JSONify."""
return [ return [

View file

@ -229,62 +229,42 @@ class AppleTvDevice(MediaPlayerDevice):
self._playing = None self._playing = None
self._power.set_power_on(False) self._power.set_power_on(False)
def async_media_play_pause(self): async def async_media_play_pause(self):
"""Pause media on media player. """Pause media on media player."""
if not self._playing:
return
state = self.state
if state == STATE_PAUSED:
await self.atv.remote_control.play()
elif state == STATE_PLAYING:
await self.atv.remote_control.pause()
This method must be run in the event loop and returns a coroutine. async def async_media_play(self):
""" """Play media."""
if self._playing: if self._playing:
state = self.state await self.atv.remote_control.play()
if state == STATE_PAUSED:
return self.atv.remote_control.play()
if state == STATE_PLAYING:
return self.atv.remote_control.pause()
def async_media_play(self): async def async_media_stop(self):
"""Play media. """Stop the media player."""
This method must be run in the event loop and returns a coroutine.
"""
if self._playing: if self._playing:
return self.atv.remote_control.play() await self.atv.remote_control.stop()
def async_media_stop(self): async def async_media_pause(self):
"""Stop the media player. """Pause the media player."""
This method must be run in the event loop and returns a coroutine.
"""
if self._playing: if self._playing:
return self.atv.remote_control.stop() await self.atv.remote_control.pause()
def async_media_pause(self): async def async_media_next_track(self):
"""Pause the media player. """Send next track command."""
This method must be run in the event loop and returns a coroutine.
"""
if self._playing: if self._playing:
return self.atv.remote_control.pause() await self.atv.remote_control.next()
def async_media_next_track(self): async def async_media_previous_track(self):
"""Send next track command. """Send previous track command."""
This method must be run in the event loop and returns a coroutine.
"""
if self._playing: if self._playing:
return self.atv.remote_control.next() await self.atv.remote_control.previous()
def async_media_previous_track(self): async def async_media_seek(self, position):
"""Send previous track command. """Send seek command."""
This method must be run in the event loop and returns a coroutine.
"""
if self._playing: if self._playing:
return self.atv.remote_control.previous() await self.atv.remote_control.set_position(position)
def async_media_seek(self, position):
"""Send seek command.
This method must be run in the event loop and returns a coroutine.
"""
if self._playing:
return self.atv.remote_control.set_position(position)

View file

@ -61,17 +61,10 @@ class AppleTVRemote(remote.RemoteDevice):
""" """
self._power.set_power_on(False) self._power.set_power_on(False)
def async_send_command(self, command, **kwargs): async def async_send_command(self, command, **kwargs):
"""Send a command to one device. """Send a command to one device."""
for single_command in command:
if not hasattr(self._atv.remote_control, single_command):
continue
This method must be run in the event loop and returns a coroutine. await getattr(self._atv.remote_control, single_command)()
"""
# Send commands in specified order but schedule only one coroutine
async def _send_commands():
for single_command in command:
if not hasattr(self._atv.remote_control, single_command):
continue
await getattr(self._atv.remote_control, single_command)()
return _send_commands()

View file

@ -1,4 +1,5 @@
"""Support for the Asterisk Voicemail interface.""" """Support for the Asterisk Voicemail interface."""
from functools import partial
import logging import logging
from asterisk_mbox import ServerError from asterisk_mbox import ServerError
@ -55,7 +56,9 @@ class AsteriskMailbox(Mailbox):
client = self.hass.data[ASTERISK_DOMAIN].client client = self.hass.data[ASTERISK_DOMAIN].client
try: try:
return client.mp3(msgid, sync=True) return await self.hass.async_add_executor_job(
partial(client.mp3, msgid, sync=True)
)
except ServerError as err: except ServerError as err:
raise StreamError(err) raise StreamError(err)
@ -63,9 +66,9 @@ class AsteriskMailbox(Mailbox):
"""Return a list of the current messages.""" """Return a list of the current messages."""
return self.hass.data[ASTERISK_DOMAIN].messages return self.hass.data[ASTERISK_DOMAIN].messages
def async_delete(self, msgid): async def async_delete(self, msgid):
"""Delete the specified messages.""" """Delete the specified messages."""
client = self.hass.data[ASTERISK_DOMAIN].client client = self.hass.data[ASTERISK_DOMAIN].client
_LOGGER.info("Deleting: %s", msgid) _LOGGER.info("Deleting: %s", msgid)
client.delete(msgid) await self.hass.async_add_executor_job(client.delete, msgid)
return True return True

View file

@ -92,6 +92,7 @@ async def async_attach_trigger(hass, config, action, automation_info):
hass.data["litejet_system"].on_switch_pressed(number, pressed) hass.data["litejet_system"].on_switch_pressed(number, pressed)
hass.data["litejet_system"].on_switch_released(number, released) hass.data["litejet_system"].on_switch_released(number, released)
@callback
def async_remove(): def async_remove():
"""Remove all subscriptions used for this trigger.""" """Remove all subscriptions used for this trigger."""
return return

View file

@ -5,7 +5,7 @@ import voluptuous as vol
from homeassistant.components.device_automation.const import CONF_IS_OFF, CONF_IS_ON from homeassistant.components.device_automation.const import CONF_IS_OFF, CONF_IS_ON
from homeassistant.const import ATTR_DEVICE_CLASS, CONF_ENTITY_ID, CONF_FOR, CONF_TYPE from homeassistant.const import ATTR_DEVICE_CLASS, CONF_ENTITY_ID, CONF_FOR, CONF_TYPE
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import condition, config_validation as cv from homeassistant.helpers import condition, config_validation as cv
from homeassistant.helpers.entity_registry import ( from homeassistant.helpers.entity_registry import (
async_entries_for_device, async_entries_for_device,
@ -232,6 +232,7 @@ async def async_get_conditions(
return conditions return conditions
@callback
def async_condition_from_config( def async_condition_from_config(
config: ConfigType, config_validation: bool config: ConfigType, config_validation: bool
) -> condition.ConditionCheckerType: ) -> condition.ConditionCheckerType:

View file

@ -10,6 +10,7 @@ import socket
import voluptuous as vol import voluptuous as vol
from homeassistant.const import CONF_HOST from homeassistant.const import CONF_HOST
from homeassistant.core import callback
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.util.dt import utcnow from homeassistant.util.dt import utcnow
@ -64,67 +65,67 @@ SERVICE_SEND_SCHEMA = vol.Schema(
SERVICE_LEARN_SCHEMA = vol.Schema({vol.Required(CONF_HOST): cv.string}) SERVICE_LEARN_SCHEMA = vol.Schema({vol.Required(CONF_HOST): cv.string})
@callback
def async_setup_service(hass, host, device): def async_setup_service(hass, host, device):
"""Register a device for given host for use in services.""" """Register a device for given host for use in services."""
hass.data.setdefault(DOMAIN, {})[host] = device hass.data.setdefault(DOMAIN, {})[host] = device
if not hass.services.has_service(DOMAIN, SERVICE_LEARN): if hass.services.has_service(DOMAIN, SERVICE_LEARN):
return
async def _learn_command(call): async def _learn_command(call):
"""Learn a packet from remote.""" """Learn a packet from remote."""
device = hass.data[DOMAIN][call.data[CONF_HOST]] device = hass.data[DOMAIN][call.data[CONF_HOST]]
try: try:
auth = await hass.async_add_executor_job(device.auth) auth = await hass.async_add_executor_job(device.auth)
except socket.timeout: except socket.timeout:
_LOGGER.error("Failed to connect to device, timeout") _LOGGER.error("Failed to connect to device, timeout")
return
if not auth:
_LOGGER.error("Failed to connect to device")
return
await hass.async_add_executor_job(device.enter_learning)
_LOGGER.info("Press the key you want Home Assistant to learn")
start_time = utcnow()
while (utcnow() - start_time) < timedelta(seconds=20):
packet = await hass.async_add_executor_job(device.check_data)
if packet:
data = b64encode(packet).decode("utf8")
log_msg = f"Received packet is: {data}"
_LOGGER.info(log_msg)
hass.components.persistent_notification.async_create(
log_msg, title="Broadlink switch"
)
return return
if not auth: await asyncio.sleep(1)
_LOGGER.error("Failed to connect to device") _LOGGER.error("No signal was received")
return hass.components.persistent_notification.async_create(
"No signal was received", title="Broadlink switch"
await hass.async_add_executor_job(device.enter_learning)
_LOGGER.info("Press the key you want Home Assistant to learn")
start_time = utcnow()
while (utcnow() - start_time) < timedelta(seconds=20):
packet = await hass.async_add_executor_job(device.check_data)
if packet:
data = b64encode(packet).decode("utf8")
log_msg = f"Received packet is: {data}"
_LOGGER.info(log_msg)
hass.components.persistent_notification.async_create(
log_msg, title="Broadlink switch"
)
return
await asyncio.sleep(1)
_LOGGER.error("No signal was received")
hass.components.persistent_notification.async_create(
"No signal was received", title="Broadlink switch"
)
hass.services.async_register(
DOMAIN, SERVICE_LEARN, _learn_command, schema=SERVICE_LEARN_SCHEMA
) )
if not hass.services.has_service(DOMAIN, SERVICE_SEND): hass.services.async_register(
DOMAIN, SERVICE_LEARN, _learn_command, schema=SERVICE_LEARN_SCHEMA
)
async def _send_packet(call): async def _send_packet(call):
"""Send a packet.""" """Send a packet."""
device = hass.data[DOMAIN][call.data[CONF_HOST]] device = hass.data[DOMAIN][call.data[CONF_HOST]]
packets = call.data[CONF_PACKET] packets = call.data[CONF_PACKET]
for packet in packets: for packet in packets:
for retry in range(DEFAULT_RETRY): for retry in range(DEFAULT_RETRY):
try:
await hass.async_add_executor_job(device.send_data, packet)
break
except (socket.timeout, ValueError):
try: try:
await hass.async_add_executor_job(device.send_data, packet) await hass.async_add_executor_job(device.auth)
break except socket.timeout:
except (socket.timeout, ValueError): if retry == DEFAULT_RETRY - 1:
try: _LOGGER.error("Failed to send packet to device")
await hass.async_add_executor_job(device.auth)
except socket.timeout:
if retry == DEFAULT_RETRY - 1:
_LOGGER.error("Failed to send packet to device")
hass.services.async_register( hass.services.async_register(
DOMAIN, SERVICE_SEND, _send_packet, schema=SERVICE_SEND_SCHEMA DOMAIN, SERVICE_SEND, _send_packet, schema=SERVICE_SEND_SCHEMA
) )

View file

@ -364,19 +364,12 @@ class Camera(Entity):
"""Return bytes of camera image.""" """Return bytes of camera image."""
raise NotImplementedError() raise NotImplementedError()
@callback async def async_camera_image(self):
def async_camera_image(self): """Return bytes of camera image."""
"""Return bytes of camera image. return await self.hass.async_add_job(self.camera_image)
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(self.camera_image)
async def handle_async_still_stream(self, request, interval): async def handle_async_still_stream(self, request, interval):
"""Generate an HTTP MJPEG stream from camera images. """Generate an HTTP MJPEG stream from camera images."""
This method must be run in the event loop.
"""
return await async_get_still_stream( return await async_get_still_stream(
request, self.async_camera_image, self.content_type, interval request, self.async_camera_image, self.content_type, interval
) )
@ -386,7 +379,6 @@ class Camera(Entity):
This method can be overridden by camera plaforms to proxy This method can be overridden by camera plaforms to proxy
a direct stream from the camera. a direct stream from the camera.
This method must be run in the event loop.
""" """
return await self.handle_async_still_stream(request, self.frame_interval) return await self.handle_async_still_stream(request, self.frame_interval)

View file

@ -11,7 +11,7 @@ from homeassistant.const import (
CONF_ENTITY_ID, CONF_ENTITY_ID,
CONF_TYPE, CONF_TYPE,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import condition, config_validation as cv, entity_registry from homeassistant.helpers import condition, config_validation as cv, entity_registry
from homeassistant.helpers.config_validation import DEVICE_CONDITION_BASE_SCHEMA from homeassistant.helpers.config_validation import DEVICE_CONDITION_BASE_SCHEMA
from homeassistant.helpers.typing import ConfigType, TemplateVarsType from homeassistant.helpers.typing import ConfigType, TemplateVarsType
@ -77,6 +77,7 @@ async def async_get_conditions(
return conditions return conditions
@callback
def async_condition_from_config( def async_condition_from_config(
config: ConfigType, config_validation: bool config: ConfigType, config_validation: bool
) -> condition.ConditionCheckerType: ) -> condition.ConditionCheckerType:

View file

@ -236,23 +236,17 @@ class CoverDevice(Entity):
"""Open the cover.""" """Open the cover."""
raise NotImplementedError() raise NotImplementedError()
def async_open_cover(self, **kwargs): async def async_open_cover(self, **kwargs):
"""Open the cover. """Open the cover."""
await self.hass.async_add_job(ft.partial(self.open_cover, **kwargs))
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(ft.partial(self.open_cover, **kwargs))
def close_cover(self, **kwargs: Any) -> None: def close_cover(self, **kwargs: Any) -> None:
"""Close cover.""" """Close cover."""
raise NotImplementedError() raise NotImplementedError()
def async_close_cover(self, **kwargs): async def async_close_cover(self, **kwargs):
"""Close cover. """Close cover."""
await self.hass.async_add_job(ft.partial(self.close_cover, **kwargs))
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(ft.partial(self.close_cover, **kwargs))
def toggle(self, **kwargs: Any) -> None: def toggle(self, **kwargs: Any) -> None:
"""Toggle the entity.""" """Toggle the entity."""
@ -261,69 +255,52 @@ class CoverDevice(Entity):
else: else:
self.close_cover(**kwargs) self.close_cover(**kwargs)
def async_toggle(self, **kwargs): async def async_toggle(self, **kwargs):
"""Toggle the entity. """Toggle the entity."""
This method must be run in the event loop and returns a coroutine.
"""
if self.is_closed: if self.is_closed:
return self.async_open_cover(**kwargs) await self.async_open_cover(**kwargs)
return self.async_close_cover(**kwargs) else:
await self.async_close_cover(**kwargs)
def set_cover_position(self, **kwargs): def set_cover_position(self, **kwargs):
"""Move the cover to a specific position.""" """Move the cover to a specific position."""
pass pass
def async_set_cover_position(self, **kwargs): async def async_set_cover_position(self, **kwargs):
"""Move the cover to a specific position. """Move the cover to a specific position."""
await self.hass.async_add_job(ft.partial(self.set_cover_position, **kwargs))
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(ft.partial(self.set_cover_position, **kwargs))
def stop_cover(self, **kwargs): def stop_cover(self, **kwargs):
"""Stop the cover.""" """Stop the cover."""
pass pass
def async_stop_cover(self, **kwargs): async def async_stop_cover(self, **kwargs):
"""Stop the cover. """Stop the cover."""
await self.hass.async_add_job(ft.partial(self.stop_cover, **kwargs))
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(ft.partial(self.stop_cover, **kwargs))
def open_cover_tilt(self, **kwargs: Any) -> None: def open_cover_tilt(self, **kwargs: Any) -> None:
"""Open the cover tilt.""" """Open the cover tilt."""
pass pass
def async_open_cover_tilt(self, **kwargs): async def async_open_cover_tilt(self, **kwargs):
"""Open the cover tilt. """Open the cover tilt."""
await self.hass.async_add_job(ft.partial(self.open_cover_tilt, **kwargs))
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(ft.partial(self.open_cover_tilt, **kwargs))
def close_cover_tilt(self, **kwargs: Any) -> None: def close_cover_tilt(self, **kwargs: Any) -> None:
"""Close the cover tilt.""" """Close the cover tilt."""
pass pass
def async_close_cover_tilt(self, **kwargs): async def async_close_cover_tilt(self, **kwargs):
"""Close the cover tilt. """Close the cover tilt."""
await self.hass.async_add_job(ft.partial(self.close_cover_tilt, **kwargs))
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(ft.partial(self.close_cover_tilt, **kwargs))
def set_cover_tilt_position(self, **kwargs): def set_cover_tilt_position(self, **kwargs):
"""Move the cover tilt to a specific position.""" """Move the cover tilt to a specific position."""
pass pass
def async_set_cover_tilt_position(self, **kwargs): async def async_set_cover_tilt_position(self, **kwargs):
"""Move the cover tilt to a specific position. """Move the cover tilt to a specific position."""
await self.hass.async_add_job(
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(
ft.partial(self.set_cover_tilt_position, **kwargs) ft.partial(self.set_cover_tilt_position, **kwargs)
) )
@ -331,12 +308,9 @@ class CoverDevice(Entity):
"""Stop the cover.""" """Stop the cover."""
pass pass
def async_stop_cover_tilt(self, **kwargs): async def async_stop_cover_tilt(self, **kwargs):
"""Stop the cover. """Stop the cover."""
await self.hass.async_add_job(ft.partial(self.stop_cover_tilt, **kwargs))
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(ft.partial(self.stop_cover_tilt, **kwargs))
def toggle_tilt(self, **kwargs: Any) -> None: def toggle_tilt(self, **kwargs: Any) -> None:
"""Toggle the entity.""" """Toggle the entity."""
@ -345,11 +319,9 @@ class CoverDevice(Entity):
else: else:
self.close_cover_tilt(**kwargs) self.close_cover_tilt(**kwargs)
def async_toggle_tilt(self, **kwargs): async def async_toggle_tilt(self, **kwargs):
"""Toggle the entity. """Toggle the entity."""
This method must be run in the event loop and returns a coroutine.
"""
if self.current_cover_tilt_position == 0: if self.current_cover_tilt_position == 0:
return self.async_open_cover_tilt(**kwargs) await self.async_open_cover_tilt(**kwargs)
return self.async_close_cover_tilt(**kwargs) else:
await self.async_close_cover_tilt(**kwargs)

View file

@ -18,7 +18,7 @@ from homeassistant.const import (
STATE_OPEN, STATE_OPEN,
STATE_OPENING, STATE_OPENING,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import ( from homeassistant.helpers import (
condition, condition,
config_validation as cv, config_validation as cv,
@ -163,6 +163,7 @@ async def async_get_condition_capabilities(hass: HomeAssistant, config: dict) ->
} }
@callback
def async_condition_from_config( def async_condition_from_config(
config: ConfigType, config_validation: bool config: ConfigType, config_validation: bool
) -> condition.ConditionCheckerType: ) -> condition.ConditionCheckerType:
@ -196,6 +197,7 @@ def async_condition_from_config(
f"{{{{ state.attributes.{position} }}}}" f"{{{{ state.attributes.{position} }}}}"
) )
@callback
def template_if(hass: HomeAssistant, variables: TemplateVarsType = None) -> bool: def template_if(hass: HomeAssistant, variables: TemplateVarsType = None) -> bool:
"""Validate template based if-condition.""" """Validate template based if-condition."""
value_template.hass = hass value_template.hass = hass

View file

@ -71,7 +71,7 @@ class DemoMailbox(Mailbox):
reverse=True, reverse=True,
) )
def async_delete(self, msgid): async def async_delete(self, msgid):
"""Delete the specified messages.""" """Delete the specified messages."""
if msgid in self._messages: if msgid in self._messages:
_LOGGER.info("Deleting: %s", msgid) _LOGGER.info("Deleting: %s", msgid)

View file

@ -24,7 +24,7 @@ from homeassistant.const import (
CONF_PLATFORM, CONF_PLATFORM,
CONF_TYPE, CONF_TYPE,
) )
from homeassistant.core import CALLBACK_TYPE, Context, HomeAssistant from homeassistant.core import CALLBACK_TYPE, Context, HomeAssistant, callback
from homeassistant.helpers import condition, config_validation as cv from homeassistant.helpers import condition, config_validation as cv
from homeassistant.helpers.entity_registry import async_entries_for_device from homeassistant.helpers.entity_registry import async_entries_for_device
from homeassistant.helpers.typing import ConfigType, TemplateVarsType from homeassistant.helpers.typing import ConfigType, TemplateVarsType
@ -121,6 +121,7 @@ async def async_call_action_from_config(
) )
@callback
def async_condition_from_config(config: ConfigType) -> condition.ConditionCheckerType: def async_condition_from_config(config: ConfigType) -> condition.ConditionCheckerType:
"""Evaluate state based on configuration.""" """Evaluate state based on configuration."""
condition_type = config[CONF_TYPE] condition_type = config[CONF_TYPE]

View file

@ -113,29 +113,27 @@ async def async_setup(hass, config):
return None return None
return next_setting - LIGHT_TRANSITION_TIME * len(light_ids) return next_setting - LIGHT_TRANSITION_TIME * len(light_ids)
def async_turn_on_before_sunset(light_id): async def async_turn_on_before_sunset(light_id):
"""Turn on lights.""" """Turn on lights."""
if not anyone_home() or light.is_on(light_id): if not anyone_home() or light.is_on(light_id):
return return
hass.async_create_task( await hass.services.async_call(
hass.services.async_call( DOMAIN_LIGHT,
DOMAIN_LIGHT, SERVICE_TURN_ON,
SERVICE_TURN_ON, {
{ ATTR_ENTITY_ID: light_id,
ATTR_ENTITY_ID: light_id, ATTR_TRANSITION: LIGHT_TRANSITION_TIME.seconds,
ATTR_TRANSITION: LIGHT_TRANSITION_TIME.seconds, ATTR_PROFILE: light_profile,
ATTR_PROFILE: light_profile, },
},
)
) )
@callback
def async_turn_on_factory(light_id): def async_turn_on_factory(light_id):
"""Generate turn on callbacks as factory.""" """Generate turn on callbacks as factory."""
@callback async def async_turn_on_light(now):
def async_turn_on_light(now):
"""Turn on specific light.""" """Turn on specific light."""
async_turn_on_before_sunset(light_id) await async_turn_on_before_sunset(light_id)
return async_turn_on_light return async_turn_on_light

View file

@ -13,7 +13,7 @@ from homeassistant.const import (
STATE_HOME, STATE_HOME,
STATE_NOT_HOME, STATE_NOT_HOME,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import condition, config_validation as cv, entity_registry from homeassistant.helpers import condition, config_validation as cv, entity_registry
from homeassistant.helpers.config_validation import DEVICE_CONDITION_BASE_SCHEMA from homeassistant.helpers.config_validation import DEVICE_CONDITION_BASE_SCHEMA
from homeassistant.helpers.typing import ConfigType, TemplateVarsType from homeassistant.helpers.typing import ConfigType, TemplateVarsType
@ -65,6 +65,7 @@ async def async_get_conditions(
return conditions return conditions
@callback
def async_condition_from_config( def async_condition_from_config(
config: ConfigType, config_validation: bool config: ConfigType, config_validation: bool
) -> condition.ConditionCheckerType: ) -> condition.ConditionCheckerType:
@ -76,6 +77,7 @@ def async_condition_from_config(
else: else:
state = STATE_NOT_HOME state = STATE_NOT_HOME
@callback
def test_is_state(hass: HomeAssistant, variables: TemplateVarsType) -> bool: def test_is_state(hass: HomeAssistant, variables: TemplateVarsType) -> bool:
"""Test if an entity is a certain state.""" """Test if an entity is a certain state."""
return condition.state(hass, config[ATTR_ENTITY_ID], state) return condition.state(hass, config[ATTR_ENTITY_ID], state)

View file

@ -491,34 +491,25 @@ class DeviceScanner:
"""Scan for devices.""" """Scan for devices."""
raise NotImplementedError() raise NotImplementedError()
def async_scan_devices(self) -> Any: async def async_scan_devices(self) -> Any:
"""Scan for devices. """Scan for devices."""
return await self.hass.async_add_job(self.scan_devices)
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(self.scan_devices)
def get_device_name(self, device: str) -> str: def get_device_name(self, device: str) -> str:
"""Get the name of a device.""" """Get the name of a device."""
raise NotImplementedError() raise NotImplementedError()
def async_get_device_name(self, device: str) -> Any: async def async_get_device_name(self, device: str) -> Any:
"""Get the name of a device. """Get the name of a device."""
return await self.hass.async_add_job(self.get_device_name, device)
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(self.get_device_name, device)
def get_extra_attributes(self, device: str) -> dict: def get_extra_attributes(self, device: str) -> dict:
"""Get the extra attributes of a device.""" """Get the extra attributes of a device."""
raise NotImplementedError() raise NotImplementedError()
def async_get_extra_attributes(self, device: str) -> Any: async def async_get_extra_attributes(self, device: str) -> Any:
"""Get the extra attributes of a device. """Get the extra attributes of a device."""
return await self.hass.async_add_job(self.get_extra_attributes, device)
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(self.get_extra_attributes, device)
async def async_load_config( async def async_load_config(

View file

@ -309,44 +309,26 @@ class EmbyDevice(MediaPlayerDevice):
return SUPPORT_EMBY return SUPPORT_EMBY
return None return None
def async_media_play(self): async def async_media_play(self):
"""Play media. """Play media."""
await self.device.media_play()
This method must be run in the event loop and returns a coroutine. async def async_media_pause(self):
""" """Pause the media player."""
return self.device.media_play() await self.device.media_pause()
def async_media_pause(self): async def async_media_stop(self):
"""Pause the media player. """Stop the media player."""
await self.device.media_stop()
This method must be run in the event loop and returns a coroutine. async def async_media_next_track(self):
""" """Send next track command."""
return self.device.media_pause() await self.device.media_next()
def async_media_stop(self): async def async_media_previous_track(self):
"""Stop the media player. """Send next track command."""
await self.device.media_previous()
This method must be run in the event loop and returns a coroutine. async def async_media_seek(self, position):
""" """Send seek command."""
return self.device.media_stop() await self.device.media_seek(position)
def async_media_next_track(self):
"""Send next track command.
This method must be run in the event loop and returns a coroutine.
"""
return self.device.media_next()
def async_media_previous_track(self):
"""Send next track command.
This method must be run in the event loop and returns a coroutine.
"""
return self.device.media_previous()
def async_media_seek(self, position):
"""Send seek command.
This method must be run in the event loop and returns a coroutine.
"""
return self.device.media_seek(position)

View file

@ -21,6 +21,7 @@ from aioesphomeapi import (
import attr import attr
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.storage import Store from homeassistant.helpers.storage import Store
from homeassistant.helpers.typing import HomeAssistantType from homeassistant.helpers.typing import HomeAssistantType
@ -71,6 +72,7 @@ class RuntimeEntryData:
loaded_platforms = attr.ib(type=Set[str], factory=set) loaded_platforms = attr.ib(type=Set[str], factory=set)
platform_load_lock = attr.ib(type=asyncio.Lock, factory=asyncio.Lock) platform_load_lock = attr.ib(type=asyncio.Lock, factory=asyncio.Lock)
@callback
def async_update_entity( def async_update_entity(
self, hass: HomeAssistantType, component_key: str, key: int self, hass: HomeAssistantType, component_key: str, key: int
) -> None: ) -> None:
@ -80,6 +82,7 @@ class RuntimeEntryData:
) )
async_dispatcher_send(hass, signal) async_dispatcher_send(hass, signal)
@callback
def async_remove_entity( def async_remove_entity(
self, hass: HomeAssistantType, component_key: str, key: int self, hass: HomeAssistantType, component_key: str, key: int
) -> None: ) -> None:
@ -120,11 +123,13 @@ class RuntimeEntryData:
signal = DISPATCHER_ON_LIST.format(entry_id=self.entry_id) signal = DISPATCHER_ON_LIST.format(entry_id=self.entry_id)
async_dispatcher_send(hass, signal, infos) async_dispatcher_send(hass, signal, infos)
@callback
def async_update_state(self, hass: HomeAssistantType, state: EntityState) -> None: def async_update_state(self, hass: HomeAssistantType, state: EntityState) -> None:
"""Distribute an update of state information to all platforms.""" """Distribute an update of state information to all platforms."""
signal = DISPATCHER_ON_STATE.format(entry_id=self.entry_id) signal = DISPATCHER_ON_STATE.format(entry_id=self.entry_id)
async_dispatcher_send(hass, signal, state) async_dispatcher_send(hass, signal, state)
@callback
def async_update_device_state(self, hass: HomeAssistantType) -> None: def async_update_device_state(self, hass: HomeAssistantType) -> None:
"""Distribute an update of a core device state like availability.""" """Distribute an update of a core device state like availability."""
signal = DISPATCHER_ON_DEVICE_UPDATE.format(entry_id=self.entry_id) signal = DISPATCHER_ON_DEVICE_UPDATE.format(entry_id=self.entry_id)

View file

@ -111,25 +111,20 @@ class FanEntity(ToggleEntity):
"""Set the speed of the fan.""" """Set the speed of the fan."""
raise NotImplementedError() raise NotImplementedError()
def async_set_speed(self, speed: str): async def async_set_speed(self, speed: str):
"""Set the speed of the fan. """Set the speed of the fan."""
This method must be run in the event loop and returns a coroutine.
"""
if speed is SPEED_OFF: if speed is SPEED_OFF:
return self.async_turn_off() await self.async_turn_off()
return self.hass.async_add_job(self.set_speed, speed) else:
await self.hass.async_add_job(self.set_speed, speed)
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."""
raise NotImplementedError() raise NotImplementedError()
def async_set_direction(self, direction: str): async def async_set_direction(self, direction: str):
"""Set the direction of the fan. """Set the direction of the fan."""
await self.hass.async_add_job(self.set_direction, direction)
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(self.set_direction, direction)
# pylint: disable=arguments-differ # pylint: disable=arguments-differ
def turn_on(self, speed: Optional[str] = None, **kwargs) -> None: def turn_on(self, speed: Optional[str] = None, **kwargs) -> None:
@ -137,25 +132,20 @@ class FanEntity(ToggleEntity):
raise NotImplementedError() raise NotImplementedError()
# pylint: disable=arguments-differ # pylint: disable=arguments-differ
def async_turn_on(self, speed: Optional[str] = None, **kwargs): async def async_turn_on(self, speed: Optional[str] = None, **kwargs):
"""Turn on the fan. """Turn on the fan."""
This method must be run in the event loop and returns a coroutine.
"""
if speed is SPEED_OFF: if speed is SPEED_OFF:
return self.async_turn_off() await self.async_turn_off()
return self.hass.async_add_job(ft.partial(self.turn_on, speed, **kwargs)) else:
await self.hass.async_add_job(ft.partial(self.turn_on, speed, **kwargs))
def oscillate(self, oscillating: bool) -> None: def oscillate(self, oscillating: bool) -> None:
"""Oscillate the fan.""" """Oscillate the fan."""
pass pass
def async_oscillate(self, oscillating: bool): async def async_oscillate(self, oscillating: bool):
"""Oscillate the fan. """Oscillate the fan."""
await self.hass.async_add_job(self.oscillate, oscillating)
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(self.oscillate, oscillating)
@property @property
def is_on(self): def is_on(self):

View file

@ -13,7 +13,7 @@ from homeassistant.const import (
STATE_OFF, STATE_OFF,
STATE_ON, STATE_ON,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import condition, config_validation as cv, entity_registry from homeassistant.helpers import condition, config_validation as cv, entity_registry
from homeassistant.helpers.config_validation import DEVICE_CONDITION_BASE_SCHEMA from homeassistant.helpers.config_validation import DEVICE_CONDITION_BASE_SCHEMA
from homeassistant.helpers.typing import ConfigType, TemplateVarsType from homeassistant.helpers.typing import ConfigType, TemplateVarsType
@ -64,6 +64,7 @@ async def async_get_conditions(
return conditions return conditions
@callback
def async_condition_from_config( def async_condition_from_config(
config: ConfigType, config_validation: bool config: ConfigType, config_validation: bool
) -> condition.ConditionCheckerType: ) -> condition.ConditionCheckerType:
@ -75,6 +76,7 @@ def async_condition_from_config(
else: else:
state = STATE_OFF state = STATE_OFF
@callback
def test_is_state(hass: HomeAssistant, variables: TemplateVarsType) -> bool: def test_is_state(hass: HomeAssistant, variables: TemplateVarsType) -> bool:
"""Test if an entity is a certain state.""" """Test if an entity is a certain state."""
return condition.state(hass, config[ATTR_ENTITY_ID], state) return condition.state(hass, config[ATTR_ENTITY_ID], state)

View file

@ -453,10 +453,7 @@ class GenericThermostat(ClimateDevice, RestoreEntity):
await self.hass.services.async_call(HA_DOMAIN, SERVICE_TURN_OFF, data) await self.hass.services.async_call(HA_DOMAIN, SERVICE_TURN_OFF, data)
async def async_set_preset_mode(self, preset_mode: str): async def async_set_preset_mode(self, preset_mode: str):
"""Set new preset mode. """Set new preset mode."""
This method must be run in the event loop and returns a coroutine.
"""
if preset_mode == PRESET_AWAY and not self._is_away: if preset_mode == PRESET_AWAY and not self._is_away:
self._is_away = True self._is_away = True
self._saved_target_temp = self._target_temp self._saved_target_temp = self._target_temp

View file

@ -122,6 +122,7 @@ class AbstractConfig(ABC):
] ]
await gather(*jobs) await gather(*jobs)
@callback
def async_enable_report_state(self): def async_enable_report_state(self):
"""Enable proactive mode.""" """Enable proactive mode."""
# Circular dep # Circular dep
@ -131,6 +132,7 @@ class AbstractConfig(ABC):
if self._unsub_report_state is None: if self._unsub_report_state is None:
self._unsub_report_state = async_enable_report_state(self.hass, self) self._unsub_report_state = async_enable_report_state(self.hass, self)
@callback
def async_disable_report_state(self): def async_disable_report_state(self):
"""Disable report state.""" """Disable report state."""
if self._unsub_report_state is not None: if self._unsub_report_state is not None:

View file

@ -7,6 +7,7 @@ import aiohttp
import hangups import hangups
from hangups import ChatMessageEvent, ChatMessageSegment, Client, get_auth, hangouts_pb2 from hangups import ChatMessageEvent, ChatMessageSegment, Client, get_auth, hangouts_pb2
from homeassistant.core import callback
from homeassistant.helpers import dispatcher, intent from homeassistant.helpers import dispatcher, intent
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
@ -75,6 +76,7 @@ class HangoutsBot:
return conv return conv
return None return None
@callback
def async_update_conversation_commands(self): def async_update_conversation_commands(self):
"""Refresh the commands for every conversation.""" """Refresh the commands for every conversation."""
self._conversation_intents = {} self._conversation_intents = {}
@ -110,6 +112,7 @@ class HangoutsBot:
self._async_handle_conversation_event self._async_handle_conversation_event
) )
@callback
def async_resolve_conversations(self, _): def async_resolve_conversations(self, _):
"""Resolve the list of default and error suppressed conversations.""" """Resolve the list of default and error suppressed conversations."""
self._default_conv_ids = [] self._default_conv_ids = []

View file

@ -2,6 +2,7 @@
from homekit.model.characteristics import CharacteristicsTypes from homekit.model.characteristics import CharacteristicsTypes
from homeassistant.components.air_quality import AirQualityEntity from homeassistant.components.air_quality import AirQualityEntity
from homeassistant.core import callback
from . import KNOWN_DEVICES, HomeKitEntity from . import KNOWN_DEVICES, HomeKitEntity
@ -83,6 +84,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
hkid = config_entry.data["AccessoryPairingID"] hkid = config_entry.data["AccessoryPairingID"]
conn = hass.data[KNOWN_DEVICES][hkid] conn = hass.data[KNOWN_DEVICES][hkid]
@callback
def async_add_service(aid, service): def async_add_service(aid, service):
if service["stype"] != "air-quality": if service["stype"] != "air-quality":
return False return False

View file

@ -17,6 +17,7 @@ from homeassistant.const import (
STATE_ALARM_DISARMED, STATE_ALARM_DISARMED,
STATE_ALARM_TRIGGERED, STATE_ALARM_TRIGGERED,
) )
from homeassistant.core import callback
from . import KNOWN_DEVICES, HomeKitEntity from . import KNOWN_DEVICES, HomeKitEntity
@ -45,6 +46,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
hkid = config_entry.data["AccessoryPairingID"] hkid = config_entry.data["AccessoryPairingID"]
conn = hass.data[KNOWN_DEVICES][hkid] conn = hass.data[KNOWN_DEVICES][hkid]
@callback
def async_add_service(aid, service): def async_add_service(aid, service):
if service["stype"] != "security-system": if service["stype"] != "security-system":
return False return False

View file

@ -7,6 +7,7 @@ from homeassistant.components.binary_sensor import (
DEVICE_CLASS_SMOKE, DEVICE_CLASS_SMOKE,
BinarySensorDevice, BinarySensorDevice,
) )
from homeassistant.core import callback
from . import KNOWN_DEVICES, HomeKitEntity from . import KNOWN_DEVICES, HomeKitEntity
@ -98,6 +99,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
hkid = config_entry.data["AccessoryPairingID"] hkid = config_entry.data["AccessoryPairingID"]
conn = hass.data[KNOWN_DEVICES][hkid] conn = hass.data[KNOWN_DEVICES][hkid]
@callback
def async_add_service(aid, service): def async_add_service(aid, service):
entity_class = ENTITY_TYPES.get(service["stype"]) entity_class = ENTITY_TYPES.get(service["stype"])
if not entity_class: if not entity_class:

View file

@ -20,6 +20,7 @@ from homeassistant.components.climate.const import (
SUPPORT_TARGET_TEMPERATURE, SUPPORT_TARGET_TEMPERATURE,
) )
from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS
from homeassistant.core import callback
from . import KNOWN_DEVICES, HomeKitEntity from . import KNOWN_DEVICES, HomeKitEntity
@ -50,6 +51,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
hkid = config_entry.data["AccessoryPairingID"] hkid = config_entry.data["AccessoryPairingID"]
conn = hass.data[KNOWN_DEVICES][hkid] conn = hass.data[KNOWN_DEVICES][hkid]
@callback
def async_add_service(aid, service): def async_add_service(aid, service):
if service["stype"] != "thermostat": if service["stype"] != "thermostat":
return False return False

View file

@ -12,6 +12,7 @@ from homekit.exceptions import (
from homekit.model.characteristics import CharacteristicsTypes from homekit.model.characteristics import CharacteristicsTypes
from homekit.model.services import ServicesTypes from homekit.model.services import ServicesTypes
from homeassistant.core import callback
from homeassistant.helpers.event import async_track_time_interval from homeassistant.helpers.event import async_track_time_interval
from .const import DOMAIN, ENTITY_MAP, HOMEKIT_ACCESSORY_DISPATCH from .const import DOMAIN, ENTITY_MAP, HOMEKIT_ACCESSORY_DISPATCH
@ -116,6 +117,7 @@ class HKDevice:
char for char in self.pollable_characteristics if char[0] != accessory_id char for char in self.pollable_characteristics if char[0] != accessory_id
] ]
@callback
def async_set_unavailable(self): def async_set_unavailable(self):
"""Mark state of all entities on this connection as unavailable.""" """Mark state of all entities on this connection as unavailable."""
self.available = False self.available = False

View file

@ -16,6 +16,7 @@ from homeassistant.components.cover import (
CoverDevice, CoverDevice,
) )
from homeassistant.const import STATE_CLOSED, STATE_CLOSING, STATE_OPEN, STATE_OPENING from homeassistant.const import STATE_CLOSED, STATE_CLOSING, STATE_OPEN, STATE_OPENING
from homeassistant.core import callback
from . import KNOWN_DEVICES, HomeKitEntity from . import KNOWN_DEVICES, HomeKitEntity
@ -41,6 +42,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
hkid = config_entry.data["AccessoryPairingID"] hkid = config_entry.data["AccessoryPairingID"]
conn = hass.data[KNOWN_DEVICES][hkid] conn = hass.data[KNOWN_DEVICES][hkid]
@callback
def async_add_service(aid, service): def async_add_service(aid, service):
info = {"aid": aid, "iid": service["iid"]} info = {"aid": aid, "iid": service["iid"]}
if service["stype"] == "garage-door-opener": if service["stype"] == "garage-door-opener":

View file

@ -15,6 +15,7 @@ from homeassistant.components.fan import (
SUPPORT_SET_SPEED, SUPPORT_SET_SPEED,
FanEntity, FanEntity,
) )
from homeassistant.core import callback
from . import KNOWN_DEVICES, HomeKitEntity from . import KNOWN_DEVICES, HomeKitEntity
@ -235,6 +236,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
hkid = config_entry.data["AccessoryPairingID"] hkid = config_entry.data["AccessoryPairingID"]
conn = hass.data[KNOWN_DEVICES][hkid] conn = hass.data[KNOWN_DEVICES][hkid]
@callback
def async_add_service(aid, service): def async_add_service(aid, service):
entity_class = ENTITY_TYPES.get(service["stype"]) entity_class = ENTITY_TYPES.get(service["stype"])
if not entity_class: if not entity_class:

View file

@ -12,6 +12,7 @@ from homeassistant.components.light import (
SUPPORT_COLOR_TEMP, SUPPORT_COLOR_TEMP,
Light, Light,
) )
from homeassistant.core import callback
from . import KNOWN_DEVICES, HomeKitEntity from . import KNOWN_DEVICES, HomeKitEntity
@ -23,6 +24,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
hkid = config_entry.data["AccessoryPairingID"] hkid = config_entry.data["AccessoryPairingID"]
conn = hass.data[KNOWN_DEVICES][hkid] conn = hass.data[KNOWN_DEVICES][hkid]
@callback
def async_add_service(aid, service): def async_add_service(aid, service):
if service["stype"] != "lightbulb": if service["stype"] != "lightbulb":
return False return False

View file

@ -5,6 +5,7 @@ from homekit.model.characteristics import CharacteristicsTypes
from homeassistant.components.lock import LockDevice from homeassistant.components.lock import LockDevice
from homeassistant.const import ATTR_BATTERY_LEVEL, STATE_LOCKED, STATE_UNLOCKED from homeassistant.const import ATTR_BATTERY_LEVEL, STATE_LOCKED, STATE_UNLOCKED
from homeassistant.core import callback
from . import KNOWN_DEVICES, HomeKitEntity from . import KNOWN_DEVICES, HomeKitEntity
@ -22,6 +23,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
hkid = config_entry.data["AccessoryPairingID"] hkid = config_entry.data["AccessoryPairingID"]
conn = hass.data[KNOWN_DEVICES][hkid] conn = hass.data[KNOWN_DEVICES][hkid]
@callback
def async_add_service(aid, service): def async_add_service(aid, service):
if service["stype"] != "lock-mechanism": if service["stype"] != "lock-mechanism":
return False return False

View file

@ -2,6 +2,7 @@
from homekit.model.characteristics import CharacteristicsTypes from homekit.model.characteristics import CharacteristicsTypes
from homeassistant.const import DEVICE_CLASS_BATTERY, TEMP_CELSIUS from homeassistant.const import DEVICE_CLASS_BATTERY, TEMP_CELSIUS
from homeassistant.core import callback
from . import KNOWN_DEVICES, HomeKitEntity from . import KNOWN_DEVICES, HomeKitEntity
@ -246,6 +247,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
hkid = config_entry.data["AccessoryPairingID"] hkid = config_entry.data["AccessoryPairingID"]
conn = hass.data[KNOWN_DEVICES][hkid] conn = hass.data[KNOWN_DEVICES][hkid]
@callback
def async_add_service(aid, service): def async_add_service(aid, service):
entity_class = ENTITY_TYPES.get(service["stype"]) entity_class = ENTITY_TYPES.get(service["stype"])
if not entity_class: if not entity_class:

View file

@ -45,6 +45,7 @@ class EntityMapStorage:
"""Get a pairing cache item.""" """Get a pairing cache item."""
return self.storage_data.get(homekit_id) return self.storage_data.get(homekit_id)
@callback
def async_create_or_update_map(self, homekit_id, config_num, accessories): def async_create_or_update_map(self, homekit_id, config_num, accessories):
"""Create a new pairing cache.""" """Create a new pairing cache."""
data = {"config_num": config_num, "accessories": accessories} data = {"config_num": config_num, "accessories": accessories}
@ -52,6 +53,7 @@ class EntityMapStorage:
self._async_schedule_save() self._async_schedule_save()
return data return data
@callback
def async_delete_map(self, homekit_id): def async_delete_map(self, homekit_id):
"""Delete pairing cache.""" """Delete pairing cache."""
if homekit_id not in self.storage_data: if homekit_id not in self.storage_data:

View file

@ -4,6 +4,7 @@ import logging
from homekit.model.characteristics import CharacteristicsTypes from homekit.model.characteristics import CharacteristicsTypes
from homeassistant.components.switch import SwitchDevice from homeassistant.components.switch import SwitchDevice
from homeassistant.core import callback
from . import KNOWN_DEVICES, HomeKitEntity from . import KNOWN_DEVICES, HomeKitEntity
@ -17,6 +18,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
hkid = config_entry.data["AccessoryPairingID"] hkid = config_entry.data["AccessoryPairingID"]
conn = hass.data[KNOWN_DEVICES][hkid] conn = hass.data[KNOWN_DEVICES][hkid]
@callback
def async_add_service(aid, service): def async_add_service(aid, service):
if service["stype"] not in ("switch", "outlet"): if service["stype"] not in ("switch", "outlet"):
return False return False

View file

@ -13,6 +13,7 @@ from homeassistant.components.device_tracker import (
) )
from homeassistant.components.device_tracker.config_entry import ScannerEntity from homeassistant.components.device_tracker.config_entry import ScannerEntity
from homeassistant.const import CONF_URL from homeassistant.const import CONF_URL
from homeassistant.core import callback
from homeassistant.helpers import entity_registry from homeassistant.helpers import entity_registry
from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.dispatcher import async_dispatcher_connect
@ -70,6 +71,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
async_add_new_entities(hass, router.url, async_add_entities, tracked) async_add_new_entities(hass, router.url, async_add_entities, tracked)
@callback
def async_add_new_entities(hass, router_url, async_add_entities, tracked): def async_add_new_entities(hass, router_url, async_add_entities, tracked):
"""Add new entities that are not already being tracked.""" """Add new entities that are not already being tracked."""
router = hass.data[DOMAIN].routers[router_url] router = hass.data[DOMAIN].routers[router_url]

View file

@ -2,6 +2,8 @@
import asyncio import asyncio
from homeassistant.core import callback
async def async_pulse(hass, ihc_controller, ihc_id: int): async def async_pulse(hass, ihc_controller, ihc_id: int):
"""Send a short on/off pulse to an IHC controller resource.""" """Send a short on/off pulse to an IHC controller resource."""
@ -10,6 +12,7 @@ async def async_pulse(hass, ihc_controller, ihc_id: int):
await async_set_bool(hass, ihc_controller, ihc_id, False) await async_set_bool(hass, ihc_controller, ihc_id, False)
@callback
def async_set_bool(hass, ihc_controller, ihc_id: int, value: bool): def async_set_bool(hass, ihc_controller, ihc_id: int, value: bool):
"""Set a bool value on an IHC controller resource.""" """Set a bool value on an IHC controller resource."""
return hass.async_add_executor_job( return hass.async_add_executor_job(
@ -17,6 +20,7 @@ def async_set_bool(hass, ihc_controller, ihc_id: int, value: bool):
) )
@callback
def async_set_int(hass, ihc_controller, ihc_id: int, value: int): def async_set_int(hass, ihc_controller, ihc_id: int, value: int):
"""Set a int value on an IHC controller resource.""" """Set a int value on an IHC controller resource."""
return hass.async_add_executor_job( return hass.async_add_executor_job(

View file

@ -107,12 +107,9 @@ class ImageProcessingEntity(Entity):
"""Process image.""" """Process image."""
raise NotImplementedError() raise NotImplementedError()
def async_process_image(self, image): async def async_process_image(self, image):
"""Process image. """Process image."""
return await self.hass.async_add_job(self.process_image, image)
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(self.process_image, image)
async def async_update(self): async def async_update(self):
"""Update image and process it. """Update image and process it.

View file

@ -326,6 +326,7 @@ class InputDatetime(RestoreEntity):
"""Return unique id of the entity.""" """Return unique id of the entity."""
return self._config[CONF_ID] return self._config[CONF_ID]
@callback
def async_set_datetime(self, date_val, time_val): def async_set_datetime(self, date_val, time_val):
"""Set a new date / time.""" """Set a new date / time."""
if self.has_date and self.has_time and date_val and time_val: if self.has_date and self.has_time and date_val and time_val:

View file

@ -48,9 +48,8 @@ class KiraRemote(Entity):
_LOGGER.info("Sending Command: %s to %s", *code_tuple) _LOGGER.info("Sending Command: %s to %s", *code_tuple)
self._kira.sendCode(code_tuple) self._kira.sendCode(code_tuple)
def async_send_command(self, command, **kwargs): async def async_send_command(self, command, **kwargs):
"""Send a command to a device. """Send a command to a device."""
return await self.hass.async_add_job(
This method must be run in the event loop and returns a coroutine. ft.partial(self.send_command, command, **kwargs)
""" )
return self.hass.async_add_job(ft.partial(self.send_command, command, **kwargs))

View file

@ -330,10 +330,7 @@ class KNXClimate(ClimateDevice):
return list(filter(None, _presets)) return list(filter(None, _presets))
async def async_set_preset_mode(self, preset_mode: str) -> None: async def async_set_preset_mode(self, preset_mode: str) -> None:
"""Set new preset mode. """Set new preset mode."""
This method must be run in the event loop and returns a coroutine.
"""
if self.device.mode.supports_operation_mode: if self.device.mode.supports_operation_mode:
knx_operation_mode = HVACOperationMode(PRESET_MODES_INV.get(preset_mode)) knx_operation_mode = HVACOperationMode(PRESET_MODES_INV.get(preset_mode))
await self.device.mode.set_operation_mode(knx_operation_mode) await self.device.mode.set_operation_mode(knx_operation_mode)

View file

@ -668,20 +668,14 @@ class KodiDevice(MediaPlayerDevice):
assert (await self.server.Input.ExecuteAction("volumedown")) == "OK" assert (await self.server.Input.ExecuteAction("volumedown")) == "OK"
@cmd @cmd
def async_set_volume_level(self, volume): async def async_set_volume_level(self, volume):
"""Set volume level, range 0..1. """Set volume level, range 0..1."""
await self.server.Application.SetVolume(int(volume * 100))
This method must be run in the event loop and returns a coroutine.
"""
return self.server.Application.SetVolume(int(volume * 100))
@cmd @cmd
def async_mute_volume(self, mute): async def async_mute_volume(self, mute):
"""Mute (true) or unmute (false) media player. """Mute (true) or unmute (false) media player."""
await self.server.Application.SetMute(mute)
This method must be run in the event loop and returns a coroutine.
"""
return self.server.Application.SetMute(mute)
async def async_set_play_state(self, state): async def async_set_play_state(self, state):
"""Handle play/pause/toggle.""" """Handle play/pause/toggle."""
@ -691,28 +685,19 @@ class KodiDevice(MediaPlayerDevice):
await self.server.Player.PlayPause(players[0]["playerid"], state) await self.server.Player.PlayPause(players[0]["playerid"], state)
@cmd @cmd
def async_media_play_pause(self): async def async_media_play_pause(self):
"""Pause media on media player. """Pause media on media player."""
await self.async_set_play_state("toggle")
This method must be run in the event loop and returns a coroutine.
"""
return self.async_set_play_state("toggle")
@cmd @cmd
def async_media_play(self): async def async_media_play(self):
"""Play media. """Play media."""
await self.async_set_play_state(True)
This method must be run in the event loop and returns a coroutine.
"""
return self.async_set_play_state(True)
@cmd @cmd
def async_media_pause(self): async def async_media_pause(self):
"""Pause the media player. """Pause the media player."""
await self.async_set_play_state(False)
This method must be run in the event loop and returns a coroutine.
"""
return self.async_set_play_state(False)
@cmd @cmd
async def async_media_stop(self): async def async_media_stop(self):
@ -735,20 +720,14 @@ class KodiDevice(MediaPlayerDevice):
await self.server.Player.GoTo(players[0]["playerid"], direction) await self.server.Player.GoTo(players[0]["playerid"], direction)
@cmd @cmd
def async_media_next_track(self): async def async_media_next_track(self):
"""Send next track command. """Send next track command."""
await self._goto("next")
This method must be run in the event loop and returns a coroutine.
"""
return self._goto("next")
@cmd @cmd
def async_media_previous_track(self): async def async_media_previous_track(self):
"""Send next track command. """Send next track command."""
await self._goto("previous")
This method must be run in the event loop and returns a coroutine.
"""
return self._goto("previous")
@cmd @cmd
async def async_media_seek(self, position): async def async_media_seek(self, position):
@ -772,21 +751,18 @@ class KodiDevice(MediaPlayerDevice):
await self.server.Player.Seek(players[0]["playerid"], time) await self.server.Player.Seek(players[0]["playerid"], time)
@cmd @cmd
def async_play_media(self, media_type, media_id, **kwargs): async def async_play_media(self, media_type, media_id, **kwargs):
"""Send the play_media command to the media player. """Send the play_media command to the media player."""
This method must be run in the event loop and returns a coroutine.
"""
if media_type == "CHANNEL": if media_type == "CHANNEL":
return self.server.Player.Open({"item": {"channelid": int(media_id)}}) await self.server.Player.Open({"item": {"channelid": int(media_id)}})
if media_type == "PLAYLIST": elif media_type == "PLAYLIST":
return self.server.Player.Open({"item": {"playlistid": int(media_id)}}) await self.server.Player.Open({"item": {"playlistid": int(media_id)}})
if media_type == "DIRECTORY": elif media_type == "DIRECTORY":
return self.server.Player.Open({"item": {"directory": str(media_id)}}) await self.server.Player.Open({"item": {"directory": str(media_id)}})
if media_type == "PLUGIN": elif media_type == "PLUGIN":
return self.server.Player.Open({"item": {"file": str(media_id)}}) await self.server.Player.Open({"item": {"file": str(media_id)}})
else:
return self.server.Player.Open({"item": {"file": str(media_id)}}) await self.server.Player.Open({"item": {"file": str(media_id)}})
async def async_set_shuffle(self, shuffle): async def async_set_shuffle(self, shuffle):
"""Set shuffle mode, for the first player.""" """Set shuffle mode, for the first player."""

View file

@ -5,7 +5,7 @@ import voluptuous as vol
from homeassistant.components.device_automation import toggle_entity from homeassistant.components.device_automation import toggle_entity
from homeassistant.const import CONF_DOMAIN from homeassistant.const import CONF_DOMAIN
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.condition import ConditionCheckerType from homeassistant.helpers.condition import ConditionCheckerType
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
@ -16,6 +16,7 @@ CONDITION_SCHEMA = toggle_entity.CONDITION_SCHEMA.extend(
) )
@callback
def async_condition_from_config( def async_condition_from_config(
config: ConfigType, config_validation: bool config: ConfigType, config_validation: bool
) -> ConditionCheckerType: ) -> ConditionCheckerType:

View file

@ -104,34 +104,25 @@ class LockDevice(Entity):
"""Lock the lock.""" """Lock the lock."""
raise NotImplementedError() raise NotImplementedError()
def async_lock(self, **kwargs): async def async_lock(self, **kwargs):
"""Lock the lock. """Lock the lock."""
await self.hass.async_add_job(ft.partial(self.lock, **kwargs))
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(ft.partial(self.lock, **kwargs))
def unlock(self, **kwargs): def unlock(self, **kwargs):
"""Unlock the lock.""" """Unlock the lock."""
raise NotImplementedError() raise NotImplementedError()
def async_unlock(self, **kwargs): async def async_unlock(self, **kwargs):
"""Unlock the lock. """Unlock the lock."""
await self.hass.async_add_job(ft.partial(self.unlock, **kwargs))
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(ft.partial(self.unlock, **kwargs))
def open(self, **kwargs): def open(self, **kwargs):
"""Open the door latch.""" """Open the door latch."""
raise NotImplementedError() raise NotImplementedError()
def async_open(self, **kwargs): async def async_open(self, **kwargs):
"""Open the door latch. """Open the door latch."""
await self.hass.async_add_job(ft.partial(self.open, **kwargs))
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(ft.partial(self.open, **kwargs))
@property @property
def state_attributes(self): def state_attributes(self):

View file

@ -13,7 +13,7 @@ from homeassistant.const import (
STATE_LOCKED, STATE_LOCKED,
STATE_UNLOCKED, STATE_UNLOCKED,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import condition, config_validation as cv, entity_registry from homeassistant.helpers import condition, config_validation as cv, entity_registry
from homeassistant.helpers.config_validation import DEVICE_CONDITION_BASE_SCHEMA from homeassistant.helpers.config_validation import DEVICE_CONDITION_BASE_SCHEMA
from homeassistant.helpers.typing import ConfigType, TemplateVarsType from homeassistant.helpers.typing import ConfigType, TemplateVarsType
@ -63,6 +63,7 @@ async def async_get_conditions(hass: HomeAssistant, device_id: str) -> List[dict
return conditions return conditions
@callback
def async_condition_from_config( def async_condition_from_config(
config: ConfigType, config_validation: bool config: ConfigType, config_validation: bool
) -> condition.ConditionCheckerType: ) -> condition.ConditionCheckerType:

View file

@ -141,6 +141,7 @@ class Mailbox:
self.hass = hass self.hass = hass
self.name = name self.name = name
@callback
def async_update(self): def async_update(self):
"""Send event notification of updated mailbox.""" """Send event notification of updated mailbox."""
self.hass.bus.async_fire(EVENT) self.hass.bus.async_fire(EVENT)
@ -168,7 +169,7 @@ class Mailbox:
"""Return a list of the current messages.""" """Return a list of the current messages."""
raise NotImplementedError() raise NotImplementedError()
def async_delete(self, msgid): async def async_delete(self, msgid):
"""Delete the specified messages.""" """Delete the specified messages."""
raise NotImplementedError() raise NotImplementedError()
@ -236,7 +237,7 @@ class MailboxDeleteView(MailboxView):
async def delete(self, request, platform, msgid): async def delete(self, request, platform, msgid):
"""Delete items.""" """Delete items."""
mailbox = self.get_mailbox(platform) mailbox = self.get_mailbox(platform)
mailbox.async_delete(msgid) await mailbox.async_delete(msgid)
class MailboxMediaView(MailboxView): class MailboxMediaView(MailboxView):

View file

@ -29,7 +29,6 @@ from homeassistant.const import (
STATE_ALARM_PENDING, STATE_ALARM_PENDING,
STATE_ALARM_TRIGGERED, STATE_ALARM_TRIGGERED,
) )
from homeassistant.core import callback
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.event import async_track_state_change, track_point_in_time from homeassistant.helpers.event import async_track_state_change, track_point_in_time
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
@ -427,17 +426,16 @@ class ManualMQTTAlarm(alarm.AlarmControlPanel):
self.hass, self.entity_id, self._async_state_changed_listener self.hass, self.entity_id, self._async_state_changed_listener
) )
@callback async def message_received(msg):
def message_received(msg):
"""Run when new MQTT message has been received.""" """Run when new MQTT message has been received."""
if msg.payload == self._payload_disarm: if msg.payload == self._payload_disarm:
self.async_alarm_disarm(self._code) await self.async_alarm_disarm(self._code)
elif msg.payload == self._payload_arm_home: elif msg.payload == self._payload_arm_home:
self.async_alarm_arm_home(self._code) await self.async_alarm_arm_home(self._code)
elif msg.payload == self._payload_arm_away: elif msg.payload == self._payload_arm_away:
self.async_alarm_arm_away(self._code) await self.async_alarm_arm_away(self._code)
elif msg.payload == self._payload_arm_night: elif msg.payload == self._payload_arm_night:
self.async_alarm_arm_night(self._code) await self.async_alarm_arm_night(self._code)
else: else:
_LOGGER.warning("Received unexpected payload: %s", msg.payload) _LOGGER.warning("Received unexpected payload: %s", msg.payload)
return return

View file

@ -485,122 +485,89 @@ class MediaPlayerDevice(Entity):
"""Turn the media player on.""" """Turn the media player on."""
raise NotImplementedError() raise NotImplementedError()
def async_turn_on(self): async def async_turn_on(self):
"""Turn the media player on. """Turn the media player on."""
await self.hass.async_add_job(self.turn_on)
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(self.turn_on)
def turn_off(self): def turn_off(self):
"""Turn the media player off.""" """Turn the media player off."""
raise NotImplementedError() raise NotImplementedError()
def async_turn_off(self): async def async_turn_off(self):
"""Turn the media player off. """Turn the media player off."""
await self.hass.async_add_job(self.turn_off)
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(self.turn_off)
def mute_volume(self, mute): def mute_volume(self, mute):
"""Mute the volume.""" """Mute the volume."""
raise NotImplementedError() raise NotImplementedError()
def async_mute_volume(self, mute): async def async_mute_volume(self, mute):
"""Mute the volume. """Mute the volume."""
await self.hass.async_add_job(self.mute_volume, mute)
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(self.mute_volume, mute)
def set_volume_level(self, volume): def set_volume_level(self, volume):
"""Set volume level, range 0..1.""" """Set volume level, range 0..1."""
raise NotImplementedError() raise NotImplementedError()
def async_set_volume_level(self, volume): async def async_set_volume_level(self, volume):
"""Set volume level, range 0..1. """Set volume level, range 0..1."""
await self.hass.async_add_job(self.set_volume_level, volume)
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(self.set_volume_level, volume)
def media_play(self): def media_play(self):
"""Send play command.""" """Send play command."""
raise NotImplementedError() raise NotImplementedError()
def async_media_play(self): async def async_media_play(self):
"""Send play command. """Send play command."""
await self.hass.async_add_job(self.media_play)
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(self.media_play)
def media_pause(self): def media_pause(self):
"""Send pause command.""" """Send pause command."""
raise NotImplementedError() raise NotImplementedError()
def async_media_pause(self): async def async_media_pause(self):
"""Send pause command. """Send pause command."""
await self.hass.async_add_job(self.media_pause)
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(self.media_pause)
def media_stop(self): def media_stop(self):
"""Send stop command.""" """Send stop command."""
raise NotImplementedError() raise NotImplementedError()
def async_media_stop(self): async def async_media_stop(self):
"""Send stop command. """Send stop command."""
await self.hass.async_add_job(self.media_stop)
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(self.media_stop)
def media_previous_track(self): def media_previous_track(self):
"""Send previous track command.""" """Send previous track command."""
raise NotImplementedError() raise NotImplementedError()
def async_media_previous_track(self): async def async_media_previous_track(self):
"""Send previous track command. """Send previous track command."""
await self.hass.async_add_job(self.media_previous_track)
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(self.media_previous_track)
def media_next_track(self): def media_next_track(self):
"""Send next track command.""" """Send next track command."""
raise NotImplementedError() raise NotImplementedError()
def async_media_next_track(self): async def async_media_next_track(self):
"""Send next track command. """Send next track command."""
await self.hass.async_add_job(self.media_next_track)
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(self.media_next_track)
def media_seek(self, position): def media_seek(self, position):
"""Send seek command.""" """Send seek command."""
raise NotImplementedError() raise NotImplementedError()
def async_media_seek(self, position): async def async_media_seek(self, position):
"""Send seek command. """Send seek command."""
await self.hass.async_add_job(self.media_seek, position)
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(self.media_seek, position)
def play_media(self, media_type, media_id, **kwargs): def play_media(self, media_type, media_id, **kwargs):
"""Play a piece of media.""" """Play a piece of media."""
raise NotImplementedError() raise NotImplementedError()
def async_play_media(self, media_type, media_id, **kwargs): async def async_play_media(self, media_type, media_id, **kwargs):
"""Play a piece of media. """Play a piece of media."""
await self.hass.async_add_job(
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(
ft.partial(self.play_media, media_type, media_id, **kwargs) ft.partial(self.play_media, media_type, media_id, **kwargs)
) )
@ -608,45 +575,33 @@ class MediaPlayerDevice(Entity):
"""Select input source.""" """Select input source."""
raise NotImplementedError() raise NotImplementedError()
def async_select_source(self, source): async def async_select_source(self, source):
"""Select input source. """Select input source."""
await self.hass.async_add_job(self.select_source, source)
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(self.select_source, source)
def select_sound_mode(self, sound_mode): def select_sound_mode(self, sound_mode):
"""Select sound mode.""" """Select sound mode."""
raise NotImplementedError() raise NotImplementedError()
def async_select_sound_mode(self, sound_mode): async def async_select_sound_mode(self, sound_mode):
"""Select sound mode. """Select sound mode."""
await self.hass.async_add_job(self.select_sound_mode, sound_mode)
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(self.select_sound_mode, sound_mode)
def clear_playlist(self): def clear_playlist(self):
"""Clear players playlist.""" """Clear players playlist."""
raise NotImplementedError() raise NotImplementedError()
def async_clear_playlist(self): async def async_clear_playlist(self):
"""Clear players playlist. """Clear players playlist."""
await self.hass.async_add_job(self.clear_playlist)
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(self.clear_playlist)
def set_shuffle(self, shuffle): def set_shuffle(self, shuffle):
"""Enable/disable shuffle mode.""" """Enable/disable shuffle mode."""
raise NotImplementedError() raise NotImplementedError()
def async_set_shuffle(self, shuffle): async def async_set_shuffle(self, shuffle):
"""Enable/disable shuffle mode. """Enable/disable shuffle mode."""
await self.hass.async_add_job(self.set_shuffle, shuffle)
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(self.set_shuffle, shuffle)
# No need to overwrite these. # No need to overwrite these.
@property @property
@ -714,18 +669,17 @@ class MediaPlayerDevice(Entity):
"""Boolean if shuffle is supported.""" """Boolean if shuffle is supported."""
return bool(self.supported_features & SUPPORT_SHUFFLE_SET) return bool(self.supported_features & SUPPORT_SHUFFLE_SET)
def async_toggle(self): async def async_toggle(self):
"""Toggle the power on the media player. """Toggle the power on the media player."""
This method must be run in the event loop and returns a coroutine.
"""
if hasattr(self, "toggle"): if hasattr(self, "toggle"):
# pylint: disable=no-member # pylint: disable=no-member
return self.hass.async_add_job(self.toggle) await self.hass.async_add_job(self.toggle)
return
if self.state in [STATE_OFF, STATE_IDLE]: if self.state in [STATE_OFF, STATE_IDLE]:
return self.async_turn_on() await self.async_turn_on()
return self.async_turn_off() else:
await self.async_turn_off()
async def async_volume_up(self): async def async_volume_up(self):
"""Turn volume up for media player. """Turn volume up for media player.
@ -753,18 +707,17 @@ class MediaPlayerDevice(Entity):
if self.volume_level > 0 and self.supported_features & SUPPORT_VOLUME_SET: if self.volume_level > 0 and self.supported_features & SUPPORT_VOLUME_SET:
await self.async_set_volume_level(max(0, self.volume_level - 0.1)) await self.async_set_volume_level(max(0, self.volume_level - 0.1))
def async_media_play_pause(self): async def async_media_play_pause(self):
"""Play or pause the media player. """Play or pause the media player."""
This method must be run in the event loop and returns a coroutine.
"""
if hasattr(self, "media_play_pause"): if hasattr(self, "media_play_pause"):
# pylint: disable=no-member # pylint: disable=no-member
return self.hass.async_add_job(self.media_play_pause) await self.hass.async_add_job(self.media_play_pause)
return
if self.state == STATE_PLAYING: if self.state == STATE_PLAYING:
return self.async_media_pause() await self.async_media_pause()
return self.async_media_play() else:
await self.async_media_play()
@property @property
def entity_picture(self): def entity_picture(self):

View file

@ -16,7 +16,7 @@ from homeassistant.const import (
STATE_PAUSED, STATE_PAUSED,
STATE_PLAYING, STATE_PLAYING,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import condition, config_validation as cv, entity_registry from homeassistant.helpers import condition, config_validation as cv, entity_registry
from homeassistant.helpers.config_validation import DEVICE_CONDITION_BASE_SCHEMA from homeassistant.helpers.config_validation import DEVICE_CONDITION_BASE_SCHEMA
from homeassistant.helpers.typing import ConfigType, TemplateVarsType from homeassistant.helpers.typing import ConfigType, TemplateVarsType
@ -95,6 +95,7 @@ async def async_get_conditions(
return conditions return conditions
@callback
def async_condition_from_config( def async_condition_from_config(
config: ConfigType, config_validation: bool config: ConfigType, config_validation: bool
) -> condition.ConditionCheckerType: ) -> condition.ConditionCheckerType:

View file

@ -774,27 +774,21 @@ class MQTT:
async def async_publish( async def async_publish(
self, topic: str, payload: PublishPayloadType, qos: int, retain: bool self, topic: str, payload: PublishPayloadType, qos: int, retain: bool
) -> None: ) -> None:
"""Publish a MQTT message. """Publish a MQTT message."""
This method must be run in the event loop and returns a coroutine.
"""
async with self._paho_lock: async with self._paho_lock:
_LOGGER.debug("Transmitting message on %s: %s", topic, payload) _LOGGER.debug("Transmitting message on %s: %s", topic, payload)
await self.hass.async_add_job( await self.hass.async_add_executor_job(
self._mqttc.publish, topic, payload, qos, retain self._mqttc.publish, topic, payload, qos, retain
) )
async def async_connect(self) -> str: async def async_connect(self) -> str:
"""Connect to the host. Does process messages yet. """Connect to the host. Does process messages yet."""
This method is a coroutine.
"""
# pylint: disable=import-outside-toplevel # pylint: disable=import-outside-toplevel
import paho.mqtt.client as mqtt import paho.mqtt.client as mqtt
result: int = None result: int = None
try: try:
result = await self.hass.async_add_job( result = await self.hass.async_add_executor_job(
self._mqttc.connect, self.broker, self.port, self.keepalive self._mqttc.connect, self.broker, self.port, self.keepalive
) )
except OSError as err: except OSError as err:
@ -808,19 +802,15 @@ class MQTT:
self._mqttc.loop_start() self._mqttc.loop_start()
return CONNECTION_SUCCESS return CONNECTION_SUCCESS
@callback async def async_disconnect(self):
def async_disconnect(self): """Stop the MQTT client."""
"""Stop the MQTT client.
This method must be run in the event loop and returns a coroutine.
"""
def stop(): def stop():
"""Stop the MQTT client.""" """Stop the MQTT client."""
self._mqttc.disconnect() self._mqttc.disconnect()
self._mqttc.loop_stop() self._mqttc.loop_stop()
return self.hass.async_add_job(stop) await self.hass.async_add_executor_job(stop)
async def async_subscribe( async def async_subscribe(
self, self,
@ -865,7 +855,9 @@ class MQTT:
""" """
async with self._paho_lock: async with self._paho_lock:
result: int = None result: int = None
result, _ = await self.hass.async_add_job(self._mqttc.unsubscribe, topic) result, _ = await self.hass.async_add_executor_job(
self._mqttc.unsubscribe, topic
)
_raise_on_error(result) _raise_on_error(result)
async def _async_perform_subscription(self, topic: str, qos: int) -> None: async def _async_perform_subscription(self, topic: str, qos: int) -> None:
@ -874,7 +866,9 @@ class MQTT:
async with self._paho_lock: async with self._paho_lock:
result: int = None result: int = None
result, _ = await self.hass.async_add_job(self._mqttc.subscribe, topic, qos) result, _ = await self.hass.async_add_executor_job(
self._mqttc.subscribe, topic, qos
)
_raise_on_error(result) _raise_on_error(result)
def _mqtt_on_connect(self, _mqttc, _userdata, _flags, result_code: int) -> None: def _mqtt_on_connect(self, _mqttc, _userdata, _flags, result_code: int) -> None:
@ -1010,10 +1004,7 @@ class MqttAttributes(Entity):
self._attributes_config = config self._attributes_config = config
async def async_added_to_hass(self) -> None: async def async_added_to_hass(self) -> None:
"""Subscribe MQTT events. """Subscribe MQTT events."""
This method must be run in the event loop and returns a coroutine.
"""
await super().async_added_to_hass() await super().async_added_to_hass()
await self._attributes_subscribe_topics() await self._attributes_subscribe_topics()
@ -1080,10 +1071,7 @@ class MqttAvailability(Entity):
self._avail_config = config self._avail_config = config
async def async_added_to_hass(self) -> None: async def async_added_to_hass(self) -> None:
"""Subscribe MQTT events. """Subscribe MQTT events."""
This method must be run in the event loop and returns a coroutine.
"""
await super().async_added_to_hass() await super().async_added_to_hass()
await self._availability_subscribe_topics() await self._availability_subscribe_topics()

View file

@ -1,6 +1,4 @@
"""Camera that loads a picture from an MQTT topic.""" """Camera that loads a picture from an MQTT topic."""
import asyncio
import logging import logging
import voluptuous as vol import voluptuous as vol
@ -130,8 +128,7 @@ class MqttCamera(MqttDiscoveryUpdate, MqttEntityDeviceInfo, Camera):
self.hass, self._sub_state self.hass, self._sub_state
) )
@asyncio.coroutine async def async_camera_image(self):
def async_camera_image(self):
"""Return image response.""" """Return image response."""
return self._last_image return self._last_image

View file

@ -1,5 +1,4 @@
"""Support for a local MQTT broker.""" """Support for a local MQTT broker."""
import asyncio
import logging import logging
import tempfile import tempfile
@ -29,8 +28,7 @@ HBMQTT_CONFIG_SCHEMA = vol.Any(
) )
@asyncio.coroutine async def async_start(hass, password, server_config):
def async_start(hass, password, server_config):
"""Initialize MQTT Server. """Initialize MQTT Server.
This method is a coroutine. This method is a coroutine.
@ -47,17 +45,16 @@ def async_start(hass, password, server_config):
server_config = gen_server_config server_config = gen_server_config
broker = Broker(server_config, hass.loop) broker = Broker(server_config, hass.loop)
yield from broker.start() await broker.start()
except BrokerException: except BrokerException:
_LOGGER.exception("Error initializing MQTT server") _LOGGER.exception("Error initializing MQTT server")
return False, None return False, None
finally: finally:
passwd.close() passwd.close()
@asyncio.coroutine async def async_shutdown_mqtt_server(event):
def async_shutdown_mqtt_server(event):
"""Shut down the MQTT server.""" """Shut down the MQTT server."""
yield from broker.shutdown() await broker.shutdown()
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, async_shutdown_mqtt_server) hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, async_shutdown_mqtt_server)

View file

@ -4,6 +4,7 @@ import logging
from homeassistant.components.binary_sensor import DOMAIN, BinarySensorDevice from homeassistant.components.binary_sensor import DOMAIN, BinarySensorDevice
from homeassistant.components.http import HomeAssistantView from homeassistant.components.http import HomeAssistantView
from homeassistant.const import HTTP_UNPROCESSABLE_ENTITY from homeassistant.const import HTTP_UNPROCESSABLE_ENTITY
from homeassistant.core import callback
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -81,6 +82,7 @@ class MyStromBinarySensor(BinarySensorDevice):
"""Return true if the binary sensor is on.""" """Return true if the binary sensor is on."""
return self._state return self._state
@callback
def async_on_update(self, value): def async_on_update(self, value):
"""Receive an update.""" """Receive an update."""
self._state = value self._state = value

View file

@ -177,10 +177,9 @@ class BaseNotificationService:
""" """
raise NotImplementedError() raise NotImplementedError()
def async_send_message(self, message, **kwargs): async def async_send_message(self, message, **kwargs):
"""Send a message. """Send a message.
kwargs can contain ATTR_TITLE to specify a title. kwargs can contain ATTR_TITLE to specify a title.
This method must be run in the event loop and returns a coroutine.
""" """
return self.hass.async_add_job(partial(self.send_message, message, **kwargs)) await self.hass.async_add_job(partial(self.send_message, message, **kwargs))

View file

@ -59,6 +59,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
server_id = config_entry.data[CONF_SERVER_IDENTIFIER] server_id = config_entry.data[CONF_SERVER_IDENTIFIER]
registry = await async_get_registry(hass) registry = await async_get_registry(hass)
@callback
def async_new_media_players(new_entities): def async_new_media_players(new_entities):
_async_add_entities( _async_add_entities(
hass, registry, config_entry, async_add_entities, server_id, new_entities hass, registry, config_entry, async_add_entities, server_id, new_entities

View file

@ -119,12 +119,9 @@ class RemoteDevice(ToggleEntity):
"""Send a command to a device.""" """Send a command to a device."""
raise NotImplementedError() raise NotImplementedError()
def async_send_command(self, command, **kwargs): async def async_send_command(self, command, **kwargs):
"""Send a command to a device. """Send a command to a device."""
await self.hass.async_add_executor_job(
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_executor_job(
ft.partial(self.send_command, command, **kwargs) ft.partial(self.send_command, command, **kwargs)
) )
@ -132,11 +129,6 @@ class RemoteDevice(ToggleEntity):
"""Learn a command from a device.""" """Learn a command from a device."""
raise NotImplementedError() raise NotImplementedError()
def async_learn_command(self, **kwargs): async def async_learn_command(self, **kwargs):
"""Learn a command from a device. """Learn a command from a device."""
await self.hass.async_add_executor_job(ft.partial(self.learn_command, **kwargs))
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_executor_job(
ft.partial(self.learn_command, **kwargs)
)

View file

@ -16,6 +16,7 @@ from homeassistant.const import (
CONF_USERNAME, CONF_USERNAME,
CONF_VERIFY_SSL, CONF_VERIFY_SSL,
) )
from homeassistant.core import callback
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
@ -55,6 +56,7 @@ CONFIG_SCHEMA = vol.Schema(
async def async_setup(hass, config): async def async_setup(hass, config):
"""Set up the REST command component.""" """Set up the REST command component."""
@callback
def async_register_rest_command(name, command_config): def async_register_rest_command(name, command_config):
"""Create service for rest command.""" """Create service for rest command."""
websession = async_get_clientsession(hass, command_config.get(CONF_VERIFY_SSL)) websession = async_get_clientsession(hass, command_config.get(CONF_VERIFY_SSL))

View file

@ -552,10 +552,10 @@ class SwitchableRflinkDevice(RflinkCommand, RestoreEntity):
elif command in ["off", "alloff"]: elif command in ["off", "alloff"]:
self._state = False self._state = False
def async_turn_on(self, **kwargs): async def async_turn_on(self, **kwargs):
"""Turn the device on.""" """Turn the device on."""
return self._async_handle_command("turn_on") await self._async_handle_command("turn_on")
def async_turn_off(self, **kwargs): async def async_turn_off(self, **kwargs):
"""Turn the device off.""" """Turn the device off."""
return self._async_handle_command("turn_off") await self._async_handle_command("turn_off")

View file

@ -146,17 +146,17 @@ class RflinkCover(RflinkCommand, CoverDevice, RestoreEntity):
"""Return True because covers can be stopped midway.""" """Return True because covers can be stopped midway."""
return True return True
def async_close_cover(self, **kwargs): async def async_close_cover(self, **kwargs):
"""Turn the device close.""" """Turn the device close."""
return self._async_handle_command("close_cover") await self._async_handle_command("close_cover")
def async_open_cover(self, **kwargs): async def async_open_cover(self, **kwargs):
"""Turn the device open.""" """Turn the device open."""
return self._async_handle_command("open_cover") await self._async_handle_command("open_cover")
def async_stop_cover(self, **kwargs): async def async_stop_cover(self, **kwargs):
"""Turn the device stop.""" """Turn the device stop."""
return self._async_handle_command("stop_cover") await self._async_handle_command("stop_cover")
class InvertedRflinkCover(RflinkCover): class InvertedRflinkCover(RflinkCover):

View file

@ -185,22 +185,23 @@ class RussoundZoneDevice(MediaPlayerDevice):
""" """
return float(self._zone_var("volume", 0)) / 50.0 return float(self._zone_var("volume", 0)) / 50.0
def async_turn_off(self): async def async_turn_off(self):
"""Turn off the zone.""" """Turn off the zone."""
return self._russ.send_zone_event(self._zone_id, "ZoneOff") await self._russ.send_zone_event(self._zone_id, "ZoneOff")
def async_turn_on(self): async def async_turn_on(self):
"""Turn on the zone.""" """Turn on the zone."""
return self._russ.send_zone_event(self._zone_id, "ZoneOn") await self._russ.send_zone_event(self._zone_id, "ZoneOn")
def async_set_volume_level(self, volume): async def async_set_volume_level(self, volume):
"""Set the volume level.""" """Set the volume level."""
rvol = int(volume * 50.0) rvol = int(volume * 50.0)
return self._russ.send_zone_event(self._zone_id, "KeyPress", "Volume", rvol) await self._russ.send_zone_event(self._zone_id, "KeyPress", "Volume", rvol)
def async_select_source(self, source): async def async_select_source(self, source):
"""Select the source input for this zone.""" """Select the source input for this zone."""
for source_id, name in self._sources: for source_id, name in self._sources:
if name.lower() != source.lower(): if name.lower() != source.lower():
continue continue
return self._russ.send_zone_event(self._zone_id, "SelectSource", source_id) await self._russ.send_zone_event(self._zone_id, "SelectSource", source_id)
break

View file

@ -225,6 +225,7 @@ class SAJsensor(Entity):
"""Return the date when the sensor was last updated.""" """Return the date when the sensor was last updated."""
return self._sensor.date return self._sensor.date
@callback
def async_update_values(self, unknown_state=False): def async_update_values(self, unknown_state=False):
"""Update this sensor.""" """Update this sensor."""
update = False update = False

View file

@ -94,9 +94,6 @@ class Scene(Entity):
"""Activate scene. Try to get entities into requested state.""" """Activate scene. Try to get entities into requested state."""
raise NotImplementedError() raise NotImplementedError()
def async_activate(self): async def async_activate(self):
"""Activate scene. Try to get entities into requested state. """Activate scene. Try to get entities into requested state."""
await self.hass.async_add_job(self.activate)
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(self.activate)

View file

@ -22,7 +22,7 @@ from homeassistant.const import (
DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_TIMESTAMP, DEVICE_CLASS_TIMESTAMP,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import condition, config_validation as cv from homeassistant.helpers import condition, config_validation as cv
from homeassistant.helpers.entity_registry import ( from homeassistant.helpers.entity_registry import (
async_entries_for_device, async_entries_for_device,
@ -128,6 +128,7 @@ async def async_get_conditions(
return conditions return conditions
@callback
def async_condition_from_config( def async_condition_from_config(
config: ConfigType, config_validation: bool config: ConfigType, config_validation: bool
) -> condition.ConditionCheckerType: ) -> condition.ConditionCheckerType:

View file

@ -153,15 +153,14 @@ class ShoppingData:
self.items = [itm for itm in self.items if not itm["complete"]] self.items = [itm for itm in self.items if not itm["complete"]]
self.hass.async_add_job(self.save) self.hass.async_add_job(self.save)
@asyncio.coroutine async def async_load(self):
def async_load(self):
"""Load items.""" """Load items."""
def load(): def load():
"""Load the items synchronously.""" """Load the items synchronously."""
return load_json(self.hass.config.path(PERSISTENCE), default=[]) return load_json(self.hass.config.path(PERSISTENCE), default=[])
self.items = yield from self.hass.async_add_job(load) self.items = await self.hass.async_add_executor_job(load)
def save(self): def save(self):
"""Save the items.""" """Save the items."""

View file

@ -16,6 +16,7 @@ from homeassistant.const import (
CONF_VERIFY_SSL, CONF_VERIFY_SSL,
EVENT_HOMEASSISTANT_STOP, EVENT_HOMEASSISTANT_STOP,
) )
from homeassistant.core import callback
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
@ -210,6 +211,7 @@ class SMAsensor(Entity):
"""SMA sensors are updated & don't poll.""" """SMA sensors are updated & don't poll."""
return False return False
@callback
def async_update_values(self): def async_update_values(self):
"""Update this sensor.""" """Update this sensor."""
update = False update = False

View file

@ -281,12 +281,9 @@ class SqueezeBoxDevice(MediaPlayerDevice):
return STATE_IDLE return STATE_IDLE
return None return None
def async_query(self, *parameters): async def async_query(self, *parameters):
"""Send a command to the LMS. """Send a command to the LMS."""
return await self._lms.async_query(*parameters, player=self._id)
This method must be run in the event loop and returns a coroutine.
"""
return self._lms.async_query(*parameters, player=self._id)
async def async_update(self): async def async_update(self):
"""Retrieve the current state of the player.""" """Retrieve the current state of the player."""
@ -420,121 +417,85 @@ class SqueezeBoxDevice(MediaPlayerDevice):
"""Flag media player features that are supported.""" """Flag media player features that are supported."""
return SUPPORT_SQUEEZEBOX return SUPPORT_SQUEEZEBOX
def async_turn_off(self): async def async_turn_off(self):
"""Turn off media player. """Turn off media player."""
await self.async_query("power", "0")
This method must be run in the event loop and returns a coroutine. async def async_volume_up(self):
""" """Volume up media player."""
return self.async_query("power", "0") await self.async_query("mixer", "volume", "+5")
def async_volume_up(self): async def async_volume_down(self):
"""Volume up media player. """Volume down media player."""
await self.async_query("mixer", "volume", "-5")
This method must be run in the event loop and returns a coroutine. async def async_set_volume_level(self, volume):
""" """Set volume level, range 0..1."""
return self.async_query("mixer", "volume", "+5")
def async_volume_down(self):
"""Volume down media player.
This method must be run in the event loop and returns a coroutine.
"""
return self.async_query("mixer", "volume", "-5")
def async_set_volume_level(self, volume):
"""Set volume level, range 0..1.
This method must be run in the event loop and returns a coroutine.
"""
volume_percent = str(int(volume * 100)) volume_percent = str(int(volume * 100))
return self.async_query("mixer", "volume", volume_percent) await self.async_query("mixer", "volume", volume_percent)
def async_mute_volume(self, mute): async def async_mute_volume(self, mute):
"""Mute (true) or unmute (false) media player. """Mute (true) or unmute (false) media player."""
This method must be run in the event loop and returns a coroutine.
"""
mute_numeric = "1" if mute else "0" mute_numeric = "1" if mute else "0"
return self.async_query("mixer", "muting", mute_numeric) await self.async_query("mixer", "muting", mute_numeric)
def async_media_play_pause(self): async def async_media_play_pause(self):
"""Send pause command to media player. """Send pause command to media player."""
await self.async_query("pause")
This method must be run in the event loop and returns a coroutine. async def async_media_play(self):
""" """Send play command to media player."""
return self.async_query("pause") await self.async_query("play")
def async_media_play(self): async def async_media_pause(self):
"""Send play command to media player. """Send pause command to media player."""
await self.async_query("pause", "1")
This method must be run in the event loop and returns a coroutine. async def async_media_next_track(self):
""" """Send next track command."""
return self.async_query("play") await self.async_query("playlist", "index", "+1")
def async_media_pause(self): async def async_media_previous_track(self):
"""Send pause command to media player. """Send next track command."""
await self.async_query("playlist", "index", "-1")
This method must be run in the event loop and returns a coroutine. async def async_media_seek(self, position):
""" """Send seek command."""
return self.async_query("pause", "1") await self.async_query("time", position)
def async_media_next_track(self): async def async_turn_on(self):
"""Send next track command. """Turn the media player on."""
await self.async_query("power", "1")
This method must be run in the event loop and returns a coroutine. async def async_play_media(self, media_type, media_id, **kwargs):
"""
return self.async_query("playlist", "index", "+1")
def async_media_previous_track(self):
"""Send next track command.
This method must be run in the event loop and returns a coroutine.
"""
return self.async_query("playlist", "index", "-1")
def async_media_seek(self, position):
"""Send seek command.
This method must be run in the event loop and returns a coroutine.
"""
return self.async_query("time", position)
def async_turn_on(self):
"""Turn the media player on.
This method must be run in the event loop and returns a coroutine.
"""
return self.async_query("power", "1")
def async_play_media(self, media_type, media_id, **kwargs):
""" """
Send the play_media command to the media player. Send the play_media command to the media player.
If ATTR_MEDIA_ENQUEUE is True, add `media_id` to the current playlist. If ATTR_MEDIA_ENQUEUE is True, add `media_id` to the current playlist.
This method must be run in the event loop and returns a coroutine.
""" """
if kwargs.get(ATTR_MEDIA_ENQUEUE): if kwargs.get(ATTR_MEDIA_ENQUEUE):
return self._add_uri_to_playlist(media_id) await self._add_uri_to_playlist(media_id)
return
return self._play_uri(media_id) await self._play_uri(media_id)
def _play_uri(self, media_id): async def _play_uri(self, media_id):
"""Replace the current play list with the uri.""" """Replace the current play list with the uri."""
return self.async_query("playlist", "play", media_id) await self.async_query("playlist", "play", media_id)
def _add_uri_to_playlist(self, media_id): async def _add_uri_to_playlist(self, media_id):
"""Add an item to the existing playlist.""" """Add an item to the existing playlist."""
return self.async_query("playlist", "add", media_id) await self.async_query("playlist", "add", media_id)
def async_set_shuffle(self, shuffle): async def async_set_shuffle(self, shuffle):
"""Enable/disable shuffle mode.""" """Enable/disable shuffle mode."""
return self.async_query("playlist", "shuffle", int(shuffle)) await self.async_query("playlist", "shuffle", int(shuffle))
def async_clear_playlist(self): async def async_clear_playlist(self):
"""Send the media player the command for clear playlist.""" """Send the media player the command for clear playlist."""
return self.async_query("playlist", "clear") await self.async_query("playlist", "clear")
def async_call_method(self, command, parameters=None): async def async_call_method(self, command, parameters=None):
""" """
Call Squeezebox JSON/RPC method. Call Squeezebox JSON/RPC method.
@ -545,4 +506,4 @@ class SqueezeBoxDevice(MediaPlayerDevice):
if parameters: if parameters:
for parameter in parameters: for parameter in parameters:
all_params.append(parameter) all_params.append(parameter)
return self.async_query(*all_params) await self.async_query(*all_params)

View file

@ -5,7 +5,7 @@ import voluptuous as vol
from homeassistant.components.device_automation import toggle_entity from homeassistant.components.device_automation import toggle_entity
from homeassistant.const import CONF_DOMAIN from homeassistant.const import CONF_DOMAIN
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.condition import ConditionCheckerType from homeassistant.helpers.condition import ConditionCheckerType
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
@ -16,6 +16,7 @@ CONDITION_SCHEMA = toggle_entity.CONDITION_SCHEMA.extend(
) )
@callback
def async_condition_from_config( def async_condition_from_config(
config: ConfigType, config_validation: bool config: ConfigType, config_validation: bool
) -> ConditionCheckerType: ) -> ConditionCheckerType:

View file

@ -124,17 +124,11 @@ class SwitcherControl(SwitchDevice):
self.async_schedule_update_ha_state() self.async_schedule_update_ha_state()
async def async_turn_on(self, **kwargs: Dict) -> None: async def async_turn_on(self, **kwargs: Dict) -> None:
"""Turn the entity on. """Turn the entity on."""
This method must be run in the event loop and returns a coroutine.
"""
await self._control_device(True) await self._control_device(True)
async def async_turn_off(self, **kwargs: Dict) -> None: async def async_turn_off(self, **kwargs: Dict) -> None:
"""Turn the entity off. """Turn the entity off."""
This method must be run in the event loop and returns a coroutine.
"""
await self._control_device(False) await self._control_device(False)
async def _control_device(self, send_on: bool) -> None: async def _control_device(self, send_on: bool) -> None:

View file

@ -501,14 +501,12 @@ class Provider:
"""Load tts audio file from provider.""" """Load tts audio file from provider."""
raise NotImplementedError() raise NotImplementedError()
def async_get_tts_audio(self, message, language, options=None): async def async_get_tts_audio(self, message, language, options=None):
"""Load tts audio file from provider. """Load tts audio file from provider.
Return a tuple of file extension and data as bytes. Return a tuple of file extension and data as bytes.
This method must be run in the event loop and returns a coroutine.
""" """
return self.hass.async_add_job( return await self.hass.async_add_job(
ft.partial(self.get_tts_audio, message, language, options=options) ft.partial(self.get_tts_audio, message, language, options=options)
) )

View file

@ -137,10 +137,7 @@ class UniversalMediaPlayer(MediaPlayerDevice):
self._state_template.hass = hass self._state_template.hass = hass
async def async_added_to_hass(self): async def async_added_to_hass(self):
"""Subscribe to children and template state changes. """Subscribe to children and template state changes."""
This method must be run in the event loop and returns a coroutine.
"""
@callback @callback
def async_on_dependency_update(*_): def async_on_dependency_update(*_):
@ -416,132 +413,79 @@ class UniversalMediaPlayer(MediaPlayerDevice):
"""When was the position of the current playing media valid.""" """When was the position of the current playing media valid."""
return self._child_attr(ATTR_MEDIA_POSITION_UPDATED_AT) return self._child_attr(ATTR_MEDIA_POSITION_UPDATED_AT)
def async_turn_on(self): async def async_turn_on(self):
"""Turn the media player on. """Turn the media player on."""
await self._async_call_service(SERVICE_TURN_ON, allow_override=True)
This method must be run in the event loop and returns a coroutine. async def async_turn_off(self):
""" """Turn the media player off."""
return self._async_call_service(SERVICE_TURN_ON, allow_override=True) await self._async_call_service(SERVICE_TURN_OFF, allow_override=True)
def async_turn_off(self): async def async_mute_volume(self, mute):
"""Turn the media player off. """Mute the volume."""
This method must be run in the event loop and returns a coroutine.
"""
return self._async_call_service(SERVICE_TURN_OFF, allow_override=True)
def async_mute_volume(self, mute):
"""Mute the volume.
This method must be run in the event loop and returns a coroutine.
"""
data = {ATTR_MEDIA_VOLUME_MUTED: mute} data = {ATTR_MEDIA_VOLUME_MUTED: mute}
return self._async_call_service(SERVICE_VOLUME_MUTE, data, allow_override=True) await self._async_call_service(SERVICE_VOLUME_MUTE, data, allow_override=True)
def async_set_volume_level(self, volume): async def async_set_volume_level(self, volume):
"""Set volume level, range 0..1. """Set volume level, range 0..1."""
This method must be run in the event loop and returns a coroutine.
"""
data = {ATTR_MEDIA_VOLUME_LEVEL: volume} data = {ATTR_MEDIA_VOLUME_LEVEL: volume}
return self._async_call_service(SERVICE_VOLUME_SET, data, allow_override=True) await self._async_call_service(SERVICE_VOLUME_SET, data, allow_override=True)
def async_media_play(self): async def async_media_play(self):
"""Send play command. """Send play command."""
await self._async_call_service(SERVICE_MEDIA_PLAY)
This method must be run in the event loop and returns a coroutine. async def async_media_pause(self):
""" """Send pause command."""
return self._async_call_service(SERVICE_MEDIA_PLAY) await self._async_call_service(SERVICE_MEDIA_PAUSE)
def async_media_pause(self): async def async_media_stop(self):
"""Send pause command. """Send stop command."""
await self._async_call_service(SERVICE_MEDIA_STOP)
This method must be run in the event loop and returns a coroutine. async def async_media_previous_track(self):
""" """Send previous track command."""
return self._async_call_service(SERVICE_MEDIA_PAUSE) await self._async_call_service(SERVICE_MEDIA_PREVIOUS_TRACK)
def async_media_stop(self): async def async_media_next_track(self):
"""Send stop command. """Send next track command."""
await self._async_call_service(SERVICE_MEDIA_NEXT_TRACK)
This method must be run in the event loop and returns a coroutine. async def async_media_seek(self, position):
""" """Send seek command."""
return self._async_call_service(SERVICE_MEDIA_STOP)
def async_media_previous_track(self):
"""Send previous track command.
This method must be run in the event loop and returns a coroutine.
"""
return self._async_call_service(SERVICE_MEDIA_PREVIOUS_TRACK)
def async_media_next_track(self):
"""Send next track command.
This method must be run in the event loop and returns a coroutine.
"""
return self._async_call_service(SERVICE_MEDIA_NEXT_TRACK)
def async_media_seek(self, position):
"""Send seek command.
This method must be run in the event loop and returns a coroutine.
"""
data = {ATTR_MEDIA_SEEK_POSITION: position} data = {ATTR_MEDIA_SEEK_POSITION: position}
return self._async_call_service(SERVICE_MEDIA_SEEK, data) await self._async_call_service(SERVICE_MEDIA_SEEK, data)
def async_play_media(self, media_type, media_id, **kwargs): async def async_play_media(self, media_type, media_id, **kwargs):
"""Play a piece of media. """Play a piece of media."""
This method must be run in the event loop and returns a coroutine.
"""
data = {ATTR_MEDIA_CONTENT_TYPE: media_type, ATTR_MEDIA_CONTENT_ID: media_id} data = {ATTR_MEDIA_CONTENT_TYPE: media_type, ATTR_MEDIA_CONTENT_ID: media_id}
return self._async_call_service(SERVICE_PLAY_MEDIA, data) await self._async_call_service(SERVICE_PLAY_MEDIA, data)
def async_volume_up(self): async def async_volume_up(self):
"""Turn volume up for media player. """Turn volume up for media player."""
await self._async_call_service(SERVICE_VOLUME_UP, allow_override=True)
This method must be run in the event loop and returns a coroutine. async def async_volume_down(self):
""" """Turn volume down for media player."""
return self._async_call_service(SERVICE_VOLUME_UP, allow_override=True) await self._async_call_service(SERVICE_VOLUME_DOWN, allow_override=True)
def async_volume_down(self): async def async_media_play_pause(self):
"""Turn volume down for media player. """Play or pause the media player."""
await self._async_call_service(SERVICE_MEDIA_PLAY_PAUSE)
This method must be run in the event loop and returns a coroutine. async def async_select_source(self, source):
""" """Set the input source."""
return self._async_call_service(SERVICE_VOLUME_DOWN, allow_override=True)
def async_media_play_pause(self):
"""Play or pause the media player.
This method must be run in the event loop and returns a coroutine.
"""
return self._async_call_service(SERVICE_MEDIA_PLAY_PAUSE)
def async_select_source(self, source):
"""Set the input source.
This method must be run in the event loop and returns a coroutine.
"""
data = {ATTR_INPUT_SOURCE: source} data = {ATTR_INPUT_SOURCE: source}
return self._async_call_service( await self._async_call_service(SERVICE_SELECT_SOURCE, data, allow_override=True)
SERVICE_SELECT_SOURCE, data, allow_override=True
)
def async_clear_playlist(self): async def async_clear_playlist(self):
"""Clear players playlist. """Clear players playlist."""
await self._async_call_service(SERVICE_CLEAR_PLAYLIST)
This method must be run in the event loop and returns a coroutine. async def async_set_shuffle(self, shuffle):
""" """Enable/disable shuffling."""
return self._async_call_service(SERVICE_CLEAR_PLAYLIST)
def async_set_shuffle(self, shuffle):
"""Enable/disable shuffling.
This method must be run in the event loop and returns a coroutine.
"""
data = {ATTR_MEDIA_SHUFFLE: shuffle} data = {ATTR_MEDIA_SHUFFLE: shuffle}
return self._async_call_service(SERVICE_SHUFFLE_SET, data, allow_override=True) await self._async_call_service(SERVICE_SHUFFLE_SET, data, allow_override=True)
async def async_update(self): async def async_update(self):
"""Update state in HA.""" """Update state in HA."""

View file

@ -11,7 +11,7 @@ from homeassistant.const import (
CONF_ENTITY_ID, CONF_ENTITY_ID,
CONF_TYPE, CONF_TYPE,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import condition, config_validation as cv, entity_registry from homeassistant.helpers import condition, config_validation as cv, entity_registry
from homeassistant.helpers.config_validation import DEVICE_CONDITION_BASE_SCHEMA from homeassistant.helpers.config_validation import DEVICE_CONDITION_BASE_SCHEMA
from homeassistant.helpers.typing import ConfigType, TemplateVarsType from homeassistant.helpers.typing import ConfigType, TemplateVarsType
@ -62,6 +62,7 @@ async def async_get_conditions(
return conditions return conditions
@callback
def async_condition_from_config( def async_condition_from_config(
config: ConfigType, config_validation: bool config: ConfigType, config_validation: bool
) -> condition.ConditionCheckerType: ) -> condition.ConditionCheckerType:

View file

@ -251,73 +251,75 @@ class Volumio(MediaPlayerDevice):
"""Flag of media commands that are supported.""" """Flag of media commands that are supported."""
return SUPPORT_VOLUMIO return SUPPORT_VOLUMIO
def async_media_next_track(self): async def async_media_next_track(self):
"""Send media_next command to media player.""" """Send media_next command to media player."""
return self.send_volumio_msg("commands", params={"cmd": "next"}) await self.send_volumio_msg("commands", params={"cmd": "next"})
def async_media_previous_track(self): async def async_media_previous_track(self):
"""Send media_previous command to media player.""" """Send media_previous command to media player."""
return self.send_volumio_msg("commands", params={"cmd": "prev"}) await self.send_volumio_msg("commands", params={"cmd": "prev"})
def async_media_play(self): async def async_media_play(self):
"""Send media_play command to media player.""" """Send media_play command to media player."""
return self.send_volumio_msg("commands", params={"cmd": "play"}) await self.send_volumio_msg("commands", params={"cmd": "play"})
def async_media_pause(self): async def async_media_pause(self):
"""Send media_pause command to media player.""" """Send media_pause command to media player."""
if self._state["trackType"] == "webradio": if self._state["trackType"] == "webradio":
return self.send_volumio_msg("commands", params={"cmd": "stop"}) await self.send_volumio_msg("commands", params={"cmd": "stop"})
return self.send_volumio_msg("commands", params={"cmd": "pause"}) else:
await self.send_volumio_msg("commands", params={"cmd": "pause"})
def async_set_volume_level(self, volume): async def async_set_volume_level(self, volume):
"""Send volume_up command to media player.""" """Send volume_up command to media player."""
return self.send_volumio_msg( await self.send_volumio_msg(
"commands", params={"cmd": "volume", "volume": int(volume * 100)} "commands", params={"cmd": "volume", "volume": int(volume * 100)}
) )
def async_volume_up(self): async def async_volume_up(self):
"""Service to send the Volumio the command for volume up.""" """Service to send the Volumio the command for volume up."""
return self.send_volumio_msg( await self.send_volumio_msg(
"commands", params={"cmd": "volume", "volume": "plus"} "commands", params={"cmd": "volume", "volume": "plus"}
) )
def async_volume_down(self): async def async_volume_down(self):
"""Service to send the Volumio the command for volume down.""" """Service to send the Volumio the command for volume down."""
return self.send_volumio_msg( await self.send_volumio_msg(
"commands", params={"cmd": "volume", "volume": "minus"} "commands", params={"cmd": "volume", "volume": "minus"}
) )
def async_mute_volume(self, mute): async def async_mute_volume(self, mute):
"""Send mute command to media player.""" """Send mute command to media player."""
mutecmd = "mute" if mute else "unmute" mutecmd = "mute" if mute else "unmute"
if mute: if mute:
# mute is implemented as 0 volume, do save last volume level # mute is implemented as 0 volume, do save last volume level
self._lastvol = self._state["volume"] self._lastvol = self._state["volume"]
return self.send_volumio_msg( await self.send_volumio_msg(
"commands", params={"cmd": "volume", "volume": mutecmd} "commands", params={"cmd": "volume", "volume": mutecmd}
) )
return
return self.send_volumio_msg( await self.send_volumio_msg(
"commands", params={"cmd": "volume", "volume": self._lastvol} "commands", params={"cmd": "volume", "volume": self._lastvol}
) )
def async_set_shuffle(self, shuffle): async def async_set_shuffle(self, shuffle):
"""Enable/disable shuffle mode.""" """Enable/disable shuffle mode."""
return self.send_volumio_msg( await self.send_volumio_msg(
"commands", params={"cmd": "random", "value": str(shuffle).lower()} "commands", params={"cmd": "random", "value": str(shuffle).lower()}
) )
def async_select_source(self, source): async def async_select_source(self, source):
"""Choose a different available playlist and play it.""" """Choose a different available playlist and play it."""
self._currentplaylist = source self._currentplaylist = source
return self.send_volumio_msg( await self.send_volumio_msg(
"commands", params={"cmd": "playplaylist", "name": source} "commands", params={"cmd": "playplaylist", "name": source}
) )
def async_clear_playlist(self): async def async_clear_playlist(self):
"""Clear players playlist.""" """Clear players playlist."""
self._currentplaylist = None self._currentplaylist = None
return self.send_volumio_msg("commands", params={"cmd": "clearQueue"}) await self.send_volumio_msg("commands", params={"cmd": "clearQueue"})
@Throttle(PLAYLIST_UPDATE_INTERVAL) @Throttle(PLAYLIST_UPDATE_INTERVAL)
async def _async_update_playlists(self, **kwargs): async def _async_update_playlists(self, **kwargs):

View file

@ -780,6 +780,7 @@ async def async_binding_operation(zha_gateway, source_ieee, target_ieee, operati
zdo.debug(fmt, *(log_msg[2] + (outcome,))) zdo.debug(fmt, *(log_msg[2] + (outcome,)))
@callback
def async_load_api(hass): def async_load_api(hass):
"""Set up the web socket API.""" """Set up the web socket API."""
zha_gateway = hass.data[DATA_ZHA][DATA_ZHA_GATEWAY] zha_gateway = hass.data[DATA_ZHA][DATA_ZHA_GATEWAY]
@ -1058,6 +1059,7 @@ def async_load_api(hass):
websocket_api.async_register_command(hass, websocket_unbind_devices) websocket_api.async_register_command(hass, websocket_unbind_devices)
@callback
def async_unload_api(hass): def async_unload_api(hass):
"""Unload the ZHA API.""" """Unload the ZHA API."""
hass.services.async_remove(DOMAIN, SERVICE_PERMIT) hass.services.async_remove(DOMAIN, SERVICE_PERMIT)

View file

@ -125,6 +125,7 @@ class BinarySensor(ZhaEntity, BinarySensorDevice):
"""Return device class from component DEVICE_CLASSES.""" """Return device class from component DEVICE_CLASSES."""
return self._device_class return self._device_class
@callback
def async_set_state(self, state): def async_set_state(self, state):
"""Set the state.""" """Set the state."""
self._state = bool(state) self._state = bool(state)

View file

@ -273,6 +273,7 @@ class ZHAGateway:
"""Return Group for given group id.""" """Return Group for given group id."""
return self.groups.get(group_id) return self.groups.get(group_id)
@callback
def async_get_group_by_name(self, group_name): def async_get_group_by_name(self, group_name):
"""Get ZHA group by name.""" """Get ZHA group by name."""
for group in self.groups.values(): for group in self.groups.values():

View file

@ -113,6 +113,7 @@ class ZhaCover(ZhaEntity, CoverDevice):
""" """
return self._current_position return self._current_position
@callback
def async_set_position(self, pos): def async_set_position(self, pos):
"""Handle position update from channel.""" """Handle position update from channel."""
_LOGGER.debug("setting position: %s", pos) _LOGGER.debug("setting position: %s", pos)
@ -123,6 +124,7 @@ class ZhaCover(ZhaEntity, CoverDevice):
self._state = STATE_OPEN self._state = STATE_OPEN
self.async_schedule_update_ha_state() self.async_schedule_update_ha_state()
@callback
def async_set_state(self, state): def async_set_state(self, state):
"""Handle state update from channel.""" """Handle state update from channel."""
_LOGGER.debug("state=%s", state) _LOGGER.debug("state=%s", state)

View file

@ -99,16 +99,19 @@ class ZhaEntity(RestoreEntity, LogMixin, entity.Entity):
"""Return entity availability.""" """Return entity availability."""
return self._available return self._available
@callback
def async_set_available(self, available): def async_set_available(self, available):
"""Set entity availability.""" """Set entity availability."""
self._available = available self._available = available
self.async_schedule_update_ha_state() self.async_schedule_update_ha_state()
@callback
def async_update_state_attribute(self, key, value): def async_update_state_attribute(self, key, value):
"""Update a single device state attribute.""" """Update a single device state attribute."""
self._device_state_attributes.update({key: value}) self._device_state_attributes.update({key: value})
self.async_schedule_update_ha_state() self.async_schedule_update_ha_state()
@callback
def async_set_state(self, state): def async_set_state(self, state):
"""Set the entity state.""" """Set the entity state."""
pass pass

View file

@ -136,6 +136,7 @@ class ZhaFan(ZhaEntity, FanEntity):
"""Return state attributes.""" """Return state attributes."""
return self.state_attributes return self.state_attributes
@callback
def async_set_state(self, state): def async_set_state(self, state):
"""Handle state update from channel.""" """Handle state update from channel."""
self._state = VALUE_TO_SPEED.get(state, self._state) self._state = VALUE_TO_SPEED.get(state, self._state)

View file

@ -170,6 +170,7 @@ class Light(ZhaEntity, light.Light):
"""Flag supported features.""" """Flag supported features."""
return self._supported_features return self._supported_features
@callback
def async_set_state(self, state): def async_set_state(self, state):
"""Set the state.""" """Set the state."""
self._state = bool(state) self._state = bool(state)

View file

@ -125,6 +125,7 @@ class ZhaDoorLock(ZhaEntity, LockDevice):
await super().async_update() await super().async_update()
await self.async_get_state() await self.async_get_state()
@callback
def async_set_state(self, state): def async_set_state(self, state):
"""Handle state update from channel.""" """Handle state update from channel."""
self._state = VALUE_TO_STATE.get(state, self._state) self._state = VALUE_TO_STATE.get(state, self._state)

View file

@ -149,6 +149,7 @@ class Sensor(ZhaEntity):
return None return None
return self._state return self._state
@callback
def async_set_state(self, state): def async_set_state(self, state):
"""Handle state update from channel.""" """Handle state update from channel."""
if state is not None: if state is not None:
@ -202,6 +203,7 @@ class Battery(Sensor):
state_attrs["battery_quantity"] = battery_quantity state_attrs["battery_quantity"] = battery_quantity
return state_attrs return state_attrs
@callback
def async_update_state_attribute(self, key, value): def async_update_state_attribute(self, key, value):
"""Update a single device state attribute.""" """Update a single device state attribute."""
if key == "battery_voltage": if key == "battery_voltage":

View file

@ -93,6 +93,7 @@ class Switch(ZhaEntity, SwitchDevice):
self._state = False self._state = False
self.async_schedule_update_ha_state() self.async_schedule_update_ha_state()
@callback
def async_set_state(self, state): def async_set_state(self, state):
"""Handle state update from channel.""" """Handle state update from channel."""
self._state = bool(state) self._state = bool(state)

View file

@ -260,6 +260,7 @@ class DeviceRegistry:
return new return new
@callback
def async_remove_device(self, device_id: str) -> None: def async_remove_device(self, device_id: str) -> None:
"""Remove a device from the device registry.""" """Remove a device from the device registry."""
del self.devices[device_id] del self.devices[device_id]

View file

@ -596,23 +596,17 @@ class ToggleEntity(Entity):
"""Turn the entity on.""" """Turn the entity on."""
raise NotImplementedError() raise NotImplementedError()
def async_turn_on(self, **kwargs): async def async_turn_on(self, **kwargs):
"""Turn the entity on. """Turn the entity on."""
await self.hass.async_add_job(ft.partial(self.turn_on, **kwargs))
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(ft.partial(self.turn_on, **kwargs))
def turn_off(self, **kwargs: Any) -> None: def turn_off(self, **kwargs: Any) -> None:
"""Turn the entity off.""" """Turn the entity off."""
raise NotImplementedError() raise NotImplementedError()
def async_turn_off(self, **kwargs): async def async_turn_off(self, **kwargs):
"""Turn the entity off. """Turn the entity off."""
await self.hass.async_add_job(ft.partial(self.turn_off, **kwargs))
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(ft.partial(self.turn_off, **kwargs))
def toggle(self, **kwargs: Any) -> None: def toggle(self, **kwargs: Any) -> None:
"""Toggle the entity.""" """Toggle the entity."""
@ -621,11 +615,9 @@ class ToggleEntity(Entity):
else: else:
self.turn_on(**kwargs) self.turn_on(**kwargs)
def async_toggle(self, **kwargs): async def async_toggle(self, **kwargs):
"""Toggle the entity. """Toggle the entity."""
This method must be run in the event loop and returns a coroutine.
"""
if self.is_on: if self.is_on:
return self.async_turn_off(**kwargs) await self.async_turn_off(**kwargs)
return self.async_turn_on(**kwargs) else:
await self.async_turn_on(**kwargs)

View file

@ -115,6 +115,7 @@ class RestoreStateData:
self.last_states: Dict[str, StoredState] = {} self.last_states: Dict[str, StoredState] = {}
self.entity_ids: Set[str] = set() self.entity_ids: Set[str] = set()
@callback
def async_get_stored_states(self) -> List[StoredState]: def async_get_stored_states(self) -> List[StoredState]:
"""Get the set of states which should be stored. """Get the set of states which should be stored.

View file

@ -214,6 +214,7 @@ class Script:
"""Stop running script.""" """Stop running script."""
run_callback_threadsafe(self.hass.loop, self.async_stop).result() run_callback_threadsafe(self.hass.loop, self.async_stop).result()
@callback
def async_stop(self) -> None: def async_stop(self) -> None:
"""Stop running script.""" """Stop running script."""
if self._cur == -1: if self._cur == -1:

View file

@ -13,7 +13,7 @@ from homeassistant.const import (
STATE_OFF, STATE_OFF,
STATE_ON, STATE_ON,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import condition, config_validation as cv, entity_registry from homeassistant.helpers import condition, config_validation as cv, entity_registry
from homeassistant.helpers.config_validation import DEVICE_CONDITION_BASE_SCHEMA from homeassistant.helpers.config_validation import DEVICE_CONDITION_BASE_SCHEMA
from homeassistant.helpers.typing import ConfigType, TemplateVarsType from homeassistant.helpers.typing import ConfigType, TemplateVarsType
@ -67,6 +67,7 @@ async def async_get_conditions(
return conditions return conditions
@callback
def async_condition_from_config( def async_condition_from_config(
config: ConfigType, config_validation: bool config: ConfigType, config_validation: bool
) -> condition.ConditionCheckerType: ) -> condition.ConditionCheckerType:
@ -78,6 +79,7 @@ def async_condition_from_config(
else: else:
state = STATE_OFF state = STATE_OFF
@callback
def test_is_state(hass: HomeAssistant, variables: TemplateVarsType) -> bool: def test_is_state(hass: HomeAssistant, variables: TemplateVarsType) -> bool:
"""Test if an entity is a certain state.""" """Test if an entity is a certain state."""
return condition.state(hass, config[ATTR_ENTITY_ID], state) return condition.state(hass, config[ATTR_ENTITY_ID], state)

View file

@ -1,5 +1,7 @@
"""The tests for the MQTT component embedded server.""" """The tests for the MQTT component embedded server."""
from unittest.mock import MagicMock, Mock, patch from unittest.mock import MagicMock, Mock
from asynctest import CoroutineMock, patch
import homeassistant.components.mqtt as mqtt import homeassistant.components.mqtt as mqtt
from homeassistant.const import CONF_PASSWORD from homeassistant.const import CONF_PASSWORD
@ -21,7 +23,7 @@ class TestMQTT:
@patch("passlib.apps.custom_app_context", Mock(return_value="")) @patch("passlib.apps.custom_app_context", Mock(return_value=""))
@patch("tempfile.NamedTemporaryFile", Mock(return_value=MagicMock())) @patch("tempfile.NamedTemporaryFile", Mock(return_value=MagicMock()))
@patch("hbmqtt.broker.Broker", Mock(return_value=MagicMock())) @patch("hbmqtt.broker.Broker", Mock(return_value=MagicMock(start=CoroutineMock())))
@patch("hbmqtt.broker.Broker.start", Mock(return_value=mock_coro())) @patch("hbmqtt.broker.Broker.start", Mock(return_value=mock_coro()))
@patch("homeassistant.components.mqtt.MQTT") @patch("homeassistant.components.mqtt.MQTT")
def test_creating_config_with_pass_and_no_http_pass(self, mock_mqtt): def test_creating_config_with_pass_and_no_http_pass(self, mock_mqtt):
@ -43,7 +45,7 @@ class TestMQTT:
@patch("passlib.apps.custom_app_context", Mock(return_value="")) @patch("passlib.apps.custom_app_context", Mock(return_value=""))
@patch("tempfile.NamedTemporaryFile", Mock(return_value=MagicMock())) @patch("tempfile.NamedTemporaryFile", Mock(return_value=MagicMock()))
@patch("hbmqtt.broker.Broker", Mock(return_value=MagicMock())) @patch("hbmqtt.broker.Broker", Mock(return_value=MagicMock(start=CoroutineMock())))
@patch("hbmqtt.broker.Broker.start", Mock(return_value=mock_coro())) @patch("hbmqtt.broker.Broker.start", Mock(return_value=mock_coro()))
@patch("homeassistant.components.mqtt.MQTT") @patch("homeassistant.components.mqtt.MQTT")
def test_creating_config_with_pass_and_http_pass(self, mock_mqtt): def test_creating_config_with_pass_and_http_pass(self, mock_mqtt):