Skip to content

Commit

Permalink
Reproduce states by letting each component opt in on handling state r…
Browse files Browse the repository at this point in the history
…ecovery itself (home-assistant#18700)

* Move group to it's own setup

* Let each component to handle restore of state

* Move constants for climate into const.py

For now import all into __init__.py to keep backword compat

* Move media plyaer constants to const.py file

For now import all constants into __init__.py to keep
backword compatibility

* Move media player to it's own file

* Move climate to it's own file

* Remove ecobee service from common components

BREAKING CHANGE

* Add tests for climate

* Add test for media_player

* Make sure we clone timestamps of state

* Add tests for groups

* Remove old tests for media player, it's handled by other tests

* Add tests for calls to component functions

* Add docstring for climate const

* Add docstring for media_player const

* Explicitly import constants in climate

* Explicitly import constants in media_player

* Add period to climate const

* Add period to media_player const

* Fix some lint errors in climate

* Fix some lint errors in media_player

* Fix lint warnings on climate tests

* Fix lint warnings on group tests

* Fix lint warnings on media_player tests

* Fix lint warnings on state tests

* Adjust indent for state tests
  • Loading branch information
elupus authored and balloob committed Feb 6, 2019
1 parent c76a61a commit 3bb5caa
Show file tree
Hide file tree
Showing 13 changed files with 846 additions and 228 deletions.
63 changes: 32 additions & 31 deletions homeassistant/components/climate/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,46 @@
STATE_ON, STATE_OFF, TEMP_CELSIUS, PRECISION_WHOLE,
PRECISION_TENTHS)

from .const import (
ATTR_AUX_HEAT,
ATTR_AWAY_MODE,
ATTR_CURRENT_HUMIDITY,
ATTR_CURRENT_TEMPERATURE,
ATTR_FAN_LIST,
ATTR_FAN_MODE,
ATTR_HOLD_MODE,
ATTR_HUMIDITY,
ATTR_MAX_HUMIDITY,
ATTR_MAX_TEMP,
ATTR_MIN_HUMIDITY,
ATTR_MIN_TEMP,
ATTR_OPERATION_LIST,
ATTR_OPERATION_MODE,
ATTR_SWING_LIST,
ATTR_SWING_MODE,
ATTR_TARGET_TEMP_HIGH,
ATTR_TARGET_TEMP_LOW,
ATTR_TARGET_TEMP_STEP,
DOMAIN,
SERVICE_SET_AUX_HEAT,
SERVICE_SET_AWAY_MODE,
SERVICE_SET_FAN_MODE,
SERVICE_SET_HOLD_MODE,
SERVICE_SET_HUMIDITY,
SERVICE_SET_OPERATION_MODE,
SERVICE_SET_SWING_MODE,
SERVICE_SET_TEMPERATURE,
)
from .reproduce_state import async_reproduce_states # noqa

DEFAULT_MIN_TEMP = 7
DEFAULT_MAX_TEMP = 35
DEFAULT_MIN_HUMITIDY = 30
DEFAULT_MAX_HUMIDITY = 99

DOMAIN = 'climate'

ENTITY_ID_FORMAT = DOMAIN + '.{}'
SCAN_INTERVAL = timedelta(seconds=60)

SERVICE_SET_AWAY_MODE = 'set_away_mode'
SERVICE_SET_AUX_HEAT = 'set_aux_heat'
SERVICE_SET_TEMPERATURE = 'set_temperature'
SERVICE_SET_FAN_MODE = 'set_fan_mode'
SERVICE_SET_HOLD_MODE = 'set_hold_mode'
SERVICE_SET_OPERATION_MODE = 'set_operation_mode'
SERVICE_SET_SWING_MODE = 'set_swing_mode'
SERVICE_SET_HUMIDITY = 'set_humidity'

STATE_HEAT = 'heat'
STATE_COOL = 'cool'
STATE_IDLE = 'idle'
Expand All @@ -64,26 +85,6 @@
SUPPORT_AUX_HEAT = 2048
SUPPORT_ON_OFF = 4096

