def test_emit_return(): ee = EventEmitter() # make sure emission without callback retruns False nt.assert_false(ee.emit('data')) # add a callback ee.on('data')(lambda: None) # should return true now nt.assert_true(ee.emit('data'))
class DummyEmitter(object): """Dummy emitter for testing.""" __emitter = None def __init__(self): """ctor.""" self.__emitter = EventEmitter() def on(self, evt, callback): """Register event handler.""" self.__emitter.on(evt, callback) def emit(self, evt, args): """Fire event.""" self.__emitter.emit(evt, args) def on_pin_state_change(self, psce): """Fire the pin state change event.""" self.emit("gpioStateChanged", psce) def trigger_event(self): """Trigger the state change event.""" pin_address = gpio_pins.Gpio01.value old_state = pin_state.LOW new_state = pin_state.HIGH evt = PinStateChangeEvent(old_state, new_state, pin_address) self.on_pin_state_change(evt)
def test_listener_removal(): """Removing listeners removes the correct listener from an event.""" ee = EventEmitter() # Some functions to pass to the EE def first(): return 1 ee.on('event', first) @ee.on('event') def second(): return 2 @ee.on('event') def third(): return 3 def fourth(): return 4 ee.on('event', fourth) assert ee._events['event'] == [first, second, third, fourth] ee.remove_listener('event', second) assert ee._events['event'] == [first, third, fourth] ee.remove_listener('event', first) assert ee._events['event'] == [third, fourth] ee.remove_all_listeners('event') assert ee._events['event'] == []
class DummyEmitter(object): """Dummy emitter for testing.""" __emitter = None __evt = None __pollThread = None def __init__(self): """ctor.""" self.__emitter = EventEmitter() def on(self, evt, callback): """Register event handler.""" self.__emitter.on(evt, callback) def emit(self, evt, args): """Fire event.""" self.__emitter.emit(evt, args) def on_poll_fail(self): """Fire pin poll faiure event.""" self.emit("pinPollFailed", self.__evt) def poll(self): """Execute pin polling on background thread.""" ioEx = IOException("Poll failed.") self.__evt = PinPollFailEvent(ioEx) self.__pollThread = threading.Thread(target=self.on_poll_fail) self.__pollThread.name = "DummyEmitterThread" self.__pollThread.daemon = True self.__pollThread.start()
class InterceptEmitter(object): """ This class intercepts and allows emitting events between the skill_tester and the skill being tested. When a test is running emitted communication is intercepted for analysis """ def __init__(self): self.emitter = EventEmitter() self.q = None def on(self, event, f): # run all events print("Event: ", event) self.emitter.on(event, f) def emit(self, event, *args, **kwargs): event_name = event.type if self.q: self.q.put(event) self.emitter.emit(event_name, event, *args, **kwargs) def once(self, event, f): self.emitter.once(event, f) def remove(self, event_name, func): pass def remove_all_listeners(self, event_name): pass
class RegistrationOnlyEmitter(object): def __init__(self): self.emitter = EventEmitter() def on(self, event, f): allow_events_to_execute = True if allow_events_to_execute: # don't filter events, just run them all print "Event: "+str(event) self.emitter.on(event, f) else: # filter to just the registration events, # preventing them from actually executing if event in [ 'register_intent', 'register_vocab', 'recognizer_loop:utterance' ]: print "Event: " + str(event) self.emitter.on(event, f) def emit(self, event, *args, **kwargs): event_name = event.type self.emitter.emit(event_name, event, *args, **kwargs) def once(self, event, f): self.emitter.once(event, f) def remove(self, event_name, func): pass
class InterceptEmitter(object): """ This class intercepts and allows emitting events between the skill_tester and the skill being tested. When a test is running emitted communication is intercepted for analysis """ def __init__(self): self.emitter = EventEmitter() self.q = None def on(self, event, f): # run all events print("Event: ", event) self.emitter.on(event, f) def emit(self, event, *args, **kwargs): event_name = event.type if self.q: self.q.put(event) self.emitter.emit(event_name, event, *args, **kwargs) def once(self, event, f): self.emitter.once(event, f) def remove(self, event_name, func): pass def remove_all_listeners(self, event_name): pass
class RegistrationOnlyEmitter(object): def __init__(self): self.emitter = EventEmitter() def on(self, event, f): allow_events_to_execute = True if allow_events_to_execute: # don't filter events, just run them all print "Event: " + str(event) self.emitter.on(event, f) else: # filter to just the registration events, # preventing them from actually executing if event in [ 'register_intent', 'register_vocab', 'recognizer_loop:utterance' ]: print "Event: " + str(event) self.emitter.on(event, f) def emit(self, event, *args, **kwargs): event_name = event.type self.emitter.emit(event_name, event, *args, **kwargs) def once(self, event, f): self.emitter.once(event, f) def remove(self, event_name, func): pass
class Camera() running = True def __init__(self,fps=24): self.emitter = EventEmitter() self.fps = fps self.cap = cv2.VideoCapture(0) self.thread = threading.Thread(target=self.run) self.thread.daemon = True self.thread.start() def run(self): capturePeriodSecs = 1.0 / self.fps while self.Running == True: try: self.cap_frame() except Exception as error: print ("error with capturing frames: ", error) time.sleep(capturePeriodSecs) def onNewFrame(self,callback): self.emitter.on('frame', callback) def removeOnNewFrameListener(self,callback): self.emitter.remove_emitter_listener('frame', callback) def stop(self): self.running = False def cap_frame(self): f,img = self.cap.read() self.latest_frame = img self.emitter.emit('frame', img)
def addEventListener(emitter: EventEmitter, eventName: str, handler: Callable) -> Dict[(str, Any)]: 'Add handler to the emitter and return emitter/handler.' emitter.on(eventName, handler) return { 'emitter': emitter, 'eventName': eventName, 'handler': handler, }
class RegistrationOnlyEmitter(object): def __init__(self): self.emitter = EventEmitter() def on(self, event, f): if event in ['register_intent', 'register_vocab', 'recognizer_loop:utterance']: self.emitter.on(event, f) def emit(self, event, *args, **kwargs): event_name = event.message_type self.emitter.emit(event_name, event, *args, **kwargs)
def test_emit_return(): ee = EventEmitter() # make sure emitting without a callback returns False nt.assert_false(ee.emit('data')) # add a callback ee.on('data')(lambda: None) # should return True now nt.assert_true(ee.emit('data'))
def test_proxy_new_listener(proxy_new_listener): call_me = Mock() base_ee = EventEmitter() uplifted_ee = uplift(UpliftedEventEmitter, base_ee, proxy_new_listener=proxy_new_listener) @base_ee.on('new_listener') def base_new_listener_handler(event, f): assert event in ('event', 'new_listener') call_me('base new listener handler', f) @uplifted_ee.on('new_listener') def uplifted_new_listener_handler(event, f): assert event in ('event', 'new_listener') call_me('uplifted new listener handler', f) def fresh_base_handler(): pass def fresh_uplifted_handler(): pass base_ee.on('event', fresh_base_handler) uplifted_ee.on('event', fresh_uplifted_handler) if proxy_new_listener == 'both': call_me.assert_has_calls([ call('base new listener handler', fresh_base_handler), call('uplifted new listener handler', fresh_base_handler), call('uplifted new listener handler', fresh_uplifted_handler), call('base new listener handler', fresh_uplifted_handler) ]) elif proxy_new_listener == 'neither': call_me.assert_has_calls([ call('base new listener handler', fresh_base_handler), call('uplifted new listener handler', fresh_uplifted_handler) ]) elif proxy_new_listener == 'forward': call_me.assert_has_calls([ call('base new listener handler', fresh_base_handler), call('uplifted new listener handler', fresh_base_handler), call('uplifted new listener handler', fresh_uplifted_handler) ]) elif proxy_new_listener == 'backward': call_me.assert_has_calls([ call('base new listener handler', fresh_base_handler), call('uplifted new listener handler', fresh_uplifted_handler), call('base new listener handler', fresh_uplifted_handler) ]) else: raise Exception('unrecognized proxy_new_listener')
def wait_for_event( self, emitter: EventEmitter, event: str, predicate: Callable = None, ) -> None: def listener(event_data: Any = None) -> None: if not predicate or predicate(event_data): self._fulfill(event_data) emitter.on(event, listener) self._registered_listeners.append((emitter, event, listener))
def wait_for_event_future( emitter: EventEmitter, event: str, predicate: Callable[[Any], bool] = None ) -> asyncio.Future: future: asyncio.Future = asyncio.Future() def listener(event_data: Any = None) -> None: if not predicate or predicate(event_data): future.set_result(event_data) emitter.on(event, listener) future.add_done_callback(lambda f: emitter.remove_listener(event, listener)) return future
def reject_on_event( self, emitter: EventEmitter, event: str, error: Error, predicate: Callable = None, ) -> None: def listener(event_data: Any = None) -> None: if not predicate or predicate(event_data): self._reject(error) emitter.on(event, listener) self._registered_listeners.append((emitter, event, listener))
class RegistrationOnlyEmitter(object): def __init__(self): self.emitter = EventEmitter() def on(self, event, f): if event in [ 'register_intent', 'register_vocab', 'recognizer_loop:utterance' ]: self.emitter.on(event, f) def emit(self, event, *args, **kwargs): event_name = event.message_type self.emitter.emit(event_name, event, *args, **kwargs)
def test_new_listener_event(): """The 'new_listener' event fires whenever a new listerner is added.""" call_me = Mock() ee = EventEmitter() ee.on('new_listener', call_me) # Should fire new_listener event @ee.on('event') def event_handler(data): pass call_me.assert_called_once()
def test_listener_removal_on_emit(): """Test that a listener removed during an emit is called inside the current emit cycle. """ call_me = Mock() ee = EventEmitter() def should_remove(): ee.remove_listener('remove', call_me) ee.on('remove', should_remove) ee.on('remove', call_me) ee.emit('remove') call_me.assert_called_once() call_me.reset_mock() # Also test with the listeners added in the opposite order ee = EventEmitter() ee.on('remove', call_me) ee.on('remove', should_remove) ee.emit('remove') call_me.assert_called_once()
def test_emit_return(): """Emit returns True when handlers are registered on an event, and false otherwise. """ call_me = Mock() ee = EventEmitter() # make sure emitting without a callback returns False assert not ee.emit('data') # add a callback ee.on('data')(call_me) # should return True now assert ee.emit('data')
class MessageBusEventHandler(WebSocketHandler): def __init__(self, application, request, **kwargs): super().__init__(application, request, **kwargs) self.emitter = EventEmitter() def on(self, event_name, handler): self.emitter.on(event_name, handler) def on_message(self, message): LOG.debug(message) try: deserialized_message = Message.deserialize(message) except Exception: return try: self.emitter.emit(deserialized_message.msg_type, deserialized_message) except Exception as e: LOG.exception(e) traceback.print_exc(file=sys.stdout) pass for client in client_connections: client.write_message(message) def open(self): self.write_message(Message("connected").serialize()) client_connections.append(self) def on_close(self): client_connections.remove(self) def emit(self, channel_message): if (hasattr(channel_message, 'serialize') and callable(getattr(channel_message, 'serialize'))): self.write_message(channel_message.serialize()) else: self.write_message(json.dumps(channel_message)) def check_origin(self, origin): return True
class InterceptEmitter(object): """ This class intercepts and allows emitting events between the skill_tester and the skill being tested. When a test is running emitted communication is intercepted for analysis """ def __init__(self): self.emitter = EventEmitter() self.q = None def on(self, event, f): # run all events print("Event: ", event) self.emitter.on(event, f) def emit(self, event, *args, **kwargs): event_name = event.msg_type if self.q: self.q.put(event) self.emitter.emit(event_name, event, *args, **kwargs) def wait_for_response(self, event, reply_type=None, *args, **kwargs): """Simple single thread implementation of wait_for_response.""" message_type = reply_type or event.msg_type + '.response' response = None def response_handler(msg): nonlocal response response = msg self.emitter.once(message_type, response_handler) self.emitter.emit(event.msg_type, event) return response def once(self, event, f): self.emitter.once(event, f) def remove(self, event_name, func): pass def remove_all_listeners(self, event_name): pass
class MockWebsocket(): def __init__(self): self.events = EventEmitter(scheduler=asyncio.ensure_future) self.saved_items = [] self.emitted_items = [] def on(self, *args, **kwargs): self.events.on(*args, **kwargs) def once(self, *args, **kwargs): self.events.once(*args, **kwargs) def _emit(self, event, *args, **kwargs): # save published items for testing self.emitted_items += [{ 'time': int(round(time.time() * 1000)), 'data': { 'event': event, 'args': args, 'kwargs': kwargs } }] self.events.emit(event, *args, **kwargs) def remove_all_listeners(self, *args, **kwargs): self.events.remove_all_listeners(*args, **kwargs) def cancel_order(self, *args, **kawargs): pass def submit_order(self, *args, **kawargs): pass def get_emitted_items(self): return self.emitted_items def get_last_emitted_item(self): return self.emitted_items[-1:][0] def get_emitted_items_count(self): return len(self.emitted_items)
class Sensor(Component): """A sensor abstraction component interface.""" def __init__(self, pin): """Initialize a new instance of Sensor. :param raspy.io.gpio.Gpio pin: The input pin to sample data from. :raises: ArgumentNullException if 'pin' param is None. """ Component.__init__(self) if pin is None: raise ArgumentNullException("'pin' param cannot be None.") self.__emitter = EventEmitter() self.__state = sensor_state.OPEN self.__pin = pin self.__pin.provision() def dispose(self): """Release managed resources used by this component.""" if self.is_disposed: return if self.__pin is not None: self.__pin.dispose() self.__pin = None self.__state = sensor_state.OPEN self.__emitter.remove_all_listeners() self.__emitter = None Component.dispose(self) def on(self, evt, callback): """Register an event with a callback to handle it. :param str evt: The name of the event to register a handler for. :param function callback: The callback to execute when the event fires. :raises: raspy.object_disposed_exception.ObjectDisposedException if this instance is disposed. """ if self.is_disposed: raise ObjectDisposedException("Sensor") self.__emitter.on(evt, callback) def emit(self, evt, args): """Emit the specified event to all registered listeners. :param str evt: The name of the event to emit. :param object args: The arguments to pass to the event handlers (listeners). :raises: raspy.object_disposed_exception.ObjectDisposedException if this instance is disposed. """ if self.is_disposed: raise ObjectDisposedException("Sensor") self.__emitter.emit(evt, args) def remove_all_listeners(self): """Remove all registered event listeners.""" if self.is_disposed: return if self.__emitter is not None: self.__emitter.remove_all_listeners() def on_sensor_state_change(self, change_evt): """Fire the sensor state change event. :param sensor_state_change_event.SensorStateChangeEvent change_evt: The state change event object. :raises: ObjectDisposedException if this instance has been disposed. """ if self.is_disposed: raise ObjectDisposedException("Sensor") _t = threading.Thread(target=self.emit, name=EVENT_STATE_CHANGED, args=(EVENT_STATE_CHANGED, change_evt)) _t.daemon = True _t.start() @property def state(self): """Get the state of the sensor. :returns: The sensor state. :rtype: int """ return self.__state def is_state(self, sens_state): """Check to see if the sensor is in the specified state. :param int sens_state: The state to check. :returns: True if in the specified state. :rtype: bool """ return self.state == sens_state @property def is_open(self): """Get a value indicating whether this is sensor is open. :returns: True if open; Otherwise, False. :rtype: bool """ return self.is_state(sensor_state.OPEN) @property def is_closed(self): """Get a value indicating whether this sensor is closed. :returns: True if closed; Otherwise, False. :rtype: bool """ return self.is_state(sensor_state.CLOSED) @property def pin(self): """Get the pin being used to sample sensor data. :returns: The underlying physical pin. :type: raspy.io.gpio.Gpio """ return self.__pin
class WebsocketClient(object): def __init__(self, host=None, port=None, route=None, ssl=None): config = Configuration.get().get("websocket") host = host or config.get("host") port = port or config.get("port") route = route or config.get("route") ssl = ssl or config.get("ssl") validate_param(host, "websocket.host") validate_param(port, "websocket.port") validate_param(route, "websocket.route") self.url = WebsocketClient.build_url(host, port, route, ssl) self.emitter = EventEmitter() self.client = self.create_client() self.pool = ThreadPool(10) self.retry = 5 @staticmethod def build_url(host, port, route, ssl): scheme = "wss" if ssl else "ws" return scheme + "://" + host + ":" + str(port) + route def create_client(self): return WebSocketApp(self.url, on_open=self.on_open, on_close=self.on_close, on_error=self.on_error, on_message=self.on_message) def on_open(self, ws): LOG.info("Connected") self.emitter.emit("open") # Restore reconnect timer to 5 seconds on sucessful connect self.retry = 5 def on_close(self, ws): self.emitter.emit("close") def on_error(self, ws, error): try: self.emitter.emit('error', error) self.client.close() except Exception as e: LOG.error(repr(e)) LOG.warning("WS Client will reconnect in %d seconds." % self.retry) time.sleep(self.retry) self.retry = min(self.retry * 2, 60) self.client = self.create_client() self.run_forever() def on_message(self, ws, message): self.emitter.emit('message', message) parsed_message = Message.deserialize(message) self.pool.apply_async( self.emitter.emit, (parsed_message.type, parsed_message)) def emit(self, message): if (not self.client or not self.client.sock or not self.client.sock.connected): return if hasattr(message, 'serialize'): self.client.send(message.serialize()) else: self.client.send(json.dumps(message.__dict__)) def on(self, event_name, func): self.emitter.on(event_name, func) def once(self, event_name, func): self.emitter.once(event_name, func) def remove(self, event_name, func): self.emitter.remove_listener(event_name, func) def remove_all_listeners(self, event_name): ''' Remove all listeners connected to event_name. Args: event_name: event from which to remove listeners ''' if event_name is None: raise ValueError self.emitter.remove_all_listeners(event_name) def run_forever(self): self.client.run_forever() def close(self): self.client.close()
class WebsocketClient(object): def __init__(self, host=None, port=None, route=None, ssl=None): config = Configuration.get().get("websocket") host = host or config.get("host") port = port or config.get("port") route = route or config.get("route") ssl = ssl or config.get("ssl") validate_param(host, "websocket.host") validate_param(port, "websocket.port") validate_param(route, "websocket.route") self.url = WebsocketClient.build_url(host, port, route, ssl) self.emitter = EventEmitter() self.client = self.create_client() self.pool = ThreadPool(10) self.retry = 5 self.connected_event = Event() self.started_running = False @staticmethod def build_url(host, port, route, ssl): scheme = "wss" if ssl else "ws" return scheme + "://" + host + ":" + str(port) + route def create_client(self): return WebSocketApp(self.url, on_open=self.on_open, on_close=self.on_close, on_error=self.on_error, on_message=self.on_message) def on_open(self, ws): LOG.info("Connected") self.connected_event.set() self.emitter.emit("open") # Restore reconnect timer to 5 seconds on sucessful connect self.retry = 5 def on_close(self, ws): self.emitter.emit("close") def on_error(self, ws, error): try: self.emitter.emit('error', error) self.client.close() except Exception as e: LOG.error(repr(e)) LOG.warning("WS Client will reconnect in %d seconds." % self.retry) time.sleep(self.retry) self.retry = min(self.retry * 2, 60) self.client = self.create_client() self.run_forever() def on_message(self, ws, message): self.emitter.emit('message', message) parsed_message = Message.deserialize(message) self.pool.apply_async( self.emitter.emit, (parsed_message.type, parsed_message)) def emit(self, message): if not self.connected_event.wait(10): if not self.started_running: raise ValueError('You must execute run_forever() ' 'before emitting messages') self.connected_event.wait() if hasattr(message, 'serialize'): self.client.send(message.serialize()) else: self.client.send(json.dumps(message.__dict__)) def wait_for_response(self, message, reply_type=None, timeout=None): """Send a message and wait for a response. Args: message (Message): message to send reply_type (str): the message type of the expected reply. Defaults to "<message.type>.response". timeout: seconds to wait before timeout, defaults to 3 Returns: The received message or None if the response timed out """ response = [] def handler(message): """Receive response data.""" response.append(message) # Setup response handler self.once(reply_type or message.type + '.response', handler) # Send request self.emit(message) # Wait for response start_time = monotonic.monotonic() while len(response) == 0: time.sleep(0.2) if monotonic.monotonic() - start_time > (timeout or 3.0): try: self.remove(reply_type, handler) except (ValueError, KeyError): # ValueError occurs on pyee 1.0.1 removing handlers # registered with once. # KeyError may theoretically occur if the event occurs as # the handler is removbed pass return None return response[0] def on(self, event_name, func): self.emitter.on(event_name, func) def once(self, event_name, func): self.emitter.once(event_name, func) def remove(self, event_name, func): self.emitter.remove_listener(event_name, func) def remove_all_listeners(self, event_name): ''' Remove all listeners connected to event_name. Args: event_name: event from which to remove listeners ''' if event_name is None: raise ValueError self.emitter.remove_all_listeners(event_name) def run_forever(self): self.started_running = True self.client.run_forever() def close(self): self.client.close() self.connected_event.clear()
class GenericWebsocket: """ Websocket object used to contain the base functionality of a websocket. Inlcudes an event emitter and a standard websocket client. """ def __init__(self, host, logLevel='INFO', loop=None): self.host = host self.logger = CustomLogger('BfxWebsocket', logLevel=logLevel) self.loop = loop or asyncio.get_event_loop() self.events = EventEmitter( scheduler=asyncio.ensure_future, loop=self.loop) self.ws = None def run(self): """ Run the websocket connection indefinitely """ self.loop.run_until_complete(self._main(self.host)) def get_task_executable(self): """ Get the run indefinitely asyncio task """ return self._main(self.host) async def _main(self, host): async with websockets.connect(host) as websocket: self.ws = websocket self.logger.info("Wesocket connectedt to {}".format(self.host)) while True: await asyncio.sleep(0) message = await websocket.recv() await self.on_message(message) def remove_all_listeners(self, event): """ Remove all listeners from event emitter """ self.events.remove_all_listeners(event) def on(self, event, func=None): """ Add a new event to the event emitter """ if not func: return self.events.on(event) self.events.on(event, func) def once(self, event, func=None): """ Add a new event to only fire once to the event emitter """ if not func: return self.events.once(event) self.events.once(event, func) def _emit(self, event, *args, **kwargs): self.events.emit(event, *args, **kwargs) async def on_error(self, error): """ On websocket error print and fire event """ self.logger.error(error) self.events.emit('error', error) async def on_close(self): """ On websocket close print and fire event """ self.logger.info("Websocket closed.") await self.ws.close() self._emit('done') async def on_open(self): """ On websocket open """ pass async def on_message(self, message): """ On websocket message """ pass
class GenericWebsocket: """ Websocket object used to contain the base functionality of a websocket. Inlcudes an event emitter and a standard websocket client. """ def __init__(self, host, logLevel='INFO', loop=None, max_retries=5): self.host = host self.logger = CustomLogger('BfxWebsocket', logLevel=logLevel) self.loop = loop or asyncio.get_event_loop() self.events = EventEmitter(scheduler=asyncio.ensure_future, loop=self.loop) # overide 'error' event to stop it raising an exception # self.events.on('error', self.on_error) self.ws = None self.max_retries = max_retries self.attempt_retry = True def run(self): """ Run the websocket connection indefinitely """ self.loop.run_until_complete(self._main(self.host)) def get_task_executable(self): """ Get the run indefinitely asyncio task """ return self._main(self.host) async def _connect(self, host): async with websockets.connect(host) as websocket: self.ws = websocket self.logger.info("Wesocket connected to {}".format(host)) while True: await asyncio.sleep(0) message = await websocket.recv() await self.on_message(message) def get_ws(self): return self.ws async def _main(self, host): retries = 0 while retries < self.max_retries and self.attempt_retry: try: await self._connect(host) retries = 0 except (ConnectionClosed, socket.error) as e: self._emit('disconnected') if (not self.attempt_retry): return self.logger.error(str(e)) retries += 1 # wait 5 seconds befor retrying self.logger.info("Waiting 5 seconds before retrying...") await asyncio.sleep(5) self.logger.info("Reconnect attempt {}/{}".format( retries, self.max_retries)) self.logger.info("Unable to connect to websocket.") self._emit('stopped') def remove_all_listeners(self, event): """ Remove all listeners from event emitter """ self.events.remove_all_listeners(event) def on(self, event, func=None): """ Add a new event to the event emitter """ if not func: return self.events.on(event) self.events.on(event, func) def once(self, event, func=None): """ Add a new event to only fire once to the event emitter """ if not func: return self.events.once(event) self.events.once(event, func) def _emit(self, event, *args, **kwargs): self.events.emit(event, *args, **kwargs) async def on_error(self, error): """ On websocket error print and fire event """ self.logger.error(error) async def on_close(self): """ On websocket close print and fire event. This is used by the data server. """ self.logger.info("Websocket closed.") self.attempt_retry = False await self.ws.close() self._emit('done') async def on_open(self): """ On websocket open """ pass async def on_message(self, message): """ On websocket message """ pass
class Fireplace(Device): """Fireplace device abstraction interface/base type.""" def __init__(self): """Initialize a new instance of Fireplace.""" Device.__init__(self) self.__emitter = EventEmitter() self.__timeoutDelay = 0 self.__timeoutDelayMillis = 0 self.__timeoutUnit = time_unit.MINUTES self.__taskTimer = None self.__killTimer = None self.__state = fireplace_state.OFF self.__emitter.on(EVENT_STATE_CHANGED, lambda evt: self._internal_state_changed(evt)) def _cancel_timeout_task(self): """Cancel the timeout task (if running).""" if self.__taskTimer is not None: self.__taskTimer.cancel() self.__taskTimer = None def _internal_state_changed(self, evt): """An internal handler for the state change event. :param raspy.devices.fireplaces.fireplace_state_change_event.FireplaceStateChangeEvent evt: The event object. """ if evt: pass self._cancel_timeout_task() def on(self, evt, callback): """Register an event with a callback to handle it. :param str evt: The name of the event to register a handler for. :param function callback: The callback to execute when the event fires. :raises: ObjectDisposedException if this instance is disposed. """ if self.is_disposed: raise ObjectDisposedException("Fireplace") self.__emitter.on(evt, callback) def emit(self, evt, args): """Emit the specified event to all registered listeners. :param str evt: The name of the event to emit. :param object args: The arguments to pass to the event handlers (listeners). :raises: ObjectDisposedException if this instance is disposed. """ if self.is_disposed: raise ObjectDisposedException("Fireplace") self.__emitter.emit(evt, args) def remove_all_listeners(self): """Remove all registered event listeners.""" if self.is_disposed: return if self.__emitter is not None: self.__emitter.remove_all_listeners() def on_state_change(self, evt): """Fire the state change event. :param raspy.devices.fireplaces.fireplace_state_change_event.FireplaceStateChangeEvent evt: The event object. :raises: ObjectDisposedException if this instance has been disposed. """ if self.is_disposed: raise ObjectDisposedException("Fireplace") _t = threading.Thread(target=self.emit, name=EVENT_STATE_CHANGED, args=(EVENT_STATE_CHANGED, evt)) _t.daemon = True _t.start() def on_operation_timeout(self, evt): """Fire the operation timeout event. :param raspy.devices.fireplaces.fireplace_timeout_event.FireplaceTimeoutEvent evt: The event object. """ if self.is_disposed: raise ObjectDisposedException("Fireplace") _t = threading.Thread(target=self.emit, name=EVENT_OPERATION_TIMEOUT, args=(EVENT_OPERATION_TIMEOUT, evt)) _t.daemon = True _t.start() def on_pilot_light_state_change(self, evt): """Fire the pilot light state change event. :param raspy.devices.fireplaces.fireplace_pilot_light_event.FireplacePilotLightEvent evt: The event object. """ if self.is_disposed: raise ObjectDisposedException("Fireplace") _t = threading.Thread(target=self.emit, name=EVENT_PILOT_LIGHT_STATE_CHANGED, args=(EVENT_PILOT_LIGHT_STATE_CHANGED, evt)) _t.daemon = True _t.start() @property def state(self): """Get the fireplace state. :returns: The fireplace state. :rtype: int """ return self.__state @state.setter def state(self, the_state): """Set the fireplace state. :param int the_state: The fireplace state. :raises: .fireplace_pilot_light_exception.FireplacePilotLightException if no pilot light sensor is present. """ self.__state = the_state @property def is_on(self): """Get a flag indicating whether the fireplace is on. :returns: True if the fireplace is on. :rtype: bool """ return self.state == fireplace_state.ON @property def is_off(self): """Get a flag indicating whether the fireplace is off. :returns: True if the fireplace is off. :rtype: bool """ return self.state == fireplace_state.OFF @property def is_pilot_light_on(self): """Get a flag indicating whether the pilot light is on. :returns: True if the pilot light is on. :rtype: bool """ return False @property def is_pilot_light_off(self): """Get a flag indicating whether the pilot light is off. :returns: True if the pilot light is off. :rtype: bool """ return False def get_timeout_delay(self): """Get the timeout delay. :returns: The timeout delay. :type: int """ return self.__timeoutDelay def get_timeout_unit(self): """Get the timeout unit of time. :returns: The time unit being used for the timeout delay. :rtype: int """ return self.__timeoutUnit def set_timeout_delay(self, delay, unit): """Get the timeout delay. :param int delay: The timeout delay. :param int unit: The time unit of measure for the timeout. :raises: raspy.invalid_operation_exception.InvalidOperationException if the fireplace is turned off. """ if self.is_off: msg = "Cannot set timeout when the fireplace is off." raise InvalidOperationException(msg) self.__timeoutDelay = delay self.__timeoutUnit = unit self.cancel_timeout() if self.__timeoutDelay > 0: wait_time = datetime.timedelta() if unit == time_unit.DAYS: wait_time = datetime.timedelta(days=delay) elif unit == time_unit.HOURS: wait_time = datetime.timedelta(hours=delay) elif unit == time_unit.MINUTES: wait_time = datetime.timedelta(minutes=delay) elif unit == time_unit.SECONDS: wait_time = datetime.timedelta(seconds=delay) elif unit == time_unit.MILLISECONDS: wait_time = datetime.timedelta(milliseconds=delay) kill_delay = datetime.timedelta( milliseconds=wait_time.total_seconds()) kill_delay += datetime.timedelta(seconds=1) sec = kill_delay.total_seconds() self.__killTimer = threading.Timer(sec, self._cancel_timeout_task) self.__killTimer.start() self._start_cancel_task() def turn_on(self, timeout_delay, timeout_unit): """Turn the fireplace on with the specified timeout. If the operation is not successful within the allotted time, the operation is cancelled for safety reasons. :param int timeout_delay: The timeout delay. If not specified or less than or equal to zero, then the fireplace is turned on without any safety delay (not recommended). :param int timeout_unit: The time unit of measure for the timeout. If not specified, `time_unit.SECONDS` is assumed. """ self.state = fireplace_state.ON if timeout_unit is None: timeout_unit = self.__timeoutUnit if timeout_delay is not None: if timeout_delay > 0: self.set_timeout_delay(timeout_delay, timeout_unit) def turn_off(self): """Turn the fireplace off.""" self.state = fireplace_state.OFF def _task_action(self): """The action for the background timeout task. This fires the operation timeout event, then turns off the fireplace. """ evt = FireplaceTimeoutEvent() self.on_operation_timeout(evt) if not evt.is_handled: self.turn_off() def _do_task(self): self._task_action() self.__killTimer.cancel() self.__killTimer = None def _start_cancel_task(self): """Start the background cancellation task.""" if self.__killTimer is not None: self.__taskTimer = threading.Timer(self.__timeoutDelayMillis, self._do_task) self.__taskTimer.start() def cancel_timeout(self): """Cancel the timeout.""" self._cancel_timeout_task() def shutdown(self): """Shutdown the fireplace.""" self.cancel_timeout() self.turn_off() def dispose(self): """Release all managed resources used by this component.""" if self.is_disposed: return self._cancel_timeout_task() if self.__killTimer is not None: self.__killTimer.cancel() self.__killTimer = None self.__emitter.remove_all_listeners() self.__emitter = None self.__state = fireplace_state.OFF self.__timeoutDelay = 0 self.__timeoutDelayMillis = 0 Device.dispose(self)
class PiCameraDevice(Device): """An abstraction of the RaspiCam device. RaspiCam is a peripheral camera device designed specifically for use with the Raspberry Pi. This class provides a threaded wrapper around the raspistill utility and thus a means for still capture control. See http://www.raspberrypi.org/wp-content/uploads/2013/07/RaspiCam-Documentation.pdf for instructions on how to install and configure RaspiCam support. """ def __init__(self, settings=None): """Initialize a new instance of PiCameraDevice. :param StillCaptureSettings settings: The image capture settings. """ Device.__init__(self) self.__settings = settings self.__emitter = EventEmitter() self.__isRunning = False self.__processID = -1 self.__exitCode = -1 self.__captureProc = None self.__syncLock = threading.RLock() self.__captureThread = None def on(self, evt, callback): """Register an event with a callback to handle it. :param str evt: The name of the event to register a handler for. :param function callback: The callback to execute when the event fires. :raises: ObjectDisposedException if this instance is disposed. """ if self.is_disposed: raise ObjectDisposedException("PiCameraDevice") self.__emitter.on(evt, callback) def emit(self, evt, args): """Emit the specified event to all registered listeners. :param str evt: The name of the event to emit. :param object args: The arguments to pass to the event handlers (listeners). :raises: ObjectDisposedException if this instance is disposed. """ if self.is_disposed: raise ObjectDisposedException("Gpio") self.__emitter.emit(evt, args) def remove_all_listeners(self): """Remove all registered event listeners.""" if self.is_disposed: return if self.__emitter is not None: self.__emitter.remove_all_listeners() @property def capture_settings(self): """Get the still capture settings. :returns: The still capture settings. :rtype: StillCaptureSettings """ return self.__settings @capture_settings.setter def capture_settings(self, settings): """Set the still capture settings. :param StillCaptureSettings settings: The capture settings. """ self.__settings = settings @property def process_id(self): """Get the process ID. :returns: The ID of the capture process if started; Otherwise, -1. :rtype: int """ return self.__processID @property def is_running(self): """Get whether or not the capture process is running. :returns: True if running. :rtype: bool """ return self.__isRunning @property def exit_code(self): """Get the exit code of the capture process. :returns: The process exit code if terminated normally; Otherwise, -1. """ return self.__exitCode def on_capture_started(self, start_evt): """Fire the capture started event. :param CaptureStartEvent start_evt: The event object. :raises: ObjectDisposedException if this instance has been disposed. """ if self.is_disposed: raise ObjectDisposedException("PiCameraDevice") _t = threading.Thread(target=self.emit, name=EVENT_CAPTURE_START, args=(EVENT_CAPTURE_START, start_evt)) _t.daemon = True _t.start() def on_capture_output_received(self, out_evt): """Fire the capture output event. :param CaptureOutputEvent out_evt: The event object. :raises: ObjectDisposedException if this instance has been disposed. """ if self.is_disposed: raise ObjectDisposedException("PiCameraDevice") _t = threading.Thread(target=self.emit, name=EVENT_CAPTURE_OUTPUT, args=(EVENT_CAPTURE_OUTPUT, out_evt)) _t.daemon = True _t.start() def on_capture_done(self, done_evt): """Fire the capture done event. :param CaptureDoneEvent done_evt: The event object. :raises: ObjectDisposedException if this instance has been disposed. """ if self.is_disposed: raise ObjectDisposedException("PiCameraDevice") _t = threading.Thread(target=self.emit, name=EVENT_CAPTURE_DONE, args=(EVENT_CAPTURE_DONE, done_evt)) _t.daemon = True _t.start() def cancel(self): """Cancel the still capture process, if running. Should emit EVENT_CAPTURE_DONE and include the termination signal. """ if not self.__isRunning: return self.__syncLock.acquire() self.__isRunning = False self.__syncLock.release() core_utils.sleep(500) if self.__captureProc is not None: if self.__captureProc.poll() is not None: try: self.__captureProc.kill() except OSError: # Process probably already died. pass @staticmethod def _enqueue_output(out, queue): """Enqueue output from the process. :param File out: The standard output stream from the process. :param Queue queue: The queue to store the output lines in. """ for line in iter(out.readline, b''): queue.put(line) out.close() def _monitor_capture(self): """Monitor the capture process.""" if self.__settings is None: self.__settings = StillCaptureSettings() args = self.__settings.to_argument_string() cmd = ['raspistill'] + args.split(" ") # start the process and get the PID. on_posix = 'posix' in sys.builtin_module_names self.__captureProc = Popen(cmd, stdout=PIPE, stderr=PIPE, bufsize=1, close_fds=on_posix) read_queue = Queue() enqueue_args = (self.__captureProc.stdout, read_queue) queue_thread = threading.Thread(target=PiCameraDevice._enqueue_output, args=enqueue_args) queue_thread.daemon = True queue_thread.start() self.__syncLock.acquire() self.__processID = self.__captureProc.pid self.__syncLock.release() # Notify listeners that the process started and start signaling output. self.on_capture_started(CaptureStartEvent(self.__processID)) while self.__captureProc.poll() is None: try: line = read_queue.get_nowait() except Empty: pass else: self.on_capture_output_received(CaptureOutputEvent(line)) # The process finished. Get the exit code. self.__syncLock.acquire() self.__exitCode = self.__captureProc.returncode self.__isRunning = False self.__syncLock.release() # Notify listeners that the process finished. self.on_capture_done(CaptureDoneEvent(self.__exitCode)) def start(self): """Start the capture process on a separate thread. This method immediately returns and the process continues in the background firing events as they occur. """ if self.is_running: return self.__processID = -1 self.__exitCode = 0 self.__captureThread = threading.Thread(target=self._monitor_capture) self.__captureThread.name = "raspistillMonitor" self.__captureThread.daemon = True self.__captureThread.start() self.__isRunning = True def dispose(self): """Release managed resources used by this component.""" if self.is_disposed: return self.cancel() self.__captureThread = None self.__exitCode = 0 self.__processID = -1 self.__syncLock = None self.__captureProc = None self.__settings = None self.__emitter.remove_all_listeners() self.__emitter = None Device.dispose(self)
class Opener(Device): """Opener device abstraction interface.""" def __init__(self): """Initialize a new instance of Opener.""" Device.__init__(self) self.__emitter = EventEmitter() self.__state = opener_state.CLOSED def on(self, evt, callback): """Register an event with a callback to handle it. :param str evt: The name of the event to register a handler for. :param function callback: The callback to execute when the event fires. :raises: ObjectDisposedException if this instance is disposed. """ if self.is_disposed: raise ObjectDisposedException("Opener") self.__emitter.on(evt, callback) def emit(self, evt, args): """Emit the specified event to all registered listeners. :param str evt: The name of the event to emit. :param object args: The arguments to pass to the event handlers (listeners). :raises: ObjectDisposedException if this instance is disposed. """ if self.is_disposed: raise ObjectDisposedException("Opener") self.__emitter.emit(evt, args) def remove_all_listeners(self): """Remove all registered event listeners.""" if self.is_disposed: return if self.__emitter is not None: self.__emitter.remove_all_listeners() def on_opener_state_change(self, change_evt): """Fire the opener state change event. :param raspy.devices.access.opener_state_change_event.OpenerStateChangeEvent change_evt: The event object. :raises: ObjectDisposedException if this instance has been disposed. """ if self.is_disposed: raise ObjectDisposedException("Opener") _t = threading.Thread(target=self.emit, name=EVENT_STATE_CHANGED, args=(EVENT_STATE_CHANGED, change_evt)) _t.daemon = True _t.start() def on_lock_state_change(self, change_evt): """Fire the lock state change event. :param raspy.devices.access.opener_lock_change_event.OpenerLockChangeEvent change_evt: The event object. :raises: ObjectDisposedException if this instance has been disposed. """ if self.is_disposed: raise ObjectDisposedException("Opener") _t = threading.Thread(target=self.emit, name=EVENT_LOCK_STATE_CHANGED, args=(EVENT_LOCK_STATE_CHANGED, change_evt)) _t.daemon = True _t.start() @property def state(self): """Get the state of the opener. :returns: The opener state. :rtype: int """ return self.__state @property def is_open(self): """Get a flag indicating whether the opener is open. :returns: True if open; Otherwise, False. :rtype: bool """ return self.state == opener_state.OPEN @property def is_opening(self): """Get a flag indicating if in the process of opening. :returns: True if opening; Otherwise, False. :rtype: bool """ return self.state == opener_state.OPENING @property def is_closed(self): """Get a flag indicating whether this opener is closed. :returns: True if closed; Otherwise, False. :rtype: bool """ return self.state == opener_state.CLOSED @property def is_closing(self): """Get a flag indicating if in the process of closing. :returns: True if closing; Otherwise, False. :rtype: bool """ return self.state == opener_state.CLOSING @property def is_locked(self): """Get a flag indicating whether this opener is locked. :returns: True if locked; Otherwise, False. :rtype: bool """ return False def open(self): """Instruct the device to open. :raises: ObjectDisposedException if this instance has been disposed. """ raise NotImplementedError("Method 'open()' not implemented.") def close(self): """Instruct the device to close. :raises: ObjectDisposedException if this instance has been disposed. """ raise NotImplementedError("Method 'close()' not implemented.") def dispose(self): """Release managed resources used by this component.""" if self.is_disposed: return self.__emitter.remove_all_listeners() self.__emitter = None self.__state = opener_state.CLOSED Device.dispose(self)
class GenericWebsocket: """ Websocket object used to contain the base functionality of a websocket. Inlcudes an event emitter and a standard websocket client. """ def __init__(self, host, logLevel='INFO', loop=None, max_retries=5, create_event_emitter=_start_event_worker): self.host = host self.logger = CustomLogger('BfxWebsocket', logLevel=logLevel) self.loop = loop or asyncio.get_event_loop() # overide 'error' event to stop it raising an exception # self.events.on('error', self.on_error) self.ws = None self.max_retries = max_retries self.attempt_retry = True self.sockets = {} # start seperate process for the even emitter eventLoop = create_event_emitter() self.events = EventEmitter(scheduler=asyncio.ensure_future, loop=eventLoop) def run(self): """ Starte the websocket connection. This functions spawns the initial socket thread and connection. """ self._start_new_socket() def get_task_executable(self): """ Get the run indefinitely asyncio task """ return self._run_socket() def _start_new_socket(self, socketId=None): if not socketId: socketId = len(self.sockets) def start_loop(loop): asyncio.set_event_loop(loop) loop.run_until_complete(self._run_socket()) worker_loop = asyncio.new_event_loop() worker = Thread(target=start_loop, args=(worker_loop, )) worker.start() return socketId def _wait_for_socket(self, socket_id): """ Block until the given socket connection is open """ while True: socket = self.sockets.get(socket_id, False) if socket: if socket.isConnected and socket.ws: return time.sleep(0.01) async def _connect(self, socket): async with websockets.connect(self.host) as websocket: self.sockets[socket.id].set_websocket(websocket) self.sockets[socket.id].set_connected() self.logger.info("Wesocket connected to {}".format(self.host)) while True: await asyncio.sleep(0) message = await websocket.recv() await self.on_message(socket.id, message) def get_socket(self, socketId): return self.sockets[socketId] def get_authenticated_socket(self): for socketId in self.sockets: if self.sockets[socketId].isAuthenticated: return self.sockets[socketId] return None async def _run_socket(self): retries = 0 sId = len(self.sockets) s = Socket(sId) self.sockets[sId] = s while retries < self.max_retries and self.attempt_retry: try: await self._connect(s) retries = 0 except (ConnectionClosed, socket.error) as e: self.sockets[sId].set_disconnected() self._emit('disconnected') if (not self.attempt_retry): return self.logger.error(str(e)) retries += 1 # wait 5 seconds befor retrying self.logger.info("Waiting 5 seconds before retrying...") await asyncio.sleep(5) self.logger.info("Reconnect attempt {}/{}".format( retries, self.max_retries)) self.logger.info("Unable to connect to websocket.") self._emit('stopped') def remove_all_listeners(self, event): """ Remove all listeners from event emitter """ self.events.remove_all_listeners(event) def on(self, event, func=None): """ Add a new event to the event emitter """ if not func: return self.events.on(event) self.events.on(event, func) def once(self, event, func=None): """ Add a new event to only fire once to the event emitter """ if not func: return self.events.once(event) self.events.once(event, func) def _emit(self, event, *args, **kwargs): self.events.emit(event, *args, **kwargs) async def on_error(self, error): """ On websocket error print and fire event """ self.logger.error(error) async def on_close(self): """ On websocket close print and fire event. This is used by the data server. """ self.logger.info("Websocket closed.") self.attempt_retry = False await self.ws.close() self._emit('done') async def on_open(self): """ On websocket open """ pass async def on_message(self, message): """ On websocket message """ pass
class WebsocketClient: def __init__(self, host=None, port=None, route=None, ssl=None): config = Configuration.get().get("websocket") host = host or config.get("host") port = port or config.get("port") route = route or config.get("route") ssl = ssl or config.get("ssl") validate_param(host, "websocket.host") validate_param(port, "websocket.port") validate_param(route, "websocket.route") self.url = WebsocketClient.build_url(host, port, route, ssl) self.emitter = EventEmitter() self.client = self.create_client() self.pool = ThreadPool(10) self.retry = 5 self.connected_event = Event() self.started_running = False @staticmethod def build_url(host, port, route, ssl): scheme = "wss" if ssl else "ws" return scheme + "://" + host + ":" + str(port) + route def create_client(self): return WebSocketApp(self.url, on_open=self.on_open, on_close=self.on_close, on_error=self.on_error, on_message=self.on_message) def on_open(self, ws): LOG.info("Connected") self.connected_event.set() self.emitter.emit("open") # Restore reconnect timer to 5 seconds on sucessful connect self.retry = 5 def on_close(self, ws): self.emitter.emit("close") def on_error(self, ws, error): """ On error start trying to reconnect to the websocket. """ if isinstance(error, WebSocketConnectionClosedException): LOG.warning('Could not send message because connection has closed') else: LOG.exception('=== ' + repr(error) + ' ===') try: self.emitter.emit('error', error) if self.client.keep_running: self.client.close() except Exception as e: LOG.error('Exception closing websocket: ' + repr(e)) LOG.warning("WS Client will reconnect in %d seconds." % self.retry) time.sleep(self.retry) self.retry = min(self.retry * 2, 60) try: self.emitter.emit('reconnecting') self.client = self.create_client() self.run_forever() except WebSocketException: pass def on_message(self, ws, message): self.emitter.emit('message', message) parsed_message = Message.deserialize(message) self.pool.apply_async( self.emitter.emit, (parsed_message.type, parsed_message)) def emit(self, message): if not self.connected_event.wait(10): if not self.started_running: raise ValueError('You must execute run_forever() ' 'before emitting messages') self.connected_event.wait() try: if hasattr(message, 'serialize'): self.client.send(message.serialize()) else: self.client.send(json.dumps(message.__dict__)) except WebSocketConnectionClosedException: LOG.warning('Could not send {} message because connection ' 'has been closed'.format(message.type)) def wait_for_response(self, message, reply_type=None, timeout=None): """Send a message and wait for a response. Args: message (Message): message to send reply_type (str): the message type of the expected reply. Defaults to "<message.type>.response". timeout: seconds to wait before timeout, defaults to 3 Returns: The received message or None if the response timed out """ response = [] def handler(message): """Receive response data.""" response.append(message) # Setup response handler self.once(reply_type or message.type + '.response', handler) # Send request self.emit(message) # Wait for response start_time = time.monotonic() while len(response) == 0: time.sleep(0.2) if time.monotonic() - start_time > (timeout or 3.0): try: self.remove(reply_type, handler) except (ValueError, KeyError): # ValueError occurs on pyee 1.0.1 removing handlers # registered with once. # KeyError may theoretically occur if the event occurs as # the handler is removed pass return None return response[0] def on(self, event_name, func): self.emitter.on(event_name, func) def once(self, event_name, func): self.emitter.once(event_name, func) def remove(self, event_name, func): try: if event_name in self.emitter._events: LOG.debug("Removing found '"+str(event_name)+"'") else: LOG.debug("Not able to find '"+str(event_name)+"'") self.emitter.remove_listener(event_name, func) except ValueError as e: LOG.warning('Failed to remove event {}: {}'.format(event_name, str(func))) for line in traceback.format_stack(): LOG.warning(line.strip()) if event_name in self.emitter._events: LOG.debug("Removing found '"+str(event_name)+"'") else: LOG.debug("Not able to find '"+str(event_name)+"'") LOG.warning("Existing events: " + str(self.emitter._events)) for evt in self.emitter._events: LOG.warning(" "+str(evt)) LOG.warning(" "+str(self.emitter._events[evt])) if event_name in self.emitter._events: LOG.debug("Removing found '"+str(event_name)+"'") else: LOG.debug("Not able to find '"+str(event_name)+"'") LOG.warning('----- End dump -----') def remove_all_listeners(self, event_name): ''' Remove all listeners connected to event_name. Args: event_name: event from which to remove listeners ''' if event_name is None: raise ValueError self.emitter.remove_all_listeners(event_name) def run_forever(self): self.started_running = True self.client.run_forever() def close(self): self.client.close() self.connected_event.clear()
class WebsocketClient(object): def __init__(self, host=config.get("Websocket", "host"), port=int(config.get("Websocket", "port")), route=config.get("Websocket", "route"), ssl=config.getboolean("Websocket", "ssl")): validate_param(host, "websocket.host") validate_param(port, "websocket.port") validate_param(route, "websocket.route") self.build_url(host, port, route, ssl) self.emitter = EventEmitter() self.client = self.create_client() self.pool = ThreadPool(10) self.retry = 5 def build_url(self, host, port, route, ssl): scheme = "wss" if ssl else "ws" self.url = scheme + "://" + host + ":" + str(port) + route def create_client(self): return WebSocketApp(self.url, on_open=self.on_open, on_close=self.on_close, on_error=self.on_error, on_message=self.on_message) def on_open(self, ws): LOG.info("Connected") self.emitter.emit("open") def on_close(self, ws): self.emitter.emit("close") def on_error(self, ws, error): try: self.emitter.emit('error', error) self.client.close() except Exception as e: LOG.error(repr(e)) LOG.warn("WS Client will reconnect in %d seconds." % self.retry) time.sleep(self.retry) self.retry = min(self.retry * 2, 60) self.client = self.create_client() self.run_forever() def on_message(self, ws, message): self.emitter.emit('message', message) parsed_message = Message.deserialize(message) self.pool.apply_async( self.emitter.emit, (parsed_message.type, parsed_message)) def emit(self, message): if (not self.client or not self.client.sock or not self.client.sock.connected): return if hasattr(message, 'serialize'): self.client.send(message.serialize()) else: self.client.send(json.dumps(message.__dict__)) def on(self, event_name, func): self.emitter.on(event_name, func) def once(self, event_name, func): self.emitter.once(event_name, func) def remove(self, event_name, func): self.emitter.remove_listener(event_name, func) def run_forever(self): self.client.run_forever() def close(self): self.client.close()
class WebsocketClient(object): def __init__(self, host=config.get("host"), port=config.get("port"), route=config.get("route"), ssl=config.get("ssl")): validate_param(host, "websocket.host") validate_param(port, "websocket.port") validate_param(route, "websocket.route") self.build_url(host, port, route, ssl) self.emitter = EventEmitter() self.client = self.create_client() self.pool = ThreadPool(10) self.retry = 5 def build_url(self, host, port, route, ssl): scheme = "wss" if ssl else "ws" self.url = scheme + "://" + host + ":" + str(port) + route def create_client(self): return WebSocketApp(self.url, on_open=self.on_open, on_close=self.on_close, on_error=self.on_error, on_message=self.on_message) def on_open(self, ws): LOG.info("Connected") self.emitter.emit("open") def on_close(self, ws): self.emitter.emit("close") def on_error(self, ws, error): try: self.emitter.emit('error', error) self.client.close() except Exception as e: LOG.error(repr(e)) LOG.warn("WS Client will reconnect in %d seconds." % self.retry) time.sleep(self.retry) self.retry = min(self.retry * 2, 60) self.client = self.create_client() self.run_forever() def on_message(self, ws, message): self.emitter.emit('message', message) parsed_message = Message.deserialize(message) self.pool.apply_async(self.emitter.emit, (parsed_message.type, parsed_message)) def emit(self, message): if (not self.client or not self.client.sock or not self.client.sock.connected): return if hasattr(message, 'serialize'): self.client.send(message.serialize()) else: self.client.send(json.dumps(message.__dict__)) def on(self, event_name, func): self.emitter.on(event_name, func) def once(self, event_name, func): self.emitter.once(event_name, func) def remove(self, event_name, func): self.emitter.remove_listener(event_name, func) def run_forever(self): self.client.run_forever() def close(self): self.client.close()
def addEventListener(emitter: EventEmitter, eventName: str, handler: Callable ) -> Dict[str, Any]: """Add handler to the emitter and return emitter/handler.""" emitter.on(eventName, handler) return {'emitter': emitter, 'eventName': eventName, 'handler': handler}
class TemperatureSensor(Component): """An abstract temperature sensor interface/base.""" def __init__(self, clock, data, reset): """Initialize a new instance of TempSensor. :param raspy.io.gpio.Gpio clock: The GPIO pin used for the clock. :param raspy.io.gpio.Gpio data: The GPIO used for data. :param raspy.io.gpio.Gpio reset: The GPIO pin used to trigger reset. :raises: ArgumentNullException if any of the pins are None. """ Component.__init__(self) if clock is None: raise ArgumentNullException("'clock' cannot be None.") if data is None: raise ArgumentNullException("'data' cannot be None.") if reset is None: raise ArgumentNullException("'reset' cannot be None.") self.__emitter = EventEmitter() self.__rawTemp = 0.0 self.__scale = temp_scale.CELCIUS self.__tempSensor = DS1620(clock, data, reset) def dispose(self): """Release managed resources used by this component.""" if self.is_disposed: return if self.__tempSensor is not None: self.__tempSensor.dispose() self.__tempSensor = None self.__rawTemp = 0.0 self.__scale = temp_scale.CELCIUS self.__emitter.remove_all_listeners() self.__emitter = None Component.dispose(self) def on(self, evt, callback): """Register an event with a callback to handle it. :param str evt: The name of the event to register a handler for. :param function callback: The callback to execute when the event fires. :raises: ObjectDisposedException if this instance is disposed. """ if self.is_disposed: raise ObjectDisposedException("TempSensor") self.__emitter.on(evt, callback) def emit(self, evt, args): """Emit the specified event to all registered listeners. :param str evt: The name of the event to emit. :param object args: The arguments to pass to the event handlers (listeners). :raises: ObjectDisposedException if this instance is disposed. """ if self.is_disposed: raise ObjectDisposedException("TempSensor") self.__emitter.emit(evt, args) def remove_all_listeners(self): """Remove all registered event listeners.""" if self.is_disposed: return if self.__emitter is not None: self.__emitter.remove_all_listeners() def get_raw_temperature(self): """Get the raw temperature value. :returns: The raw value read from the sensor. :rtype: float :raises: ObjectDisposedException if this instance has been disposed. """ return self.__rawTemp @property def scale(self): """Get the temperature scale. :returns: The temperature scale. :rtype: int """ return self.__scale @scale.setter def scale(self, the_scale): """Set the temperature scale. :param int the_scale: The temperature scale to set. """ self.__scale = the_scale def _set_raw_temp(self, temp): """Set the raw temperature. :param float temp: The temperature to set. """ self.__rawTemp = temp def on_temperature_change(self, change_evt): """Fire the temperature change event. :param raspy.components.temperature.temp_chang_event.TempChangeEvent change_evt: The event object. :raises: ObjectDisposedException if this instance has been disposed. """ if self.is_disposed: raise ObjectDisposedException("TempSensor") _t = threading.Thread(target=self.emit, name=EVENT_TEMPERATURE_CHANGED, args=(EVENT_TEMPERATURE_CHANGED, change_evt)) _t.daemon = True _t.start() def get_temperature(self, scale): """Get the temperature value. :param int scale: The scale to use for measurement. :returns: The temperature value in the specified scale. :rtype: float :raises: ObjectDisposedException if this instance has been disposed. """ raw = self.get_raw_temperature() return temp_conversion.convert(self.scale, scale, raw) @property def sensor(self): """Get the sensor being used to measure. :returns: The temperature sensor. :rtype: raspy.sensors.ds1620.DS1620 """ return self.__tempSensor
class Button(Component): """A buttons device abstraction component base class.""" def __init__(self): """Initialize a new Button.""" super(Component, self).__init__() self.__holdTimer = None self.__baseState = button_state.RELEASED self.__emitter = EventEmitter() @property def is_pressed(self): """Get a value indicating whether or not the buttons is pressed. :returns: True if the buttons is pressed; Otherwise, False. :rtype: bool """ return self.__baseState == button_state.PRESSED @property def is_released(self): """Get a value indicating whether or not the buttons is released. :returns: True if the buttons is released; Otherwise, False. :rtype: bool """ return self.__baseState == button_state.RELEASED def _set_state(self, state): """Override state with the specified state. :param int state: The state to set. """ self.__baseState = state @property def state(self): """Get the buttons state. :returns: The buttons state. :rtype: int """ return self.__baseState def is_state(self, state): """Check to see if the buttons is in the specified state. :param int state: The state to check. :returns: True if the buttons is in the specified state. :rtype: bool """ return self.__baseState == state def on(self, evt, callback): """Register an event with a callback to handle it. :param str evt: The name of the event to register a handler for. :param function callback: The callback to execute when the event fires. :raises: raspy.object_disposed_exception.ObjectDisposedException if this instance is disposed. """ if self.is_disposed: raise ObjectDisposedException("Button") self.__emitter.on(evt, callback) def emit(self, evt, args): """Emit the specified event to all registered listeners. :param str evt: The name of the event to emit. :param object args: The arguments to pass to the event handlers (listeners). :raises: raspy.object_disposed_exception.ObjectDisposedException if this instance is disposed. """ if self.is_disposed: raise ObjectDisposedException("Button") self.__emitter.emit(evt, args) def on_button_hold(self, btn_event): """Fire the buttons hold event. :param raspy.components.buttons.button_event.ButtonEvent btn_event: The buttons event info. """ if self.is_disposed: raise ObjectDisposedException("Button") self.emit(EVENT_HOLD, btn_event) def _on_hold_timer_elapsed(self, btn_event): """Handle the timer elapsed event. This internal method fires the button hold event when the hold timer elapses. Should only be called by _fire_button_hold_event(). :param raspy.components.buttons.button_event.ButtonEvent btn_event: The button event info. """ if self.is_pressed: self.on_button_hold(btn_event) def _stop_hold_timer(self): """Stop the internal hold timer.""" if self.__holdTimer is not None: self.__holdTimer.cancel() self.__holdTimer = None def _fire_button_hold_event(self): """Fire the timer elapsed event handler.""" evt = ButtonEvent(self) self._on_hold_timer_elapsed(evt) def _start_hold_timer(self): """Start the button hold timer.""" self.__holdTimer = Timer(2.0, self._fire_button_hold_event) self.__holdTimer.start() def on_state_changed(self, btn_event): """Fire the buttons state changed event. :param raspy.components.buttons.button_event.ButtonEvent btn_event: The buttons event info. """ if self.is_disposed: raise ObjectDisposedException("Button") self.emit(EVENT_STATE_CHANGED, btn_event) if btn_event.is_pressed: self.on_button_pressed(btn_event) if btn_event.is_released: self.on_button_released(btn_event) def on_button_pressed(self, btn_event): """Fire the buttons pressed event. :param raspy.components.buttons.button_event.ButtonEvent btn_event: The buttons event info. """ if self.is_disposed: raise ObjectDisposedException("Button") self.emit(EVENT_PRESSED, btn_event) self._stop_hold_timer() self._start_hold_timer() def on_button_released(self, btn_event): """Fire the buttons released event. :param raspy.components.buttons.button_event.ButtonEvent btn_event: The buttons event info. """ if self.is_disposed: raise ObjectDisposedException("Button") self.emit(EVENT_RELEASED, btn_event) self._stop_hold_timer() def __str__(self): """Return the string representation of this class instance. :returns: The string representation of this class. :rtype: str """ base = Component.component_name.fget() if not string_utils.is_null_or_empty(base): return base return self.__class__.__name__ def dispose(self): """Dispose of all the managed resources used by this instance.""" if self.is_disposed: return self.__emitter.remove_all_listeners() self._stop_hold_timer() self.__emitter = None Component.dispose(self)