示例#1
0
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 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)
示例#3
0
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)
示例#4
0
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
示例#5
0
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 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
示例#7
0
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()
示例#8
0
class WebsocketClient(object):
    def __init__(self, url):
        self.url = url
        self.emitter = EventEmitter()
        self.client = self.create_client()
        self.pool = ThreadPool(10)
        self.retry = 5

    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):
        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:
            print(repr(e))
        print("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()
示例#9
0
class MotionSensor(Component):
    """A motion sensor abstraction component interface."""
    def __init__(self, pin):
        """Initialize a new instance of MotionSensor.

        :param raspy.io.gpio.Gpio pin: The input pin to check for motion on.
        :raises: ArgumentNullException if pin is None.
        """
        Component.__init__(self)
        if pin is None:
            raise ArgumentNullException("'pin' param cannot be None.")

        self.__emitter = EventEmitter()
        self.__lastMotion = None
        self.__lastInactive = None
        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.__lastMotion = None
        self.__lastInactive = None
        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("MotionSensor")

        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("MotionSensor")

        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_motion_state_changed(self, motion_evt):
        """Fire the motion state changed event.

        :param raspy.components.sensors.motion_detected_event.MotionDetectedEvent motion_evt:
        The motion detected event object.
        :raises: ObjectDisposedException if this instance has been disposed.
        """
        if self.is_disposed:
            raise ObjectDisposedException("MotionSensor")

        if motion_evt.is_motion_detected:
            self.__lastMotion = datetime.now()
        else:
            self.__lastInactive = datetime.now()

        _t = threading.Thread(target=self.emit,
                              name=EVENT_MOTION_STATE_CHANGED,
                              args=(EVENT_MOTION_STATE_CHANGED, motion_evt))
        _t.daemon = True
        _t.start()

    @property
    def last_motion_timestamp(self):
        """Get the timestamp of the last time motion was detected.

        :returns: The timestamp of when motion was detected.
        :rtype: datetime.datetime
        """
        return self.__lastMotion

    @property
    def last_inactivity_timestamp(self):
        """The last inactivity timestamp.

        :returns: The timestamp of the last time the sensor went idle.
        :rtype: datetime.datetime
        """
        return self.__lastInactive

    @property
    def pin(self):
        """Get the pin being used to sample sensor data.

        :returns: The underlying physical pin.
        :rtype: raspy.io.gpio.Gpio
        """
        return self.__pin

    @property
    def is_motion_detected(self):
        """Check to see if motion was detected.

        :returns: True if motion was detected.
        :rtype: bool
        """
        return False
示例#10
0
class MicrochipPotentiometer(Potentiometer):
    """An MCP45XX or MCP46XX IC device abstraction base type."""
    def __init__(self,
                 device=None,
                 pin_a0=False,
                 pin_a1=False,
                 pin_a2=False,
                 channel=microchip_pot_channel.NONE,
                 non_vol_mode=microchip_pot_non_volatile_mode.
                 VOLATILE_AND_NON_VOLATILE,
                 init_non_vol_wiper_val=0):
        """Initialize a new instance of MicrochipPotentiometer.

        :param raspy.io.i2c.i2c_interface.I2CInterface device: The I2C bus
        device this instance is connected to.
        :param bool pin_a0: Set True if device's address pin A0 is high.
        :param bool pin_a1: Set True if device's address pin A1 is high.
        :param bool pin_a2: Set True if device's address pin A2 is high.
        :param int channel: The potentiometer channel.
        :param int non_vol_mode: The non-volatility mode.
        :param int init_non_vol_wiper_val: The initial value to set.
        :raises: raspy.argument_null_exception.ArgumentNullException if
        'device' param is None.
        :raises: raspy.illegal_argument_exception.IllegalArgumentException if
        the specified channel is not supported by the device.
        :raises: raspy.io.io_exception.IOException if unable to open the
        I2C bus.
        """
        Potentiometer.__init__(self)
        if device is None:
            msg = "Param 'device' cannot be None."
            raise ArgumentNullException(msg)

        if not self.is_channel_supported(channel):
            msg = "Specified channel not supported by device."
            raise IllegalArgumentException(msg)

        self.__emitter = EventEmitter()
        self.__channel = channel
        self.__currentValue = 0
        self.__nonVolMode = non_vol_mode
        device_addr = MicrochipPotentiometer._build_i2c_address(
            pin_a0, pin_a1, pin_a2)
        self.__controller = mcp_device_controller.MCPDeviceController(
            device, device_addr)
        self.__emitter.on(
            EVENT_WIPER_ACTION,
            lambda wiper_evt: self._on_wiper_action_event(wiper_evt))
        self._initialize(init_non_vol_wiper_val)

    @staticmethod
    def _build_i2c_address(pin_a0=False, pin_a1=False, pin_a2=False):
        """Build the I2C bus address of the device based on which pins are set.

        :param bool pin_a0: Set True if the device's address pinA0 is high.
        :param bool pin_a1: Set True if the device's address pinA1 is high.
        :param bool pin_a2: Set True if the device's address pinA2 is high.
        :returns: The I2C address based on address pins given.
        :rtype: int
        """
        # Constant component.
        i2c_addr = 0x0101000

        # Dynamic component if device knows A0.
        if pin_a0:
            i2c_addr |= 0x0000001

        # Dynamic component if device knows A1.
        if pin_a1:
            i2c_addr |= 0x0000010

        # Dynamic component if device knows A2.
        if pin_a2:
            i2c_addr |= 0x0000100
        return i2c_addr

    def _get_value_according_boundaries(self, val=0):
        """Adjust the given value according to the boundaries (0 and max_val).

        :param int val: The wiper's value to be set.
        :returns: A valid wiper value.
        :rtype: int
        """
        if val is None or not type(val) == int or val < 0:
            val = 0
            new_val = val
        elif val > self.max_value:
            new_val = self.max_value
        else:
            new_val = val
        return new_val

    def _initialize(self, init_val):
        """Initialize the wiper to a defined status.

        For devices capable of non-volatile wipers, the non-volatile value is
        loaded. For devices not capable, the given value is set in the device.

        :param int init_val: The initial value for devices not capable of
        non-volatile wipers.
        :raises: raspy.io.io_exception.IOException if communication with the
        device failed or a malformed result.
        """
        chan = device_control_channel.value_of(self.__channel)
        if self.is_non_volatile_wiper_capable:
            self.__currentValue = self.__controller.get_value(chan, True)
        else:
            new_init_val = self._get_value_according_boundaries(init_val)
            vol = mcp_device_controller.VOLATILE_WIPER
            self.__controller.set_value(chan, new_init_val, vol)
            self.__currentValue = new_init_val

    def _on_wiper_action_event(self, wiper_evt=None):
        """Handle the internal EVENT_WIPER_ACTION.

        :param WiperEvent wiper_evt: The event info.
        """
        # Do nothing if no event specified.
        if wiper_evt is None:
            return

        # For volatile wiper.
        if (self.__nonVolMode == microchip_pot_non_volatile_mode.VOLATILE_ONLY
                or self.__nonVolMode
                == microchip_pot_non_volatile_mode.VOLATILE_AND_NON_VOLATILE):
            wiper_evt.set_channel_value(mcp_device_controller.VOLATILE_WIPER)

        # For non-volatile wiper.
        if (self.__nonVolMode
                == microchip_pot_non_volatile_mode.NON_VOLATILE_ONLY
                or self.__nonVolMode
                == microchip_pot_non_volatile_mode.VOLATILE_AND_NON_VOLATILE):
            wiper_evt.set_channel_value(
                mcp_device_controller.NON_VOLATILE_WIPER)

    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("MicrochipPotentiometer")

        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("MicrochipPotentiometer")

        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 dispose(self):
        """Dispose managed resources."""
        if self.is_disposed:
            return

        if self.__controller is not None:
            self.__controller.dispose()
            self.__controller = None

        self.__emitter.remove_all_listeners()
        self.__emitter = None
        self.__currentValue = -1
        self.__nonVolMode = microchip_pot_non_volatile_mode.VOLATILE_AND_NON_VOLATILE
        Potentiometer.dispose(self)

    def _fire_wiper_action_event(self, wiper_evt):
        """Fire the EVENT_WIPER_ACTION event.

        :param WiperEvent wiper_evt: The event info.
        """
        if self.is_disposed:
            raise ObjectDisposedException("MicrochipPotentiometer")
        self.emit(EVENT_WIPER_ACTION, wiper_evt)

    @property
    def current_value(self):
        """Get the wiper's current value.

        :returns: The current value. Values from 0 to max_value are valid.
        Values above or below these boundaries should be corrected quietly.
        :rtype: int
        """
        return self.__currentValue

    @current_value.setter
    def current_value(self, value):
        """Set the wiper's current value.

        :param int value: The wiper value to set.
        """
        # Check bounds.
        new_val = self._get_value_according_boundaries(value)

        # Set wipers according to mode.
        chan = device_control_channel.value_of(self.channel)
        evt = WiperEvent(chan, self.__controller, new_val)
        self._fire_wiper_action_event(evt)

        # Set value only if volatile wiper is affected.
        if self.__nonVolMode == microchip_pot_non_volatile_mode.NON_VOLATILE_ONLY:
            return
        self.__currentValue = new_val

    @property
    def channel(self):
        """Get the channel this device is configured for.

        :returns: The device channel.
        :rtype: int
        """
        return microchip_pot_channel.NONE

    @property
    def is_non_volatile_wiper_capable(self):
        """Get whether or not this device is capable of non-volatile wipers.

        :returns: True if the device is capable of non-volatile wipers.
        :rtype: bool
        """
        return False

    @property
    def non_volatile_mode(self):
        """Get the way non-volatile reads and/or writes are done.

        :returns: The non-volatile mode.
        :rtype: int
        """
        return microchip_pot_non_volatile_mode.VOLATILE_AND_NON_VOLATILE

    @property
    def supported_channels(self):
        """Get the channels that are suppored by the underlying device.

        :returns: A list of `raspy.components.potentiometers.microchip.MicrochipPotChannel`
        that represent the supported channels by the underlying device.
        :rtype: list
        """
        return list()

    @property
    def device_status(self):
        """Get the device and wiper status.

        :returns: The device status.
        :rtype: raspy.components.potentiometers.microchip.MicrochipPotDeviceStatus.
        :raises: raspy.io.io_exception.IOException if communication with the
        device fails.
        """
        dev_stat = self.__controller.device_status
        wipe_lock_act = dev_stat.channel_b_locked
        if self.__channel == microchip_pot_channel.A:
            wipe_lock_act = dev_stat.channel_a_locked
        write_act = dev_stat.eeprom_write_active
        write_prot = dev_stat.eeprom_write_protected
        return MicrochipPotDevStatus(self.__channel, write_act, write_prot,
                                     wipe_lock_act)

    @property
    def terminal_configuration(self):
        """Get the current terminal configuration.

        :returns: The terminal configuration.
        :rtype: raspy.components.potentiometers.microchip.MCPTerminalConfiguration
        :raises: raspy.io.io_exception.IOException if communication with
        the device fails.
        """
        chan = device_control_channel.value_of(self.__channel)
        tcon = self.__controller.get_terminal_config(chan)
        chan_enable = tcon.channel_enabled
        pin_a = tcon.pin_a_enabled
        pin_w = tcon.pin_w_enabled
        pin_b = tcon.pin_b_enabled
        return MCPTerminalConfiguration(self.__channel, chan_enable, pin_a,
                                        pin_w, pin_b)

    @terminal_configuration.setter
    def terminal_configuration(self, config):
        """Set the terminal configuration.

        :param MCPTerminalConfiguration config:
        The terminal configuration.
        :raises: raspy.io.io_exception.IOException if communication with
        the device fails.
        """
        if config is None:
            raise ArgumentNullException("config value cannot be None.")

        if config.channel != self.__channel:
            msg = "Setting a configuration with a channel that is not the "
            msg += "potentiometer's channel is not supported."
            raise IllegalArgumentException(msg)

        chan = device_control_channel.value_of(self.__channel)
        chan_enable = config.is_channel_enabled
        pin_a = config.is_pin_a_enabled
        pin_w = config.is_pin_w_enabled
        pin_b = config.is_pin_b_enabled
        dev_con = DeviceControllerTermConfig(chan, chan_enable, pin_a, pin_w,
                                             pin_b)
        self.__controller.set_terminal_config(dev_con)

    def _set_non_volatile_mode(self, mode):
        """Set the non-volatility mode.

        :param int mode: The way non-volatile reads or writes are done.
        :raises: raspy.invalid_operation_exception.InvalidOperationException if
        this device is not capable of non-volatile wipers.
        """
        vol_only = microchip_pot_non_volatile_mode.VOLATILE_ONLY
        if (not self.is_non_volatile_wiper_capable
                and self.__nonVolMode != vol_only):
            msg = "This device is not capable of non-volatile wipers."
            msg += "You *must* use microchip_pot_non_volatile_mode.VOLATILE_ONLY"
            raise InvalidOperationException(msg)
        self.__nonVolMode = mode

    def _get_non_volatile_value(self):
        """Get the non-volatile wiper's value.

        :returns: The non-volatile wiper's value.
        :rtype: int
        :raises: raspy.invalid_operation_exception.InvalidOperationException if
        this device is not capable of non-volatile wipers.
        """
        if self.is_non_volatile_wiper_capable:
            msg = "This device is not capable of non-volatile wipers."
            raise InvalidOperationException(msg)
        chan = device_control_channel.value_of(self.__channel)
        return self.__controller.get_value(chan, True)

    def update_cache_from_device(self):
        """Update the cache from the wiper's value.

        :returns: The wiper's current value.
        :rtype: int
        :raises: raspy.io.io_exception.IOException if communication with
        the device fails.
        """
        chan = device_control_channel.value_of(self.__channel)
        self.__currentValue = self.__controller.get_value(chan, False)
        return self.__currentValue

    def is_channel_supported(self, channel):
        """Get whether or not the specified channel is supported by the device.

        :param int channel: The channel to check.
        :returns: True if the channel is supported.
        :rtype: bool
        """
        supported = False
        for chan in self.supported_channels:
            if chan == channel:
                supported = True
                break
        return supported

    def decrease(self, steps=0):
        """Decrease the wiper's value by the specified number of steps.

        It is not an error if the wiper hits or already hit the lower
        boundary (0). In such situations, the wiper sticks to the lower
        boundary or doesn't change.

        :param int steps: The number of steps to decrease by. If not
        specified or zero, then defaults to 1.
        :raises: raspy.io.io_exception.IOException if communication with the
        device failed.
        """
        if self.__currentValue == 0:
            return

        if steps < 0:
            msg = "Only positive integer values are permitted."
            raise IllegalArgumentException(msg)

        if self.non_volatile_mode != microchip_pot_non_volatile_mode.VOLATILE_ONLY:
            msg = "Decrease is only permitted on volatile-only wipers."
            raise InvalidOperationException(msg)

        # Check bounds:
        actual_steps = steps
        if steps > self.__currentValue:
            actual_steps = self.__currentValue

        new_val = self.__currentValue - actual_steps
        if new_val == 0 or steps > 5:
            self.__currentValue = new_val
        else:
            chan = device_control_channel.value_of(self.__channel)
            self.__controller.decrease(chan, actual_steps)
            self.__currentValue = new_val

    def increase(self, steps=0):
        """Increase the wiper's value bye the specified number of steps.

        It is not an error if the wiper hits or already hit the upper
        boundary. In such situations, the wiper sticks to the upper boundary
        or doesn't change.
        :param int steps: How many steps to increase. If not specified or
        zero, then defaults to 1. If the current value is equal to the max
        value, then nothing happens. If steps is less than zero, then an
        exception is thrown.
        :raises: raspy.io.io_exception.IOException if communication with the
        device failed.
        """
        max_val = self.max_value
        if self.__currentValue == max_val:
            return

        if steps < 0:
            msg = "Only positive integer values are permitted."
            raise IllegalArgumentException(msg)

        vol_only = microchip_pot_non_volatile_mode.VOLATILE_ONLY
        if self.non_volatile_mode != vol_only:
            msg = "Increase is only permitted for volatile-only wipers."
            raise InvalidOperationException(msg)

        # Check bounds.
        actual_steps = steps
        if (steps + self.__currentValue) > max_val:
            actual_steps = max_val - self.__currentValue

        new_val = self.__currentValue + actual_steps
        if new_val == max_val or steps > 5:
            self.__currentValue = new_val
        else:
            chan = device_control_channel.value_of(self.__channel)
            self.__controller.increase(chan, actual_steps)
            self.__currentValue = new_val

    def set_wiper_lock(self, enabled=False):
        """Enable or disable the wiper lock.

        :param bool enabled: Set True to enable the wiper lock.
        :raises: raspy.io.io_exception.IOException if communication with
        the device fails.
        :raises: raspy.object_disposed_exception.ObjectDisposedException if
        this instance has been disposed.
        """
        if self.is_disposed:
            raise ObjectDisposedException("MicrochipPotentiometer")
        chan = device_control_channel.value_of(self.__channel)
        self.__controller.set_wiper_lock(chan, enabled)

    def set_write_protection(self, enabled=False):
        """Enable or disable write protection for devices with non-vol memory.

        Enabling write-protection does not only protect non-volatile wipers,
        it also protects any other non-volatile information stored (ie.
        wiper-locks).

        :param bool enabled: Set True to enable.
        :raises: raspy.io.io_exception.IOException if communication with
        the device fails.
        :raises: raspy.object_disposed_exception.ObjectDisposedException if
        this instance has been disposed.
        """
        if self.is_disposed:
            raise ObjectDisposedException("MicrochipPotentiometer")
        self.__controller.set_write_protection(enabled)
示例#11
0
class Light(Component):
    """The base type for light component abstractions."""

    def __init__(self):
        """Initialize a new instance of Light."""
        Component.__init__(self)
        self.__emitter = EventEmitter()

    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("Light")

        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("Light")

        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 is_on(self):
        """Get a value indicating whether or not the light is on.

        :returns: True if the light is on.
        :rtype: bool
        """
        return False

    @property
    def is_off(self):
        """Get a value indicating whether or not the light is off.

        :returns: True if the light is off.
        :rtype: bool
        """
        return not self.is_on

    def turn_on(self):
        """Turn the light on.

        :raises: raspy.object_disposed_exception.ObjectDisposedException if
        this instance has been disposed.
        """
        raise NotImplementedError("Method turn_on() not implemented.")

    def turn_off(self):
        """Turn the light off.

        :raises: raspy.object_disposed_exception.ObjectDisposedException if
        this instance has been disposed.
        """
        raise NotImplementedError("Method turn_off() not implemented.")

    def on_light_state_changed(self, evt):
        """Fire the light state change event.

        :param raspy.components.lights.light_state_change_event.LightStateChangeEvent evt:
        The state change event object.
        """
        if self.is_disposed:
            raise ObjectDisposedException("Light")

        _t = threading.Thread(target=self.emit,
                              name="stateChange",
                              args=(EVENT_STATE_CHANGED, evt))
        _t.daemon = True
        _t.start()

    def dispose(self):
        """Dispose managed resources."""
        if self.is_disposed:
            return

        if self.__emitter is not None:
            self.__emitter.remove_all_listeners()
            self.__emitter = None

        Component.dispose(self)
示例#12
0
class Relay(Component):
    """A relay component abstraction interface/base."""

    def __init__(self, pin):
        """Initialize a new instance of Relay.

        :param raspy.io.gpio.Gpio pin: The output pin being used to control
        the relay.
        :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 = relay_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 = relay_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("Relay")

        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("Relay")

        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_relay_state_changed(self, change_evt):
        """Fire the relay state change event.

        :param raspy.components.relays.relay_state_change_event.RelayStateChangeEvent change_evt:
        The state change event info.
        :raises: ObjectDisposedException if this instance has been disposed.
        """
        if self.is_disposed:
            raise ObjectDisposedException("Relay")

        _t = threading.Thread(target=self.emit,
                              name=EVENT_STATE_CHANGED,
                              args=(EVENT_STATE_CHANGED, change_evt))
        _t.daemon = True
        _t.start()

    def on_pulse_start(self):
        """Fire the pulse start event.

        :raises: ObjectDisposedException if this instance has been disposed.
        """
        if self.is_disposed:
            raise ObjectDisposedException("Relay")

        _t = threading.Thread(target=self.emit,
                              name=EVENT_PULSE_START,
                              args=[EVENT_PULSE_START])
        _t.daemon = True
        _t.start()

    def on_pulse_stop(self):
        """Fire the pulse stop event.

        :raises: ObjectDisposedException if this instance has been disposed.
        """
        if self.is_disposed:
            raise ObjectDisposedException("Relay")

        _t = threading.Thread(target=self.emit,
                              name=EVENT_PULSE_STOP,
                              args=[EVENT_PULSE_STOP])
        _t.daemon = True
        _t.start()

    @property
    def state(self):
        """Get the relay state.

        :returns: The relay state.
        :rtype: int
        """
        return self.__state

    @state.setter
    def state(self, rel_state):
        """Set the relay state.

        :param int rel_state: The relay state to set.
        :raises: ObjectDisposedException if this instance has been disposed.
        """
        if self.is_disposed:
            raise ObjectDisposedException("Relay")

        self.__state = rel_state

    @property
    def is_open(self):
        """Check to see if the relay is in an open state.

        :returns: True if in an open state; Otherwise, False.
        :rtype: bool
        """
        return self.state == relay_state.OPEN

    @property
    def is_closed(self):
        """Check to see if the relay is in a closed state.

        :returns: True if closed; Otherwise, False.
        :rtype: bool
        """
        return self.state == relay_state.CLOSED

    @property
    def pin(self):
        """Get the pin being used to drive the relay.

        :returns: The underlying physical pin.
        :rtype: raspy.io.gpio.Gpio
        """
        return self.__pin

    def open(self):
        """Open (deactivate) the relay."""
        self.state = relay_state.OPEN

    def close(self):
        """Close (activate) the relay."""
        self.state = relay_state.CLOSED

    def toggle(self):
        """Toggle the relay (switch on, then off)."""
        if self.is_open:
            self.close()
        else:
            self.open()

    def pulse(self, millis=0):
        """Pulse the relay on for the specified number of milliseconds.

        :param int millis: The number of milliseconds to wait before switching
        back off. If not specified or invalid, then pulses for
        DEFAULT_PULSE_MILLISECONDS.
        """
        self.on_pulse_start()
        self.close()
        self.__pin.pulse(millis)
        self.open()
        self.on_pulse_stop()
示例#13
0
class Switch(Component):
    """An interface/base type for switch device abstractions."""
    def __init__(self):
        """Initialize a new instance of Switch."""
        Component.__init__(self)
        self.__emitter = EventEmitter()
        self.__state = switch_state.OFF

    def dispose(self):
        """Release managed resources used by this component."""
        if self.is_disposed:
            return

        self.__state = switch_state.OFF
        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("Switch")

        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("Switch")

        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_switch_state_changed(self, evt):
        """Fire the switch state change event.

        :param: raspy.components.switches.switch_state_change_event.SwitchStateChangeEvent evt:
        The event info object.
        :raises: ObjectDisposedException if this instance has been disposed.
        """
        if self.is_disposed:
            raise ObjectDisposedException("Switch")

        _t = threading.Thread(target=self.emit,
                              name=EVENT_STATE_CHANGED,
                              args=(EVENT_STATE_CHANGED, evt))
        _t.daemon = True
        _t.start()

    @property
    def state(self):
        """Get the state of the switch.

        :returns: The switch state.
        :rtype: int
        """
        return self.__state

    def is_state(self, sw_state):
        """Get the state of the switch.

        :param int sw_state: The state to check.
        :returns: The switch state.
        :rtype: int
        """
        return self.state == sw_state

    @property
    def is_on(self):
        """Get whether or not this switch is in the on position.

        :returns: True if on; Otherwise, False.
        :rtype: bool
        """
        return self.is_state(switch_state.ON)

    @property
    def is_off(self):
        """Get whether or not this switch is in the off position.

        :returns: True if off; Otherwise, False.
        :rtype: bool
        """
        return self.is_state(switch_state.OFF)

    @property
    def pin(self):
        """Get the underlying physical pin.

        :returns: The pin.
        :rtype: raspy.io.gpio.Gpio
        """
        return None
示例#14
0
文件: motor.py 项目: cyrusbuilt/RasPy
class Motor(Component):
    """A motor abstraction base class."""
    def __init__(self):
        """Initialize a new instance of Motor."""
        Component.__init__(self)
        self.__emitter = EventEmitter()
        self.__state = motor_state.STOP

    def dispose(self):
        """Dispose managed resources."""
        if self.is_disposed:
            return

        self.__emitter.remove_all_listeners()
        self.__emitter = None
        Component.dispose(self)

    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(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("Motor")

        self.__emitter.on(evt, callback)

    def emit(self, evt, args=None):
        """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("Motor")

        self.__emitter.emit(evt, args)

    def _fire_events(self, change_evt):
        """Fire motor state change events.

        :param raspy.components.motors.motor_state_change_event.MotorStateChangeEvent change_evt:
        The event info object.
        """
        self.emit(EVENT_STATE_CHANGED, change_evt)
        if change_evt.new_state == motor_state.STOP:
            self.emit(EVENT_STOPPED)
        elif change_evt.new_state == motor_state.FORWARD:
            self.emit(EVENT_FORWARD)
        elif change_evt.new_state == motor_state.REVERSE:
            self.emit(EVENT_REVERSE)

    def on_motor_state_change(self, change_evt):
        """Fire the motor state change event.

        :param raspy.components.motors.motor_state_change_event.MotorStateChangeEvent change_evt:
        The event info object.
        """
        if self.is_disposed:
            raise ObjectDisposedException("Motor")

        _t = threading.Thread(target=self._fire_events,
                              name="motorStateChange",
                              args=[change_evt])
        _t.daemon = True
        _t.start()

    @property
    def state(self):
        """Get the motor state.

        :returns: The motor state.
        :rtype: int
        """
        return self.__state

    @state.setter
    def state(self, mot_state):
        """Set the motor state.

        :param int mot_state: The motor state.
        :raises: raspy.object_disposed_exception.ObjectDisposedException if
        this instance has been disposed.
        """
        if self.is_disposed:
            raise ObjectDisposedException("Motor")
        self.__state = mot_state

    @property
    def is_stopped(self):
        """Get whether or not the motor is stopped.

        :returns: True if the motor is stopped.
        :rtype: bool
        """
        return self.is_state(motor_state.STOP)

    def forward(self, millis=0):
        """Tell the motor to move forward for the specified millis.

        :param int millis: The number of milliseconds to continue moving
        forward for. If zero or None, then moves forward continuously until
        stopped.
        """
        if self.state == motor_state.FORWARD:
            return

        old_state = self.state
        self.state = motor_state.FORWARD
        evt = MotorStateChangeEvent(old_state, motor_state.FORWARD)
        self.on_motor_state_change(evt)
        if millis > 0:
            _t = threading.Timer(millis / 1000, self.stop)
            _t.start()

    def reverse(self, millis=0):
        """Tell the motor to move in reverse for the specified millis.

        :param int millis: The number of milliseconds to continue moving in
        reverse for. If zero or None, then moves in reverse continuously until
        stopped.
        """
        if self.state == motor_state.REVERSE:
            return

        old_state = self.state
        self.state = motor_state.REVERSE
        evt = MotorStateChangeEvent(old_state, motor_state.REVERSE)
        self.on_motor_state_change(evt)
        if millis > 0:
            _t = threading.Timer(millis / 1000, self.stop)
            _t.start()

    def stop(self):
        """Stop the motor's movement."""
        if self.state == motor_state.STOP:
            return

        old_state = self.state
        self.state = motor_state.STOP
        evt = MotorStateChangeEvent(old_state, motor_state.STOP)
        self.on_motor_state_change(evt)

    def is_state(self, state):
        """Determine if motor is in the specified state.

        :param int state: The state to check for.
        :returns: True if the motor is in the specified state.
        :rtype: bool
        """
        return self.__state == state
示例#15
0
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)
示例#16
0
文件: gpio.py 项目: cyrusbuilt/RasPy
class Gpio(Pin):
    """Implemented by classes that represent GPIO pins on the Raspberry Pi."""
    def __init__(self, pn, mode, value):
        """Initialize a new instance of raspy.io.Gpio.

        :param raspy.io.gpio_pins.GpioPin pn: The GPIO pin.
        :param int mode: The I/O pin mode.
        :param int value: The initial pin value.
        """
        super(Pin, self).__init__()
        self.__emitter = EventEmitter()

        self.__pin = pn
        if self.__pin is None:
            self.__pin = gpio_pins.GpioNone

        self.__mode = mode
        if not isinstance(self.__mode, (int, long)):
            self.__mode = pin_mode.OUT

        self.__initValue = value
        if not isinstance(self.__initValue, (int, long)):
            self.__initValue = pin_state.LOW

        self.__revision = board_revision.REV2
        self.__state = pin_state.LOW

    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("Gpio")

        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("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()

    def write(self, ps):
        """Write a value to the pin.

        :param int ps: The pin state value to write to the pin.
        :raises: raspy.object_disposed_exception.ObjectDisposedException if
        this instance has been disposed.
        """
        if self.is_disposed:
            raise ObjectDisposedException("Gpio")

        self.__state = ps

    def pulse(self, millis):
        """Pulse the pin output for the specified number of milliseconds.

        :param int, long millis: The number of milliseconds to wait between
        states.
        :raises: raspy.object_disposed_exception.ObjectDisposedException if
        this instance has been disposed.
        """
        if not isinstance(millis, (int, long)):
            millis = 0

        if self.is_disposed:
            raise ObjectDisposedException("Gpio")

        seconds = 0
        if millis > 0:
            seconds = millis / 1000

        self.write(pin_state.HIGH)
        time.sleep(seconds)
        self.write(pin_state.LOW)

    def read(self):
        """Read a value from the pin.

        :returns: The state (value) of the pin.
        :rtype: int
        :raises: raspy.object_disposed_exception.ObjectDisposedException if
        this instance has been disposed.
        """
        if self.is_disposed:
            raise ObjectDisposedException("Gpio")

        return pin_state.LOW

    def on_pin_state_change(self, psce):
        """Fire the pin state change event.

        :param raspy.io.pin_state_change_event.PinStateChangeEvent psce: The
        event object.
        :raises: raspy.object_disposed_exception.ObjectDisposedException if
        this instance is disposed.
        """
        if self.is_disposed:
            raise ObjectDisposedException("Gpio")

        _t = threading.Thread(target=self.emit,
                              name="stateChange",
                              args=(EVENT_GPIO_STATE_CHANGED, psce))
        _t.daemon = True
        _t.start()

    @property
    def revision(self):
        """Get the board revision.

        :returns: The board revision.
        :rtype: int
        """
        return self.__revision

    @property
    def state(self):
        """Get the state of the pin.

        :returns: The pin state.
        :rtype: int
        :raises: raspy.object_disposed_exception.ObjectDisposedException if
        this instance has been disposed.
        """
        self.__state = self.read()
        return self.__state

    @property
    def inner_pin(self):
        """Get the physical pin being represented by this instance.

        :returns: The underlying physical pin.
        :rtype: raspy.io.gpio_pins.GpioPin
        """
        return self.__pin

    def provision(self):
        """Provision this pin.

        :raises: raspy.object_disposed_exception.ObjectDisposedException if
        this instance has been disposed.
        """
        if self.is_disposed:
            raise ObjectDisposedException("Gpio")

        self.write(self.__initValue)

    @property
    def mode(self):
        """Get the pin mode.

        :returns: The pin mode.
        :rtype: int
        """
        return self.__mode

    @mode.setter
    def mode(self, p_mode):
        """Set the pin mode.

        :param int p_mode: The pin mode to set.
        :raises: raspy.object_disposed_exception.ObjectDisposedException if
        this instance has been disposed.
        """
        if self.is_disposed:
            raise ObjectDisposedException("Gpio")

        if p_mode is None:
            p_mode = p_mode.TRI

        if self.__mode != p_mode:
            self.__mode = p_mode
            self.provision()

    @property
    def address(self):
        """Get the pin address.

        :returns: The pin address.
        :rtype: int
        """
        return self.__pin.value

    def change_board_revision(self, revision):
        """Change the board revision.

        :param int revision: The board revision.
        """
        if revision is None or not isinstance(revision, (int, long)):
            revision = board_revision.REV2

        self.__revision = revision

    def get_initial_pin_value(self):
        """Get the initial pin value.

        :returns: The initial pin value.
        :rtype: int
        """
        return self.__initValue

    @property
    def pwm(self):
        """Get the PWM (pulse-width modulation) value.

        :returns: The PWM value.
        :rtype: int
        """
        return 0

    @pwm.setter
    def pwm(self, val):
        """Set the PWM (pulse-width modulation) value.

        :param int val: The PWM value.
        """
        pass

    @property
    def pwm_range(self):
        """Get the PWM (pulse-width modulation) range.

        :returns: The PWM range.
        :rtype: int
        """
        return 0

    @pwm_range.setter
    def pwm_range(self, rng):
        """Set the PWM (pulse-width modulation) range.

        :param int rng: The PWM range.
        """
        pass

    def dispose(self):
        """Dispose managed resources."""
        if self.is_disposed:
            return

        self.__emitter.remove_all_listeners()
        self.__emitter = None
        self.__state = None
        self.__mode = None
        self.__pin = None
        self.__initValue = None
        Pin.dispose(self)
示例#17
0
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
示例#18
0
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()
示例#19
0
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
示例#20
0
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
示例#21
0
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)
示例#22
0
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)
示例#23
0
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()
示例#24
0
class PowerInterface(Component):
    """An interface base type for power components."""
    def __init__(self):
        """Initialize a new instance of PowerInterface."""
        Component.__init__(self)
        self.__emitter = EventEmitter()
        self.__state = power_state.OFF

    def dispose(self):
        """Dispose managed resources."""
        if self.is_disposed:
            return

        self.__emitter.remove_all_listeners()
        self.__emitter = None
        Component.dispose(self)

    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(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("Motor")

        self.__emitter.on(evt, callback)

    def emit(self, evt, args=None):
        """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("Motor")

        self.__emitter.emit(evt, args)

    def on_power_state_changed(self, event_info):
        """Fire the power state change event.

        :param raspy.components.power.power_state_change_event.PowerStateChangeEvent event_info:
        The event info object.
        """
        if self.is_disposed:
            raise ObjectDisposedException("PowerInterface")

        _t = threading.Thread(target=self.emit,
                              name=EVENT_STATE_CHANGED,
                              args=(EVENT_STATE_CHANGED, event_info))
        _t.daemon = True
        _t.start()

    @property
    def state(self):
        """Get the state of the power component.

        :returns: The state of the power component.
        :rtype: int
        :raises: raspy.object_disposed_exception.ObjectDisposedException if
        this instance has been disposed.
        :raises: raspy.io.invalid_pin_mode_exception.InvalidPinModeException if
        the pin being used to control this device is not configure as an
        output.
        """
        return power_state.UNKNOWN

    @state.setter
    def state(self, pwr_state):
        """Set the state of the power component.

        :param int pwr_state: The state to set.
        :raises: raspy.object_disposed_exception.ObjectDisposedException if
        this instance has been disposed.
        :raises: raspy.io.invalid_pin_mode_exception.InvalidPinModeException if
        the pin being used to control this device is not configure as an
        output.
        :raises: raspy.invalid_operation_exception.InvalidOperationException if
        an invalid state is specified.
        """
        pass

    @property
    def is_on(self):
        """Check to see if the device is on.

        :returns: True if the device is on.
        :rtype: bool
        """
        return self.state == power_state.ON

    @property
    def is_off(self):
        """Check to see if the device is off.

        :returns: True if the device is off.
        :rtype: bool
        """
        return self.state == power_state.OFF

    def turn_on(self):
        """Turn the device on."""
        self.state = power_state.ON

    def turn_off(self):
        """Turn the device off."""
        self.state = power_state.OFF