EventBus: return function to unlisten

This commit is contained in:
Paulus Schoutsen 2016-08-25 23:25:35 -07:00
parent 62ba0fa7a2
commit d9ecc4af64
11 changed files with 183 additions and 83 deletions

View File

@ -7,8 +7,7 @@ at https://home-assistant.io/components/automation/#state-trigger
import voluptuous as vol import voluptuous as vol
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
from homeassistant.const import ( from homeassistant.const import MATCH_ALL, CONF_PLATFORM
EVENT_STATE_CHANGED, EVENT_TIME_CHANGED, MATCH_ALL, CONF_PLATFORM)
from homeassistant.helpers.event import track_state_change, track_point_in_time from homeassistant.helpers.event import track_state_change, track_point_in_time
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
@ -60,23 +59,20 @@ def trigger(hass, config, action):
def state_for_listener(now): def state_for_listener(now):
"""Fire on state changes after a delay and calls action.""" """Fire on state changes after a delay and calls action."""
hass.bus.remove_listener( remove_state_for_cancel()
EVENT_STATE_CHANGED, attached_state_for_cancel)
call_action() call_action()
def state_for_cancel_listener(entity, inner_from_s, inner_to_s): def state_for_cancel_listener(entity, inner_from_s, inner_to_s):
"""Fire on changes and cancel for listener if changed.""" """Fire on changes and cancel for listener if changed."""
if inner_to_s.state == to_s.state: if inner_to_s.state == to_s.state:
return return
hass.bus.remove_listener(EVENT_TIME_CHANGED, remove_state_for_listener()
attached_state_for_listener) remove_state_for_cancel()
hass.bus.remove_listener(EVENT_STATE_CHANGED,
attached_state_for_cancel)
attached_state_for_listener = track_point_in_time( remove_state_for_listener = track_point_in_time(
hass, state_for_listener, dt_util.utcnow() + time_delta) hass, state_for_listener, dt_util.utcnow() + time_delta)
attached_state_for_cancel = track_state_change( remove_state_for_cancel = track_state_change(
hass, entity, state_for_cancel_listener) hass, entity, state_for_cancel_listener)
track_state_change( track_state_change(

View File

@ -5,7 +5,6 @@ For more details about this platform, please refer to the documentation
https://home-assistant.io/components/demo/ https://home-assistant.io/components/demo/
""" """
from homeassistant.components.cover import CoverDevice from homeassistant.components.cover import CoverDevice
from homeassistant.const import EVENT_TIME_CHANGED
from homeassistant.helpers.event import track_utc_time_change from homeassistant.helpers.event import track_utc_time_change
@ -32,8 +31,8 @@ class DemoCover(CoverDevice):
self._tilt_position = tilt_position self._tilt_position = tilt_position
self._closing = True self._closing = True
self._closing_tilt = True self._closing_tilt = True
self._listener_cover = None self._unsub_listener_cover = None
self._listener_cover_tilt = None self._unsub_listener_cover_tilt = None
@property @property
def name(self): def name(self):
@ -120,10 +119,9 @@ class DemoCover(CoverDevice):
"""Stop the cover.""" """Stop the cover."""
if self._position is None: if self._position is None:
return return
if self._listener_cover is not None: if self._unsub_listener_cover is not None:
self.hass.bus.remove_listener(EVENT_TIME_CHANGED, self._unsub_listener_cover()
self._listener_cover) self._unsub_listener_cover = None
self._listener_cover = None
self._set_position = None self._set_position = None
def stop_cover_tilt(self, **kwargs): def stop_cover_tilt(self, **kwargs):
@ -131,16 +129,15 @@ class DemoCover(CoverDevice):
if self._tilt_position is None: if self._tilt_position is None:
return return
if self._listener_cover_tilt is not None: if self._unsub_listener_cover_tilt is not None:
self.hass.bus.remove_listener(EVENT_TIME_CHANGED, self._unsub_listener_cover_tilt()
self._listener_cover_tilt) self._unsub_listener_cover_tilt = None
self._listener_cover_tilt = None
self._set_tilt_position = None self._set_tilt_position = None
def _listen_cover(self): def _listen_cover(self):
"""Listen for changes in cover.""" """Listen for changes in cover."""
if self._listener_cover is None: if self._unsub_listener_cover is None:
self._listener_cover = track_utc_time_change( self._unsub_listener_cover = track_utc_time_change(
self.hass, self._time_changed_cover) self.hass, self._time_changed_cover)
def _time_changed_cover(self, now): def _time_changed_cover(self, now):
@ -156,8 +153,8 @@ class DemoCover(CoverDevice):
def _listen_cover_tilt(self): def _listen_cover_tilt(self):
"""Listen for changes in cover tilt.""" """Listen for changes in cover tilt."""
if self._listener_cover_tilt is None: if self._unsub_listener_cover_tilt is None:
self._listener_cover_tilt = track_utc_time_change( self._unsub_listener_cover_tilt = track_utc_time_change(
self.hass, self._time_changed_cover_tilt) self.hass, self._time_changed_cover_tilt)
def _time_changed_cover_tilt(self, now): def _time_changed_cover_tilt(self, now):

View File

@ -175,6 +175,7 @@ class Group(Entity):
self.group_off = None self.group_off = None
self._assumed_state = False self._assumed_state = False
self._lock = threading.Lock() self._lock = threading.Lock()
self._unsub_state_changed = None
if entity_ids is not None: if entity_ids is not None:
self.update_tracked_entity_ids(entity_ids) self.update_tracked_entity_ids(entity_ids)
@ -236,15 +237,16 @@ class Group(Entity):
def start(self): def start(self):
"""Start tracking members.""" """Start tracking members."""
track_state_change( self._unsub_state_changed = track_state_change(
self.hass, self.tracking, self._state_changed_listener) self.hass, self.tracking, self._state_changed_listener)
def stop(self): def stop(self):
"""Unregister the group from Home Assistant.""" """Unregister the group from Home Assistant."""
self.hass.states.remove(self.entity_id) self.hass.states.remove(self.entity_id)
self.hass.bus.remove_listener( if self._unsub_state_changed:
ha.EVENT_STATE_CHANGED, self._state_changed_listener) self._unsub_state_changed()
self._unsub_state_changed = None
def update(self): def update(self):
"""Query all members and determine current group state.""" """Query all members and determine current group state."""

View File

@ -170,9 +170,14 @@ def subscribe(hass, topic, callback, qos=DEFAULT_QOS):
callback(event.data[ATTR_TOPIC], event.data[ATTR_PAYLOAD], callback(event.data[ATTR_TOPIC], event.data[ATTR_PAYLOAD],
event.data[ATTR_QOS]) event.data[ATTR_QOS])
hass.bus.listen(EVENT_MQTT_MESSAGE_RECEIVED, mqtt_topic_subscriber) remove = hass.bus.listen(EVENT_MQTT_MESSAGE_RECEIVED,
mqtt_topic_subscriber)
# Future: track subscriber count and unsubscribe in remove
MQTT_CLIENT.subscribe(topic, qos) MQTT_CLIENT.subscribe(topic, qos)
return remove
def _setup_server(hass, config): def _setup_server(hass, config):
"""Try to start embedded MQTT broker.""" """Try to start embedded MQTT broker."""

View File

@ -5,7 +5,6 @@ For more details about this platform, please refer to the documentation
https://home-assistant.io/components/demo/ https://home-assistant.io/components/demo/
""" """
from homeassistant.components.rollershutter import RollershutterDevice from homeassistant.components.rollershutter import RollershutterDevice
from homeassistant.const import EVENT_TIME_CHANGED
from homeassistant.helpers.event import track_utc_time_change from homeassistant.helpers.event import track_utc_time_change
@ -27,7 +26,7 @@ class DemoRollershutter(RollershutterDevice):
self._name = name self._name = name
self._position = position self._position = position
self._moving_up = True self._moving_up = True
self._listener = None self._unsub_listener = None
@property @property
def name(self): def name(self):
@ -70,15 +69,15 @@ class DemoRollershutter(RollershutterDevice):
def stop(self, **kwargs): def stop(self, **kwargs):
"""Stop the roller shutter.""" """Stop the roller shutter."""
if self._listener is not None: if self._unsub_listener is not None:
self.hass.bus.remove_listener(EVENT_TIME_CHANGED, self._listener) self._unsub_listener()
self._listener = None self._unsub_listener = None
def _listen(self): def _listen(self):
"""Listen for changes.""" """Listen for changes."""
if self._listener is None: if self._unsub_listener is None:
self._listener = track_utc_time_change(self.hass, self._unsub_listener = track_utc_time_change(self.hass,
self._time_changed) self._time_changed)
def _time_changed(self, now): def _time_changed(self, now):
"""Track time changes.""" """Track time changes."""

View File

@ -297,6 +297,12 @@ class EventBus(object):
else: else:
self._listeners[event_type] = [listener] self._listeners[event_type] = [listener]
def remove_listener():
"""Remove the listener."""
self.remove_listener(event_type, listener)
return remove_listener
def listen_once(self, event_type, listener): def listen_once(self, event_type, listener):
"""Listen once for event of a specific type. """Listen once for event of a specific type.

View File

@ -14,8 +14,7 @@ def track_state_change(hass, entity_ids, action, from_state=None,
entity_ids, from_state and to_state can be string or list. entity_ids, from_state and to_state can be string or list.
Use list to match multiple. Use list to match multiple.
Returns the listener that listens on the bus for EVENT_STATE_CHANGED. Returns a function that can be called to remove the listener.
Pass the return value into hass.bus.remove_listener to remove it.
""" """
from_state = _process_state_match(from_state) from_state = _process_state_match(from_state)
to_state = _process_state_match(to_state) to_state = _process_state_match(to_state)
@ -50,9 +49,7 @@ def track_state_change(hass, entity_ids, action, from_state=None,
event.data.get('old_state'), event.data.get('old_state'),
event.data.get('new_state')) event.data.get('new_state'))
hass.bus.listen(EVENT_STATE_CHANGED, state_change_listener) return hass.bus.listen(EVENT_STATE_CHANGED, state_change_listener)
return state_change_listener
def track_point_in_time(hass, action, point_in_time): def track_point_in_time(hass, action, point_in_time):
@ -77,23 +74,20 @@ def track_point_in_utc_time(hass, action, point_in_time):
"""Listen for matching time_changed events.""" """Listen for matching time_changed events."""
now = event.data[ATTR_NOW] now = event.data[ATTR_NOW]
if now >= point_in_time and \ if now < point_in_time or hasattr(point_in_time_listener, 'run'):
not hasattr(point_in_time_listener, 'run'): return
# Set variable so that we will never run twice. # Set variable so that we will never run twice.
# Because the event bus might have to wait till a thread comes # Because the event bus might have to wait till a thread comes
# available to execute this listener it might occur that the # available to execute this listener it might occur that the
# listener gets lined up twice to be executed. This will make # listener gets lined up twice to be executed. This will make
# sure the second time it does nothing. # sure the second time it does nothing.
point_in_time_listener.run = True point_in_time_listener.run = True
remove()
action(now)
hass.bus.remove_listener(EVENT_TIME_CHANGED, remove = hass.bus.listen(EVENT_TIME_CHANGED, point_in_time_listener)
point_in_time_listener) return remove
action(now)
hass.bus.listen(EVENT_TIME_CHANGED, point_in_time_listener)
return point_in_time_listener
def track_sunrise(hass, action, offset=None): def track_sunrise(hass, action, offset=None):
@ -112,10 +106,18 @@ def track_sunrise(hass, action, offset=None):
def sunrise_automation_listener(now): def sunrise_automation_listener(now):
"""Called when it's time for action.""" """Called when it's time for action."""
nonlocal remove
track_point_in_utc_time(hass, sunrise_automation_listener, next_rise()) track_point_in_utc_time(hass, sunrise_automation_listener, next_rise())
action() action()
track_point_in_utc_time(hass, sunrise_automation_listener, next_rise()) remove = track_point_in_utc_time(hass, sunrise_automation_listener,
next_rise())
def remove_listener():
"""Remove sunrise listener."""
remove()
return remove_listener
def track_sunset(hass, action, offset=None): def track_sunset(hass, action, offset=None):
@ -134,10 +136,19 @@ def track_sunset(hass, action, offset=None):
def sunset_automation_listener(now): def sunset_automation_listener(now):
"""Called when it's time for action.""" """Called when it's time for action."""
track_point_in_utc_time(hass, sunset_automation_listener, next_set()) nonlocal remove
remove = track_point_in_utc_time(hass, sunset_automation_listener,
next_set())
action() action()
track_point_in_utc_time(hass, sunset_automation_listener, next_set()) remove = track_point_in_utc_time(hass, sunset_automation_listener,
next_set())
def remove_listener():
"""Remove sunset listener."""
remove()
return remove_listener
# pylint: disable=too-many-arguments # pylint: disable=too-many-arguments
@ -152,8 +163,7 @@ def track_utc_time_change(hass, action, year=None, month=None, day=None,
"""Fire every time event that comes in.""" """Fire every time event that comes in."""
action(event.data[ATTR_NOW]) action(event.data[ATTR_NOW])
hass.bus.listen(EVENT_TIME_CHANGED, time_change_listener) return hass.bus.listen(EVENT_TIME_CHANGED, time_change_listener)
return time_change_listener
pmp = _process_time_match pmp = _process_time_match
year, month, day = pmp(year), pmp(month), pmp(day) year, month, day = pmp(year), pmp(month), pmp(day)
@ -178,8 +188,7 @@ def track_utc_time_change(hass, action, year=None, month=None, day=None,
action(now) action(now)
hass.bus.listen(EVENT_TIME_CHANGED, pattern_time_change_listener) return hass.bus.listen(EVENT_TIME_CHANGED, pattern_time_change_listener)
return pattern_time_change_listener
# pylint: disable=too-many-arguments # pylint: disable=too-many-arguments

View File

@ -7,7 +7,7 @@ from typing import Optional, Sequence
import voluptuous as vol import voluptuous as vol
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.const import EVENT_TIME_CHANGED, CONF_CONDITION from homeassistant.const import CONF_CONDITION
from homeassistant.helpers import ( from homeassistant.helpers import (
service, condition, template, config_validation as cv) service, condition, template, config_validation as cv)
from homeassistant.helpers.event import track_point_in_utc_time from homeassistant.helpers.event import track_point_in_utc_time
@ -47,7 +47,7 @@ class Script():
self.can_cancel = any(CONF_DELAY in action for action self.can_cancel = any(CONF_DELAY in action for action
in self.sequence) in self.sequence)
self._lock = threading.Lock() self._lock = threading.Lock()
self._delay_listener = None self._unsub_delay_listener = None
@property @property
def is_running(self) -> bool: def is_running(self) -> bool:
@ -72,7 +72,7 @@ class Script():
# Call ourselves in the future to continue work # Call ourselves in the future to continue work
def script_delay(now): def script_delay(now):
"""Called after delay is done.""" """Called after delay is done."""
self._delay_listener = None self._unsub_delay_listener = None
self.run(variables) self.run(variables)
delay = action[CONF_DELAY] delay = action[CONF_DELAY]
@ -83,7 +83,7 @@ class Script():
cv.positive_timedelta)( cv.positive_timedelta)(
template.render(self.hass, delay)) template.render(self.hass, delay))
self._delay_listener = track_point_in_utc_time( self._unsub_delay_listener = track_point_in_utc_time(
self.hass, script_delay, self.hass, script_delay,
date_util.utcnow() + delay) date_util.utcnow() + delay)
self._cur = cur + 1 self._cur = cur + 1
@ -139,10 +139,9 @@ class Script():
def _remove_listener(self): def _remove_listener(self):
"""Remove point in time listener, if any.""" """Remove point in time listener, if any."""
if self._delay_listener: if self._unsub_delay_listener:
self.hass.bus.remove_listener(EVENT_TIME_CHANGED, self._unsub_delay_listener()
self._delay_listener) self._unsub_delay_listener = None
self._delay_listener = None
def _log(self, msg): def _log(self, msg):
"""Logger helper.""" """Logger helper."""

View File

@ -149,7 +149,7 @@ class TestMQTT(unittest.TestCase):
def test_subscribe_topic(self): def test_subscribe_topic(self):
"""Test the subscription of a topic.""" """Test the subscription of a topic."""
mqtt.subscribe(self.hass, 'test-topic', self.record_calls) unsub = mqtt.subscribe(self.hass, 'test-topic', self.record_calls)
fire_mqtt_message(self.hass, 'test-topic', 'test-payload') fire_mqtt_message(self.hass, 'test-topic', 'test-payload')
@ -158,6 +158,13 @@ class TestMQTT(unittest.TestCase):
self.assertEqual('test-topic', self.calls[0][0]) self.assertEqual('test-topic', self.calls[0][0])
self.assertEqual('test-payload', self.calls[0][1]) self.assertEqual('test-payload', self.calls[0][1])
unsub()
fire_mqtt_message(self.hass, 'test-topic', 'test-payload')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_subscribe_topic_not_match(self): def test_subscribe_topic_not_match(self):
"""Test if subscribed topic is not a match.""" """Test if subscribed topic is not a match."""
mqtt.subscribe(self.hass, 'test-topic', self.record_calls) mqtt.subscribe(self.hass, 'test-topic', self.record_calls)

View File

@ -65,13 +65,21 @@ class TestEventHelpers(unittest.TestCase):
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertEqual(2, len(runs)) self.assertEqual(2, len(runs))
unsub = track_point_in_time(
self.hass, lambda x: runs.append(1), birthday_paulus)
unsub()
self._send_time_changed(after_birthday)
self.hass.pool.block_till_done()
self.assertEqual(2, len(runs))
def test_track_time_change(self): def test_track_time_change(self):
"""Test tracking time change.""" """Test tracking time change."""
wildcard_runs = [] wildcard_runs = []
specific_runs = [] specific_runs = []
track_time_change(self.hass, lambda x: wildcard_runs.append(1)) unsub = track_time_change(self.hass, lambda x: wildcard_runs.append(1))
track_utc_time_change( unsub_utc = track_utc_time_change(
self.hass, lambda x: specific_runs.append(1), second=[0, 30]) self.hass, lambda x: specific_runs.append(1), second=[0, 30])
self._send_time_changed(datetime(2014, 5, 24, 12, 0, 0)) self._send_time_changed(datetime(2014, 5, 24, 12, 0, 0))
@ -89,6 +97,14 @@ class TestEventHelpers(unittest.TestCase):
self.assertEqual(2, len(specific_runs)) self.assertEqual(2, len(specific_runs))
self.assertEqual(3, len(wildcard_runs)) self.assertEqual(3, len(wildcard_runs))
unsub()
unsub_utc()
self._send_time_changed(datetime(2014, 5, 24, 12, 0, 30))
self.hass.pool.block_till_done()
self.assertEqual(2, len(specific_runs))
self.assertEqual(3, len(wildcard_runs))
def test_track_state_change(self): def test_track_state_change(self):
"""Test track_state_change.""" """Test track_state_change."""
# 2 lists to track how often our callbacks get called # 2 lists to track how often our callbacks get called
@ -186,11 +202,12 @@ class TestEventHelpers(unittest.TestCase):
# Track sunrise # Track sunrise
runs = [] runs = []
track_sunrise(self.hass, lambda: runs.append(1)) unsub = track_sunrise(self.hass, lambda: runs.append(1))
offset_runs = [] offset_runs = []
offset = timedelta(minutes=30) offset = timedelta(minutes=30)
track_sunrise(self.hass, lambda: offset_runs.append(1), offset) unsub2 = track_sunrise(self.hass, lambda: offset_runs.append(1),
offset)
# run tests # run tests
self._send_time_changed(next_rising - offset) self._send_time_changed(next_rising - offset)
@ -208,6 +225,14 @@ class TestEventHelpers(unittest.TestCase):
self.assertEqual(2, len(runs)) self.assertEqual(2, len(runs))
self.assertEqual(1, len(offset_runs)) self.assertEqual(1, len(offset_runs))
unsub()
unsub2()
self._send_time_changed(next_rising + offset)
self.hass.pool.block_till_done()
self.assertEqual(2, len(runs))
self.assertEqual(1, len(offset_runs))
def test_track_sunset(self): def test_track_sunset(self):
"""Test track the sunset.""" """Test track the sunset."""
latitude = 32.87336 latitude = 32.87336
@ -232,11 +257,11 @@ class TestEventHelpers(unittest.TestCase):
# Track sunset # Track sunset
runs = [] runs = []
track_sunset(self.hass, lambda: runs.append(1)) unsub = track_sunset(self.hass, lambda: runs.append(1))
offset_runs = [] offset_runs = []
offset = timedelta(minutes=30) offset = timedelta(minutes=30)
track_sunset(self.hass, lambda: offset_runs.append(1), offset) unsub2 = track_sunset(self.hass, lambda: offset_runs.append(1), offset)
# Run tests # Run tests
self._send_time_changed(next_setting - offset) self._send_time_changed(next_setting - offset)
@ -254,6 +279,14 @@ class TestEventHelpers(unittest.TestCase):
self.assertEqual(2, len(runs)) self.assertEqual(2, len(runs))
self.assertEqual(1, len(offset_runs)) self.assertEqual(1, len(offset_runs))
unsub()
unsub2()
self._send_time_changed(next_setting + offset)
self.hass.pool.block_till_done()
self.assertEqual(2, len(runs))
self.assertEqual(1, len(offset_runs))
def _send_time_changed(self, now): def _send_time_changed(self, now):
"""Send a time changed event.""" """Send a time changed event."""
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {ha.ATTR_NOW: now}) self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {ha.ATTR_NOW: now})
@ -262,7 +295,7 @@ class TestEventHelpers(unittest.TestCase):
"""Test periodic tasks per minute.""" """Test periodic tasks per minute."""
specific_runs = [] specific_runs = []
track_utc_time_change( unsub = track_utc_time_change(
self.hass, lambda x: specific_runs.append(1), minute='/5') self.hass, lambda x: specific_runs.append(1), minute='/5')
self._send_time_changed(datetime(2014, 5, 24, 12, 0, 0)) self._send_time_changed(datetime(2014, 5, 24, 12, 0, 0))
@ -277,11 +310,17 @@ class TestEventHelpers(unittest.TestCase):
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertEqual(2, len(specific_runs)) self.assertEqual(2, len(specific_runs))
unsub()
self._send_time_changed(datetime(2014, 5, 24, 12, 5, 0))
self.hass.pool.block_till_done()
self.assertEqual(2, len(specific_runs))
def test_periodic_task_hour(self): def test_periodic_task_hour(self):
"""Test periodic tasks per hour.""" """Test periodic tasks per hour."""
specific_runs = [] specific_runs = []
track_utc_time_change( unsub = track_utc_time_change(
self.hass, lambda x: specific_runs.append(1), hour='/2') self.hass, lambda x: specific_runs.append(1), hour='/2')
self._send_time_changed(datetime(2014, 5, 24, 22, 0, 0)) self._send_time_changed(datetime(2014, 5, 24, 22, 0, 0))
@ -304,11 +343,17 @@ class TestEventHelpers(unittest.TestCase):
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertEqual(3, len(specific_runs)) self.assertEqual(3, len(specific_runs))
unsub()
self._send_time_changed(datetime(2014, 5, 25, 2, 0, 0))
self.hass.pool.block_till_done()
self.assertEqual(3, len(specific_runs))
def test_periodic_task_day(self): def test_periodic_task_day(self):
"""Test periodic tasks per day.""" """Test periodic tasks per day."""
specific_runs = [] specific_runs = []
track_utc_time_change( unsub = track_utc_time_change(
self.hass, lambda x: specific_runs.append(1), day='/2') self.hass, lambda x: specific_runs.append(1), day='/2')
self._send_time_changed(datetime(2014, 5, 2, 0, 0, 0)) self._send_time_changed(datetime(2014, 5, 2, 0, 0, 0))
@ -323,11 +368,17 @@ class TestEventHelpers(unittest.TestCase):
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertEqual(2, len(specific_runs)) self.assertEqual(2, len(specific_runs))
unsub()
self._send_time_changed(datetime(2014, 5, 4, 0, 0, 0))
self.hass.pool.block_till_done()
self.assertEqual(2, len(specific_runs))
def test_periodic_task_year(self): def test_periodic_task_year(self):
"""Test periodic tasks per year.""" """Test periodic tasks per year."""
specific_runs = [] specific_runs = []
track_utc_time_change( unsub = track_utc_time_change(
self.hass, lambda x: specific_runs.append(1), year='/2') self.hass, lambda x: specific_runs.append(1), year='/2')
self._send_time_changed(datetime(2014, 5, 2, 0, 0, 0)) self._send_time_changed(datetime(2014, 5, 2, 0, 0, 0))
@ -342,6 +393,12 @@ class TestEventHelpers(unittest.TestCase):
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertEqual(2, len(specific_runs)) self.assertEqual(2, len(specific_runs))
unsub()
self._send_time_changed(datetime(2016, 5, 2, 0, 0, 0))
self.hass.pool.block_till_done()
self.assertEqual(2, len(specific_runs))
def test_periodic_task_wrong_input(self): def test_periodic_task_wrong_input(self):
"""Test periodic tasks with wrong input.""" """Test periodic tasks with wrong input."""
specific_runs = [] specific_runs = []

View File

@ -175,6 +175,29 @@ class TestEventBus(unittest.TestCase):
# Try deleting listener while category doesn't exist either # Try deleting listener while category doesn't exist either
self.bus.remove_listener('test', listener) self.bus.remove_listener('test', listener)
def test_unsubscribe_listener(self):
"""Test unsubscribe listener from returned function."""
self.bus._pool.add_worker()
calls = []
def listener(event):
"""Mock listener."""
calls.append(event)
unsub = self.bus.listen('test', listener)
self.bus.fire('test')
self.bus._pool.block_till_done()
assert len(calls) == 1
unsub()
self.bus.fire('event')
self.bus._pool.block_till_done()
assert len(calls) == 1
def test_listen_once_event(self): def test_listen_once_event(self):
"""Test listen_once_event method.""" """Test listen_once_event method."""
runs = [] runs = []