hass-core/app/EventBus.py
2013-09-21 17:59:31 -07:00

71 lines
2.2 KiB
Python

import logging
from collections import defaultdict
from itertools import chain
from threading import Thread, RLock
ALL_EVENTS = '*'
class EventBus(object):
""" Class provides an eventbus. Allows code to listen for events and fire them. """
def __init__(self):
self.listeners = defaultdict(list)
self.lock = RLock()
self.logger = logging.getLogger(__name__)
def fire(self, event):
""" Fire an event. """
assert isinstance(event, Event), "event needs to be an instance of Event"
# We dont want the eventbus to be blocking,
# We dont want the eventbus to crash when one of its listeners throws an Exception
# So run in a thread
def run():
self.lock.acquire()
self.logger.info("Event {}: {}".format(event.event_type, event.data))
for callback in chain(self.listeners[ALL_EVENTS], self.listeners[event.event_type]):
callback(event)
if event.remove_listener:
if callback in self.listeners[ALL_EVENTS]:
self.listeners[ALL_EVENTS].remove(callback)
if callback in self.listeners[event.event_type]:
self.listeners[event.event_type].remove(callback)
event.remove_listener = False
if event.stop_propegating:
break
self.lock.release()
Thread(target=run).start()
def listen(self, event_type, callback):
""" Listen for all events or events of a specific type.
To listen to all events specify the constant ``ALL_EVENTS`` as event_type. """
self.lock.acquire()
self.listeners[event_type].append(callback)
self.logger.info("New listener for event {}. Total: {}".format(event_type, len(self.listeners[event_type])))
self.lock.release()
class Event(object):
""" An event to be sent over the eventbus. """
def __init__(self, event_type, data):
self.event_type = event_type
self.data = data
self.stop_propegating = False
self.remove_listener = False
def __str__(self):
return str([self.event_type, self.data])