ATTR_CURRENT_TEMPERATURE = 'current_temperature'
ATTR_MAX_TEMP = 'max_temp'
ATTR_MIN_TEMP = 'min_temp'
ATTR_TARGET_TEMP_HIGH = 'target_temp_high'
ATTR_TARGET_TEMP_LOW = 'target_temp_low'
ATTR_TARGET_TEMP_STEP = 'target_temp_step'
ATTR_AWAY_MODE = 'away_mode'
ATTR_AUX_HEAT = 'aux_heat'
ATTR_FAN_MODE = 'fan_mode'
ATTR_FAN_LIST = 'fan_list'
ATTR_CURRENT_HUMIDITY = 'current_humidity'
ATTR_HUMIDITY = 'humidity'
ATTR_MAX_HUMIDITY = 'max_humidity'
ATTR_MIN_HUMIDITY = 'min_humidity'
ATTR_HOLD_MODE = 'hold_mode'
ATTR_OPERATION_MODE = 'operation_mode'
ATTR_OPERATION_LIST = 'operation_list'
ATTR_SWING_MODE = 'swing_mode'
ATTR_SWING_LIST = 'swing_list'

CONVERTIBLE_ATTRIBUTE = [
ATTR_TEMPERATURE,
ATTR_TARGET_TEMP_LOW,
Expand Down
32 changes: 32 additions & 0 deletions homeassistant/components/climate/const.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""Proides the constants needed for component."""

ATTR_AUX_HEAT = 'aux_heat'
ATTR_AWAY_MODE = 'away_mode'
ATTR_CURRENT_HUMIDITY = 'current_humidity'
ATTR_CURRENT_TEMPERATURE = 'current_temperature'
ATTR_FAN_LIST = 'fan_list'
ATTR_FAN_MODE = 'fan_mode'
ATTR_HOLD_MODE = 'hold_mode'
ATTR_HUMIDITY = 'humidity'
ATTR_MAX_HUMIDITY = 'max_humidity'
ATTR_MAX_TEMP = 'max_temp'
ATTR_MIN_HUMIDITY = 'min_humidity'
ATTR_MIN_TEMP = 'min_temp'
ATTR_OPERATION_LIST = 'operation_list'
ATTR_OPERATION_MODE = 'operation_mode'
ATTR_SWING_LIST = 'swing_list'
ATTR_SWING_MODE = 'swing_mode'
ATTR_TARGET_TEMP_HIGH = 'target_temp_high'
ATTR_TARGET_TEMP_LOW = 'target_temp_low'
ATTR_TARGET_TEMP_STEP = 'target_temp_step'

DOMAIN = 'climate'

SERVICE_SET_AUX_HEAT = 'set_aux_heat'
SERVICE_SET_AWAY_MODE = 'set_away_mode'
SERVICE_SET_FAN_MODE = 'set_fan_mode'
SERVICE_SET_HOLD_MODE = 'set_hold_mode'
SERVICE_SET_HUMIDITY = 'set_humidity'
SERVICE_SET_OPERATION_MODE = 'set_operation_mode'
SERVICE_SET_SWING_MODE = 'set_swing_mode'
SERVICE_SET_TEMPERATURE = 'set_temperature'
91 changes: 91 additions & 0 deletions homeassistant/components/climate/reproduce_state.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
"""Module that groups code required to handle state restore for component."""
import asyncio
from typing import Iterable, Optional

from homeassistant.const import (
ATTR_TEMPERATURE, SERVICE_TURN_OFF,
SERVICE_TURN_ON, STATE_OFF, STATE_ON)
from homeassistant.core import Context, State
from homeassistant.helpers.typing import HomeAssistantType
from homeassistant.loader import bind_hass

from .const import (
ATTR_AUX_HEAT,
ATTR_AWAY_MODE,
ATTR_TARGET_TEMP_HIGH,
ATTR_TARGET_TEMP_LOW,
ATTR_HOLD_MODE,
ATTR_OPERATION_MODE,
ATTR_SWING_MODE,
ATTR_HUMIDITY,
SERVICE_SET_AWAY_MODE,
SERVICE_SET_AUX_HEAT,
SERVICE_SET_TEMPERATURE,
SERVICE_SET_HOLD_MODE,
SERVICE_SET_OPERATION_MODE,
SERVICE_SET_SWING_MODE,
SERVICE_SET_HUMIDITY,
DOMAIN,
)


async def _async_reproduce_states(hass: HomeAssistantType,
state: State,
context: Optional[Context] = None) -> None:
"""Reproduce component states."""
async def call_service(service: str, keys: Iterable):
"""Call service with set of attributes given."""
data = {}
data['entity_id'] = state.entity_id
for key in keys:
if key in state.attributes:
data[key] = state.attributes[key]

await hass.services.async_call(
DOMAIN, service, data,
blocking=True, context=context)

if state.state == STATE_ON:
await call_service(SERVICE_TURN_ON, [])
elif state.state == STATE_OFF:
await call_service(SERVICE_TURN_OFF, [])

if ATTR_AUX_HEAT in state.attributes:
await call_service(SERVICE_SET_AUX_HEAT, [ATTR_AUX_HEAT])

if ATTR_AWAY_MODE in state.attributes:
await call_service(SERVICE_SET_AWAY_MODE, [ATTR_AWAY_MODE])

if (ATTR_TEMPERATURE in state.attributes) or \
(ATTR_TARGET_TEMP_HIGH in state.attributes) or \
(ATTR_TARGET_TEMP_LOW in state.attributes):
await call_service(SERVICE_SET_TEMPERATURE,
[ATTR_TEMPERATURE,
ATTR_TARGET_TEMP_HIGH,
ATTR_TARGET_TEMP_LOW])

if ATTR_HOLD_MODE in state.attributes:
await call_service(SERVICE_SET_HOLD_MODE,
[ATTR_HOLD_MODE])

if ATTR_OPERATION_MODE in state.attributes:
await call_service(SERVICE_SET_OPERATION_MODE,
[ATTR_OPERATION_MODE])

if ATTR_SWING_MODE in state.attributes:
await call_service(SERVICE_SET_SWING_MODE,
[ATTR_SWING_MODE])

if ATTR_HUMIDITY in state.attributes:
await call_service(SERVICE_SET_HUMIDITY,
[ATTR_HUMIDITY])


@bind_hass
async def async_reproduce_states(hass: HomeAssistantType,
states: Iterable[State],
context: Optional[Context] = None) -> None:
"""Reproduce component states."""
await asyncio.gather(*[
_async_reproduce_states(hass, state, context)
for state in states])
2 changes: 2 additions & 0 deletions homeassistant/components/group/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import homeassistant.helpers.config_validation as cv
from homeassistant.util.async_ import run_coroutine_threadsafe

from .reproduce_state import async_reproduce_states # noqa

DOMAIN = 'group'

ENTITY_ID_FORMAT = DOMAIN + '.{}'
Expand Down
28 changes: 28 additions & 0 deletions homeassistant/components/group/reproduce_state.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""Module that groups code required to handle state restore for component."""
from typing import Iterable, Optional

from homeassistant.core import Context, State
from homeassistant.helpers.typing import HomeAssistantType
from homeassistant.loader import bind_hass


@bind_hass
async def async_reproduce_states(hass: HomeAssistantType,
states: Iterable[State],
context: Optional[Context] = None) -> None:
"""Reproduce component states."""
from . import get_entity_ids
from homeassistant.helpers.state import async_reproduce_state
states_copy = []
for state in states:
members = get_entity_ids(hass, state.entity_id)
for member in members:
states_copy.append(
State(member,
state.state,
state.attributes,
last_changed=state.last_changed,
last_updated=state.last_updated,
context=state.context))
await async_reproduce_state(hass, states_copy, blocking=True,
context=context)
68 changes: 35 additions & 33 deletions homeassistant/components/media_player/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,44 @@
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.loader import bind_hass

from .const import (
ATTR_APP_ID,
ATTR_APP_NAME,
ATTR_INPUT_SOURCE,
ATTR_INPUT_SOURCE_LIST,
ATTR_MEDIA_ALBUM_ARTIST,
ATTR_MEDIA_ALBUM_NAME,
ATTR_MEDIA_ARTIST,
ATTR_MEDIA_CHANNEL,
ATTR_MEDIA_CONTENT_ID,
ATTR_MEDIA_CONTENT_TYPE,
ATTR_MEDIA_DURATION,
ATTR_MEDIA_ENQUEUE,
ATTR_MEDIA_EPISODE,
ATTR_MEDIA_PLAYLIST,
ATTR_MEDIA_POSITION,
ATTR_MEDIA_POSITION_UPDATED_AT,
ATTR_MEDIA_SEASON,
ATTR_MEDIA_SEEK_POSITION,
ATTR_MEDIA_SERIES_TITLE,
ATTR_MEDIA_SHUFFLE,
ATTR_MEDIA_TITLE,
ATTR_MEDIA_TRACK,
ATTR_MEDIA_VOLUME_LEVEL,
ATTR_MEDIA_VOLUME_MUTED,
ATTR_SOUND_MODE,
ATTR_SOUND_MODE_LIST,
DOMAIN,
SERVICE_CLEAR_PLAYLIST,
SERVICE_PLAY_MEDIA,
SERVICE_SELECT_SOUND_MODE,
SERVICE_SELECT_SOURCE,
)
from .reproduce_state import async_reproduce_states # noqa

_LOGGER = logging.getLogger(__name__)
_RND = SystemRandom()

DOMAIN = 'media_player'
DEPENDENCIES = ['http']

ENTITY_ID_FORMAT = DOMAIN + '.{}'
Expand All @@ -55,38 +89,6 @@
CACHE_MAXSIZE: 16
}

