Support longer-than-60-second scan_interval and interval_seconds (#5147)

* Update scan_interval and interval_seconds max to 1 day vs. 60 seconds

* Format fixes

* Add docstring on unittest.

* Added and implemented new async_track_time_interval helper.

* Format fixes, removed unused import.

* Undid whoops on unsub_polling.

* Updated unit tests for scan_interval.

* Added unit test for track_time_interval.

* Allow other forms of time interval input for scan_interval and interval_seconds
This commit is contained in:
Nick Touran 2017-01-05 14:05:16 -08:00 committed by Paulus Schoutsen
parent f88b5a9c5e
commit a36ca62445
6 changed files with 81 additions and 16 deletions

View File

@ -24,6 +24,7 @@ from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers import config_per_platform, discovery
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.event import async_track_time_interval
from homeassistant.helpers.typing import GPSType, ConfigType, HomeAssistantType
import homeassistant.helpers.config_validation as cv
import homeassistant.util as util
@ -71,7 +72,7 @@ ATTR_BATTERY = 'battery'
ATTR_ATTRIBUTES = 'attributes'
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
vol.Optional(CONF_SCAN_INTERVAL): cv.positive_int, # seconds
vol.Optional(CONF_SCAN_INTERVAL): cv.time_period,
vol.Optional(CONF_TRACK_NEW, default=DEFAULT_TRACK_NEW): cv.boolean,
vol.Optional(CONF_CONSIDER_HOME,
default=timedelta(seconds=DEFAULT_CONSIDER_HOME)): vol.All(
@ -639,8 +640,9 @@ def async_setup_scanner_platform(hass: HomeAssistantType, config: ConfigType,
seen.add(mac)
hass.async_add_job(async_see_device(mac=mac, host_name=host_name))
async_track_utc_time_change(
hass, async_device_tracker_scan, second=range(0, 60, interval))
async_track_time_interval(
hass, async_device_tracker_scan,
timedelta(seconds=interval))
hass.async_add_job(async_device_tracker_scan, None)

View File

@ -405,8 +405,7 @@ def key_dependency(key, dependency):
PLATFORM_SCHEMA = vol.Schema({
vol.Required(CONF_PLATFORM): string,
vol.Optional(CONF_SCAN_INTERVAL):
vol.All(vol.Coerce(int), vol.Range(min=1)),
vol.Optional(CONF_SCAN_INTERVAL): time_period
}, extra=vol.ALLOW_EXTRA)
EVENT_SCHEMA = vol.Schema({

View File

@ -1,5 +1,6 @@
"""Helpers for components that manage entities."""
import asyncio
from datetime import timedelta
from homeassistant import config as conf_util
from homeassistant.bootstrap import (
@ -12,7 +13,7 @@ from homeassistant.exceptions import HomeAssistantError
from homeassistant.loader import get_component
from homeassistant.helpers import config_per_platform, discovery
from homeassistant.helpers.entity import async_generate_entity_id
from homeassistant.helpers.event import async_track_utc_time_change
from homeassistant.helpers.event import async_track_time_interval
from homeassistant.helpers.service import extract_entity_ids
from homeassistant.util.async import (
run_callback_threadsafe, run_coroutine_threadsafe)
@ -324,9 +325,10 @@ class EntityPlatform(object):
in self.platform_entities):
return
self._async_unsub_polling = async_track_utc_time_change(
self._async_unsub_polling = async_track_time_interval(
self.component.hass, self._update_entity_states,
second=range(0, 60, self.scan_interval))
timedelta(seconds=self.scan_interval)
)
@asyncio.coroutine
def _async_process_entity(self, new_entity, update_before_add):

View File

@ -85,7 +85,7 @@ track_state_change = threaded_listener_factory(async_track_state_change)
def async_track_point_in_time(hass, action, point_in_time):
"""Add a listener that fires once after a spefic point in time."""
"""Add a listener that fires once after a specific point in time."""
utc_point_in_time = dt_util.as_utc(point_in_time)
@callback
@ -133,6 +133,33 @@ track_point_in_utc_time = threaded_listener_factory(
async_track_point_in_utc_time)
def async_track_time_interval(hass, action, interval):
"""Add a listener that fires repetitively at every timedelta interval."""
def next_interval():
"""Return the next interval."""
return dt_util.utcnow() + interval
@callback
def interval_listener(now):
"""Called when when the interval has elapsed."""
nonlocal remove
remove = async_track_point_in_utc_time(
hass, interval_listener, next_interval())
hass.async_run_job(action, now)
remove = async_track_point_in_utc_time(
hass, interval_listener, next_interval())
def remove_listener():
"""Remove interval listener."""
remove()
return remove_listener
track_time_interval = threaded_listener_factory(async_track_time_interval)
def async_track_sunrise(hass, action, offset=None):
"""Add a listener that will fire a specified offset from sunrise daily."""
from homeassistant.components import sun

View File

@ -5,12 +5,15 @@ from collections import OrderedDict
import logging
import unittest
from unittest.mock import patch, Mock
from datetime import timedelta
import homeassistant.core as ha
import homeassistant.loader as loader
from homeassistant.components import group
from homeassistant.helpers.entity import Entity, generate_entity_id
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.entity_component import (
EntityComponent, DEFAULT_SCAN_INTERVAL)
from homeassistant.helpers import discovery
import homeassistant.util.dt as dt_util
@ -106,7 +109,7 @@ class TestHelpersEntityComponent(unittest.TestCase):
no_poll_ent.async_update.reset_mock()
poll_ent.async_update.reset_mock()
fire_time_changed(self.hass, dt_util.utcnow().replace(second=0))
fire_time_changed(self.hass, dt_util.utcnow() + timedelta(seconds=20))
self.hass.block_till_done()
assert not no_poll_ent.async_update.called
@ -123,7 +126,10 @@ class TestHelpersEntityComponent(unittest.TestCase):
assert 1 == len(self.hass.states.entity_ids())
ent2.update = lambda *_: component.add_entities([ent1])
fire_time_changed(self.hass, dt_util.utcnow().replace(second=0))
fire_time_changed(
self.hass, dt_util.utcnow() +
timedelta(seconds=DEFAULT_SCAN_INTERVAL)
)
self.hass.block_till_done()
assert 2 == len(self.hass.states.entity_ids())
@ -311,7 +317,7 @@ class TestHelpersEntityComponent(unittest.TestCase):
mock_setup.call_args[0]
@patch('homeassistant.helpers.entity_component.'
'async_track_utc_time_change')
'async_track_time_interval')
def test_set_scan_interval_via_config(self, mock_track):
"""Test the setting of the scan interval via configuration."""
def platform_setup(hass, config, add_devices, discovery_info=None):
@ -331,10 +337,10 @@ class TestHelpersEntityComponent(unittest.TestCase):
})
assert mock_track.called
assert [0, 30] == list(mock_track.call_args[1]['second'])
assert timedelta(seconds=30) == mock_track.call_args[0][2]
@patch('homeassistant.helpers.entity_component.'
'async_track_utc_time_change')
'async_track_time_interval')
def test_set_scan_interval_via_platform(self, mock_track):
"""Test the setting of the scan interval via platform."""
def platform_setup(hass, config, add_devices, discovery_info=None):
@ -355,7 +361,7 @@ class TestHelpersEntityComponent(unittest.TestCase):
})
assert mock_track.called
assert [0, 30] == list(mock_track.call_args[1]['second'])
assert timedelta(seconds=30) == mock_track.call_args[0][2]
def test_set_entity_namespace_via_config(self):
"""Test setting an entity namespace."""

