EventBus can now report overview of events that have listeners.

This commit is contained in:
Paulus Schoutsen 2013-11-01 12:28:18 -07:00
parent 63a1dfc64f
commit a60f6754aa
4 changed files with 67 additions and 16 deletions

View File

@ -124,9 +124,16 @@ class EventBus(object):
""" Class that allows code to listen for- and fire events. """
def __init__(self):
self.listeners = defaultdict(list)
self._listeners = defaultdict(list)
self.logger = logging.getLogger(__name__)
@property
def listeners(self):
""" List of events that is being listened for. """
return { key: len(self._listeners[key])
for key in self._listeners.keys()
if len(self._listeners[key]) > 0 }
def fire(self, event_type, event_data=None):
""" Fire an event. """
@ -142,8 +149,8 @@ class EventBus(object):
# We do not use itertools.chain() because some listeners might
# choose to remove themselves as a listener while being executed
for listener in self.listeners[ALL_EVENTS] + \
self.listeners[event.event_type]:
for listener in self._listeners[ALL_EVENTS] + \
self._listeners[event.event_type]:
try:
listener(event)
@ -159,7 +166,7 @@ class EventBus(object):
To listen to all events specify the constant ``ALL_EVENTS``
as event_type.
"""
self.listeners[event_type].append(listener)
self._listeners[event_type].append(listener)
def listen_once(self, event_type, listener):
""" Listen once for event of a specific type.
@ -181,10 +188,10 @@ class EventBus(object):
def remove_listener(self, event_type, listener):
""" Removes a listener of a specific event_type. """
try:
self.listeners[event_type].remove(listener)
self._listeners[event_type].remove(listener)
if len(self.listeners[event_type]) == 0:
del self.listeners[event_type]
if len(self._listeners[event_type]) == 0:
del self._listeners[event_type]
except ValueError:
pass

View File

@ -144,6 +144,7 @@ class RequestHandler(BaseHTTPRequestHandler):
'_handle_post_states_category'),
# /events
('GET', '/events', '_handle_get_events'),
('POST', re.compile(r'/events/(?P<event_type>\w+)'),
'_handle_post_events_event_type')
]
@ -300,11 +301,8 @@ class RequestHandler(BaseHTTPRequestHandler):
# Describe event bus:
write(("<table><tr><th>Event</th><th>Listeners</th></tr>"))
for category in sorted(self.server.eventbus.listeners,
key=lambda key: key.lower()):
write("<tr><td>{}</td><td>{}</td></tr>".
format(category,
len(self.server.eventbus.listeners[category])))
for event_type, count in sorted(self.server.eventbus.listeners.items()):
write("<tr><td>{}</td><td>{}</td></tr>".format(event_type, count))
# Form to allow firing events
write("</table>")
@ -368,6 +366,10 @@ class RequestHandler(BaseHTTPRequestHandler):
self._message("Invalid JSON for attributes",
HTTP_UNPROCESSABLE_ENTITY)
def _handle_get_events(self, path_match, data):
""" Handles getting overview of event listeners. """
self._write_json({'listeners': self.server.eventbus.listeners})
def _handle_post_events_event_type(self, path_match, data):
""" Handles firing of an event. """
event_type = path_match.group('event_type')

View File

@ -55,6 +55,35 @@ class EventBus(ha.EventBus):
self.logger = logging.getLogger(__name__)
@property
def listeners(self):
""" List of events that is being listened for. """
try:
req = self._call_api(METHOD_GET, hah.URL_API_EVENTS)
if req.status_code == 200:
data = req.json()
return data['listeners']
else:
raise ha.HomeAssistantException(
"Got unexpected result (3): {}.".format(req.text))
except requests.exceptions.ConnectionError:
self.logger.exception("EventBus:Error connecting to server")
raise ha.HomeAssistantException("Error connecting to server")
except ValueError: # If req.json() can't parse the json
self.logger.exception("EventBus:Got unexpected result")
raise ha.HomeAssistantException(
"Got unexpected result: {}".format(req.text))
except KeyError: # If not all expected keys are in the returned JSON
self.logger.exception("EventBus:Got unexpected result (2)")
raise ha.HomeAssistantException(
"Got unexpected result (2): {}".format(req.text))
def fire(self, event_type, event_data=None):
""" Fire an event. """

View File

@ -34,6 +34,7 @@ def ensure_homeassistant_started():
core = {'eventbus': ha.EventBus()}
core['statemachine'] = ha.StateMachine(core['eventbus'])
core['eventbus'].listen('test_event', len)
core['statemachine'].set_state('test','a_state')
hah.HTTPInterface(core['eventbus'], core['statemachine'],
@ -83,14 +84,14 @@ class TestHTTPInterface(unittest.TestCase):
def test_api_password(self):
""" Test if we get access denied if we omit or provide
a wrong api password. """
req = requests.post(
req = requests.get(
_url(hah.URL_API_STATES_CATEGORY.format("test")))
self.assertEqual(req.status_code, 401)
req = requests.post(
req = requests.get(
_url(hah.URL_API_STATES_CATEGORY.format("test")),
data={"api_password":"not the password"})
params={"api_password":"not the password"})
self.assertEqual(req.status_code, 401)
@ -125,7 +126,7 @@ class TestHTTPInterface(unittest.TestCase):
""" Test if the debug interface allows us to get a state. """
req = requests.get(
_url(hah.URL_API_STATES_CATEGORY.format("does_not_exist")),
data={"api_password":API_PASSWORD})
params={"api_password":API_PASSWORD})
self.assertEqual(req.status_code, 422)
@ -227,6 +228,15 @@ class TestHTTPInterface(unittest.TestCase):
self.assertEqual(req.status_code, 422)
self.assertEqual(len(test_value), 0)
def test_api_get_event_listeners(self):
""" Test if we can get the list of events being listened for. """
req = requests.get(_url(hah.URL_API_EVENTS),
params={"api_password":API_PASSWORD})
data = req.json()
self.assertEqual(data['listeners'], self.eventbus.listeners)
class TestRemote(unittest.TestCase):
""" Test the homeassistant.remote module. """
@ -273,6 +283,9 @@ class TestRemote(unittest.TestCase):
self.assertEqual(state['state'], "set_remotely")
self.assertEqual(state['attributes']['test'], 1)
def test_remote_eb_listening_for_same(self):
""" Test if remote EB correctly reports listener overview. """
self.assertEqual(self.eventbus.listeners, self.remote_eb.listeners)
# pylint: disable=invalid-name
def test_remote_eb_fire_event_with_no_data(self):