SERVICE_PLAY_MEDIA = 'play_media'
SERVICE_SELECT_SOURCE = 'select_source'
SERVICE_SELECT_SOUND_MODE = 'select_sound_mode'
SERVICE_CLEAR_PLAYLIST = 'clear_playlist'

ATTR_MEDIA_VOLUME_LEVEL = 'volume_level'
ATTR_MEDIA_VOLUME_MUTED = 'is_volume_muted'
ATTR_MEDIA_SEEK_POSITION = 'seek_position'
ATTR_MEDIA_CONTENT_ID = 'media_content_id'
ATTR_MEDIA_CONTENT_TYPE = 'media_content_type'
ATTR_MEDIA_DURATION = 'media_duration'
ATTR_MEDIA_POSITION = 'media_position'
ATTR_MEDIA_POSITION_UPDATED_AT = 'media_position_updated_at'
ATTR_MEDIA_TITLE = 'media_title'
ATTR_MEDIA_ARTIST = 'media_artist'
ATTR_MEDIA_ALBUM_NAME = 'media_album_name'
ATTR_MEDIA_ALBUM_ARTIST = 'media_album_artist'
ATTR_MEDIA_TRACK = 'media_track'
ATTR_MEDIA_SERIES_TITLE = 'media_series_title'
ATTR_MEDIA_SEASON = 'media_season'
ATTR_MEDIA_EPISODE = 'media_episode'
ATTR_MEDIA_CHANNEL = 'media_channel'
ATTR_MEDIA_PLAYLIST = 'media_playlist'
ATTR_APP_ID = 'app_id'
ATTR_APP_NAME = 'app_name'
ATTR_INPUT_SOURCE = 'source'
ATTR_INPUT_SOURCE_LIST = 'source_list'
ATTR_SOUND_MODE = 'sound_mode'
ATTR_SOUND_MODE_LIST = 'sound_mode_list'
ATTR_MEDIA_ENQUEUE = 'enqueue'
ATTR_MEDIA_SHUFFLE = 'shuffle'