View File

@ -15,6 +15,7 @@ from homeassistant.helpers.event import (
track_utc_time_change,
track_time_change,
track_state_change,
track_time_interval,
track_sunrise,
track_sunset,
)
@ -187,6 +188,34 @@ class TestEventHelpers(unittest.TestCase):
self.assertEqual(5, len(wildcard_runs))
self.assertEqual(6, len(wildercard_runs))
def test_track_time_interval(self):
"""Test tracking time interval."""
specific_runs = []
utc_now = dt_util.utcnow()
unsub = track_time_interval(
self.hass, lambda x: specific_runs.append(1),
timedelta(seconds=10)
)
self._send_time_changed(utc_now + timedelta(seconds=5))
self.hass.block_till_done()
self.assertEqual(0, len(specific_runs))
self._send_time_changed(utc_now + timedelta(seconds=13))
self.hass.block_till_done()
self.assertEqual(1, len(specific_runs))
self._send_time_changed(utc_now + timedelta(minutes=20))
self.hass.block_till_done()
self.assertEqual(2, len(specific_runs))
unsub()
self._send_time_changed(utc_now + timedelta(seconds=30))
self.hass.block_till_done()
self.assertEqual(2, len(specific_runs))
def test_track_sunrise(self):
"""Test track the sunrise."""
latitude = 32.87336