diff --git a/homeassistant/components/history_stats/__init__.py b/homeassistant/components/history_stats/__init__.py index 3c5385be6ad..dcdca70b71c 100644 --- a/homeassistant/components/history_stats/__init__.py +++ b/homeassistant/components/history_stats/__init__.py @@ -1 +1,4 @@ """The history_stats component.""" + +DOMAIN = "history_stats" +PLATFORMS = ["sensor"] diff --git a/homeassistant/components/history_stats/sensor.py b/homeassistant/components/history_stats/sensor.py index 2c77261d344..e4ff1ab5b28 100644 --- a/homeassistant/components/history_stats/sensor.py +++ b/homeassistant/components/history_stats/sensor.py @@ -16,16 +16,18 @@ from homeassistant.const import ( TIME_HOURS, UNIT_PERCENTAGE, ) -from homeassistant.core import callback +from homeassistant.core import CoreState, callback from homeassistant.exceptions import TemplateError import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity from homeassistant.helpers.event import async_track_state_change_event +from homeassistant.helpers.reload import setup_reload_service import homeassistant.util.dt as dt_util +from . import DOMAIN, PLATFORMS + _LOGGER = logging.getLogger(__name__) -DOMAIN = "history_stats" CONF_START = "start" CONF_END = "end" CONF_DURATION = "duration" @@ -75,6 +77,9 @@ PLATFORM_SCHEMA = vol.All( # noinspection PyUnusedLocal def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the History Stats sensor.""" + + setup_reload_service(hass, DOMAIN, PLATFORMS) + entity_id = config.get(CONF_ENTITY_ID) entity_state = config.get(CONF_STATE) start = config.get(CONF_START) @@ -118,6 +123,9 @@ class HistoryStatsSensor(Entity): self.value = None self.count = None + async def async_added_to_hass(self): + """Create listeners when the entity is added.""" + @callback def start_refresh(*args): """Register state tracking.""" @@ -128,10 +136,18 @@ class HistoryStatsSensor(Entity): self.async_schedule_update_ha_state(True) force_refresh() - async_track_state_change_event(self.hass, [self._entity_id], force_refresh) + self.async_on_remove( + async_track_state_change_event( + self.hass, [self._entity_id], force_refresh + ) + ) + + if self.hass.state == CoreState.running: + start_refresh() + return # Delay first refresh to keep startup fast - hass.bus.listen_once(EVENT_HOMEASSISTANT_START, start_refresh) + self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, start_refresh) @property def name(self): diff --git a/homeassistant/components/history_stats/services.yaml b/homeassistant/components/history_stats/services.yaml new file mode 100644 index 00000000000..38758a35df0 --- /dev/null +++ b/homeassistant/components/history_stats/services.yaml @@ -0,0 +1,2 @@ +reload: + description: Reload all history_stats entities. diff --git a/tests/components/history_stats/test_sensor.py b/tests/components/history_stats/test_sensor.py index 25cca7615a7..bab6eae4564 100644 --- a/tests/components/history_stats/test_sensor.py +++ b/tests/components/history_stats/test_sensor.py @@ -1,16 +1,19 @@ """The test for the History Statistics sensor platform.""" # pylint: disable=protected-access from datetime import datetime, timedelta +from os import path import unittest import pytest import pytz +from homeassistant import config as hass_config +from homeassistant.components.history_stats import DOMAIN from homeassistant.components.history_stats.sensor import HistoryStatsSensor -from homeassistant.const import STATE_UNKNOWN +from homeassistant.const import SERVICE_RELOAD, STATE_UNKNOWN import homeassistant.core as ha from homeassistant.helpers.template import Template -from homeassistant.setup import setup_component +from homeassistant.setup import async_setup_component, setup_component import homeassistant.util.dt as dt_util from tests.async_mock import patch @@ -255,3 +258,58 @@ class TestHistoryStatsSensor(unittest.TestCase): """Initialize the recorder.""" init_recorder_component(self.hass) self.hass.start() + + +async def test_reload(hass): + """Verify we can reload history_stats sensors.""" + await hass.async_add_executor_job( + init_recorder_component, hass + ) # force in memory db + + hass.state = ha.CoreState.not_running + hass.states.async_set("binary_sensor.test_id", "on") + + await async_setup_component( + hass, + "sensor", + { + "sensor": { + "platform": "history_stats", + "entity_id": "binary_sensor.test_id", + "name": "test", + "state": "on", + "start": "{{ as_timestamp(now()) - 3600 }}", + "duration": "01:00", + }, + }, + ) + await hass.async_block_till_done() + await hass.async_start() + await hass.async_block_till_done() + + assert len(hass.states.async_all()) == 2 + + assert hass.states.get("sensor.test") + + yaml_path = path.join( + _get_fixtures_base_path(), + "fixtures", + "history_stats/configuration.yaml", + ) + with patch.object(hass_config, "YAML_CONFIG_FILE", yaml_path): + await hass.services.async_call( + DOMAIN, + SERVICE_RELOAD, + {}, + blocking=True, + ) + await hass.async_block_till_done() + + assert len(hass.states.async_all()) == 2 + + assert hass.states.get("sensor.test") is None + assert hass.states.get("sensor.second_test") + + +def _get_fixtures_base_path(): + return path.dirname(path.dirname(path.dirname(__file__))) diff --git a/tests/fixtures/history_stats/configuration.yaml b/tests/fixtures/history_stats/configuration.yaml new file mode 100644 index 00000000000..15704996998 --- /dev/null +++ b/tests/fixtures/history_stats/configuration.yaml @@ -0,0 +1,7 @@ +sensor: + - platform: history_stats + entity_id: binary_sensor.test_id + name: second_test + state: "on" + start: "{{ as_timestamp(now()) - 3600 }}" + end: "{{ now() }}"