Ejemplo n.º 1
0
    def from_blueprint(self, plant: DevicePlant, blueprint: Dict[str, Any], owner: CompoundDevice = None) \
            -> DeviceAssembly:
        """ Given a plan dictionary as above, construct the device.

            The upstream is constructed with the following operations:
                1. The main data stream is filtered for messages on the switch's parent node's upstream topic;
                2. Then, messages are decoded to a dictionary format from, e.g, JSON;
                3. The resultant dictionary is further filtered by this device's ID (to separate out from possible
                   multiple devices in the message);
                4. Then, the message payload is extracted;
                5. Finally, the message state is set via a tap.
            At its end, the upstream flow presents an Observable for use by clients. This flow contains just messages
            from the specific device.

            The downstream is constructed with the following operations:
                1. The input payload is put in a dictionary with the device ID as the key.
                2. The result is encoded with the device's coder.
                3. A dictionary with the topic and the encoded message is created.

            :param plant:     The device plant the assembly is being added to.
            :param blueprint: A blueprint in the form of the dictionary above.
            :param owner:     The owner (usually the node) for this device
            :returns: A device bundle with the BasicSwitch device object and the up/downstream data sources/sinks.

            TODO: Currently just handles state. Add configuration too.
        """
        device = BasicSwitch(blueprint['device_id'])
        upstream_ops = (
            plant.upstream_source
            | Op.filter(lambda msg, dev=device: msg['topic'].startswith(
                dev.get_node().topic_prefix()))
            | Op.map(lambda msg, dev=device: dev.get_node().coder.decode(msg[
                'message']))
            | Op.filter(lambda msg_dict, dev=device: dev.device_id in msg_dict)
            | Op.map(lambda msg_dict, dev=device: msg_dict[dev.device_id])
            | Op.tap(lambda dev_msg, dev=device: dev.state.set_value(dev_msg[
                'state'])))
        downstream_source = AsyncStreamFromQueue(asyncio.Queue())
        downstream_ops = (
            downstream_source
            | Op.tap(lambda msg, dev=device: dev.state.set_value(msg['state']))
            | Op.map(lambda msg, dev=device: {dev.device_id: msg})
            | Op.map(lambda msg, dev=device: dev.get_node().coder.encode(msg))
            | Op.map(
                lambda msg, dev=device: {
                    'topic':
                    topic_name(f'{dev.get_node().topic_prefix()}',
                               TOPIC_DIRECTION_DOWNSTREAM),
                    'message':
                    msg
                }))
        return self.assemble(plant, device, upstream_ops, downstream_source,
                             downstream_ops)
Ejemplo n.º 2
0
 async def send_message(node: NodeDevice):
     in_queue = janus.Queue()
     source = AsyncStreamFromQueue(cast(asyncio.Queue,
                                        in_queue.async_q))
     up_streams = []
     for (key, device) in node.sub_devices.items():
         # Stream operations:
         #   1. Only forward messages on topics belonging to the node
         #   2. Decode the message from JSON into a dictionary
         #   3. Only forward messages to the device that are intended (or partly intended) for it
         #   4. Pick out the part of the message intended for the device (each root key represents a device)
         #   5. Tap the stream to store new device state values.
         up_stream = (
             source
             | Op.filter(lambda msg: msg['topic'].startswith(
                 node.topic_prefix()))
             | Op.map(lambda msg: node.coder.decode(msg['message']))
             | Op.filter(lambda msg_dict, device_id=device.device_id:
                         device_id in msg_dict)
             | Op.map(lambda msg_dict, device_id=device.device_id:
                      msg_dict[device_id])
             | Op.tap(lambda dev_msg, dev=device: dev.state.set_value(
                 dev_msg['state'])))
         up_streams.append(
             MessagePipe(source, up_stream,
                         AsyncStreamToQueue(asyncio.Queue())))
     messages = [
         {
             'topic': 'hausnet/vendorname_switch/ABC012/upstream',
             'message': '{"switch_1": {"state": "OFF"}}'
         },
         {
             'topic': 'hausnet/vendorname_switch/ABC012/upstream',
             'message': '{"switch_2": {"state": "ON"}}'
         },
         {
             'topic': 'hausnet/vendorname_switch/ABC012/upstream',
             'message': '{"switch_1": {"state": "UNDEFINED"}}'
         },
     ]
     for message in messages:
         in_queue.sync_q.put(message)
     source.start()
     await source.queue.join()
     source.stop()
     messages = []
     for stream in up_streams:
         while stream.sink.queue.qsize() > 0:
             messages.append(await stream.sink.queue.get())
             stream.sink.queue.task_done()
     return messages