MEDIA_TYPE_MUSIC = 'music'
MEDIA_TYPE_TVSHOW = 'tvshow'
MEDIA_TYPE_MOVIE = 'movie'
Expand Down
35 changes: 35 additions & 0 deletions homeassistant/components/media_player/const.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""Proides the constants needed for component."""

ATTR_APP_ID = 'app_id'
ATTR_APP_NAME = 'app_name'
ATTR_INPUT_SOURCE = 'source'
ATTR_INPUT_SOURCE_LIST = 'source_list'
ATTR_MEDIA_ALBUM_ARTIST = 'media_album_artist'
ATTR_MEDIA_ALBUM_NAME = 'media_album_name'
ATTR_MEDIA_ARTIST = 'media_artist'
ATTR_MEDIA_CHANNEL = 'media_channel'
ATTR_MEDIA_CONTENT_ID = 'media_content_id'
ATTR_MEDIA_CONTENT_TYPE = 'media_content_type'
ATTR_MEDIA_DURATION = 'media_duration'
ATTR_MEDIA_ENQUEUE = 'enqueue'
ATTR_MEDIA_EPISODE = 'media_episode'
ATTR_MEDIA_PLAYLIST = 'media_playlist'
ATTR_MEDIA_POSITION = 'media_position'
ATTR_MEDIA_POSITION_UPDATED_AT = 'media_position_updated_at'
ATTR_MEDIA_SEASON = 'media_season'
ATTR_MEDIA_SEEK_POSITION = 'seek_position'
ATTR_MEDIA_SERIES_TITLE = 'media_series_title'
ATTR_MEDIA_SHUFFLE = 'shuffle'
ATTR_MEDIA_TITLE = 'media_title'
ATTR_MEDIA_TRACK = 'media_track'
ATTR_MEDIA_VOLUME_LEVEL = 'volume_level'
ATTR_MEDIA_VOLUME_MUTED = 'is_volume_muted'
ATTR_SOUND_MODE = 'sound_mode'
ATTR_SOUND_MODE_LIST = 'sound_mode_list'

DOMAIN = 'media_player'

SERVICE_CLEAR_PLAYLIST = 'clear_playlist'
SERVICE_PLAY_MEDIA = 'play_media'
SERVICE_SELECT_SOUND_MODE = 'select_sound_mode'
SERVICE_SELECT_SOURCE = 'select_source'
Loading

0 comments on commit 3bb5caa

Please sign in to comment.