Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 11 additions & 40 deletions homeassistant/components/history.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

from homeassistant.const import (
HTTP_BAD_REQUEST, CONF_DOMAINS, CONF_ENTITIES, CONF_EXCLUDE, CONF_INCLUDE)
import homeassistant.helpers.config_validation as cv
import homeassistant.util.dt as dt_util
from homeassistant.components import recorder, script
from homeassistant.components.frontend import register_built_in_panel
Expand All @@ -28,34 +27,20 @@
DEPENDENCIES = ['recorder', 'http']

CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
CONF_EXCLUDE: vol.Schema({
vol.Optional(CONF_ENTITIES, default=[]): cv.entity_ids,
vol.Optional(CONF_DOMAINS, default=[]):
vol.All(cv.ensure_list, [cv.string])
}),
CONF_INCLUDE: vol.Schema({
vol.Optional(CONF_ENTITIES, default=[]): cv.entity_ids,
vol.Optional(CONF_DOMAINS, default=[]):
vol.All(cv.ensure_list, [cv.string])
})
}),
DOMAIN: recorder.FILTER_SCHEMA,
}, extra=vol.ALLOW_EXTRA)

SIGNIFICANT_DOMAINS = ('thermostat', 'climate')
IGNORE_DOMAINS = ('zone', 'scene',)


def last_5_states(entity_id):
"""Return the last 5 states for entity_id."""
entity_id = entity_id.lower()

states = recorder.get_model('States')
return recorder.execute(
recorder.query('States').filter(
(states.entity_id == entity_id) &
(states.last_changed == states.last_updated)
).order_by(states.state_id.desc()).limit(5))
def last_recorder_run():

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use hybrid properties instead and add this to the RecorderRun model: http://stackoverflow.com/a/14616299/646416

We then no longer need a helper method. (and yes, we should probably remove more of these helper methods in favor of that)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Postponed for future PR

"""Retireve the last closed recorder run from the DB."""
rec_runs = recorder.get_model('RecorderRuns')
with recorder.session_scope() as session:
res = recorder.query(rec_runs).order_by(rec_runs.end.desc()).first()
session.expunge(res)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

first() can return None which will blow up when passed to session.expunge

return res


def get_significant_states(start_time, end_time=None, entity_id=None,
Expand Down Expand Up @@ -91,7 +76,7 @@ def get_significant_states(start_time, end_time=None, entity_id=None,
def state_changes_during_period(start_time, end_time=None, entity_id=None):
"""Return states changes during UTC period start_time - end_time."""
states = recorder.get_model('States')
query = recorder.query('States').filter(
query = recorder.query(states).filter(
(states.last_changed == states.last_updated) &
(states.last_changed > start_time))

Expand Down Expand Up @@ -132,7 +117,7 @@ def get_states(utc_point_in_time, entity_ids=None, run=None, filters=None):
most_recent_state_ids = most_recent_state_ids.group_by(
states.entity_id).subquery()

query = recorder.query('States').join(most_recent_state_ids, and_(
query = recorder.query(states).join(most_recent_state_ids, and_(
states.state_id == most_recent_state_ids.c.max_state_id))

for state in recorder.execute(query):
Expand Down Expand Up @@ -185,27 +170,13 @@ def setup(hass, config):
filters.included_entities = include[CONF_ENTITIES]
filters.included_domains = include[CONF_DOMAINS]

hass.http.register_view(Last5StatesView)
recorder.get_instance()
hass.http.register_view(HistoryPeriodView(filters))
register_built_in_panel(hass, 'history', 'History', 'mdi:poll-box')

return True


class Last5StatesView(HomeAssistantView):
"""Handle last 5 state view requests."""

url = '/api/history/entity/{entity_id}/recent_states'
name = 'api:history:entity-recent-states'

@asyncio.coroutine
def get(self, request, entity_id):
"""Retrieve last 5 states of entity."""
result = yield from request.app['hass'].loop.run_in_executor(
None, last_5_states, entity_id)
return self.json(result)


class HistoryPeriodView(HomeAssistantView):
"""Handle history period requests."""

Expand Down
8 changes: 8 additions & 0 deletions homeassistant/components/input_boolean.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import ToggleEntity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.components.recorder.restore_state import get_last_state

DOMAIN = 'input_boolean'

Expand Down Expand Up @@ -139,6 +140,13 @@ def is_on(self):
"""Return true if entity is on."""
return self._state

@asyncio.coroutine
def async_added_to_hass(self):
"""Component added to hass, no platforms, so restore state here."""
state = get_last_state(self, False)
if state:
self._state = state.state == 'on'

@asyncio.coroutine
def async_turn_on(self, **kwargs):
"""Turn the entity on."""
Expand Down
14 changes: 14 additions & 0 deletions homeassistant/components/light/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

from homeassistant.core import callback
from homeassistant.components import group
from homeassistant.components.recorder.restore_state import get_last_state
from homeassistant.config import load_yaml_config_file
from homeassistant.const import (
STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, SERVICE_TOGGLE,
Expand Down Expand Up @@ -369,3 +370,16 @@ def state_attributes(self):
def supported_features(self):
"""Flag supported features."""
return 0

@asyncio.coroutine
def async_added_to_hass(self):
"""Component added, restore_state using platforms."""
state = get_last_state(self)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get_last_state should only take in an entity_id.

if not hasattr(self, 'async_restore_state'):
    return

state = get_last_state(self.entity_id)

if state:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you convert this to a guard clause? This example is going to be copied by a lot of other component/platform developers and thus needs to be perfect 🥇

if state is None:
    return

params =

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your view on moving this to homeassistant.helpers.restore_state?

Two reasons:

  • recorder does not explicitly have to request the cache, but get_last_state handles this.
  • Many components & platforms (possibly every sensor) will import this "helper"

The cache function can then be a _private

@balloob balloob Feb 19, 2017

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add another helper to this file that wraps the entity logic around get_state.

def extract_info(state):
    """Restore properties from state object."""
    return {
        is_on: state.state == STATE_ON,
        # etc…
    }

class Light:
    def async_added_to_hass(self):
        yield from restore_state.xxxxx(self, extract_info)

And in restore state, the helper xxxxx does all the logic that was in light before.

def xxxxx(entity, extract_info):
    if not hasattr(entity, 'async_restore_state'):
        return

    state = get_state(entity.entity_id)

    if not state:
        return

    yield from entity.async_restore_state(**extract_info(state))

params = {key: state.attr[key] for key in (
ATTR_BRIGHTNESS, ATTR_RGB_COLOR, ATTR_XY_COLOR,
ATTR_COLOR_TEMP, ATTR_COLOR_NAME, ATTR_WHITE_VALUE
) if key in state.attr}
params['is_on'] = state.state == STATE_ON
# pylint: disable=no-member
yield from self.async_restore_state(**params)
Loading