예제 #1
0
def get_client_with_messages(msgs: dict) -> HomieClient:
    c = HomieClient()

    for topic, payload in msgs.items():
        msg = MQTTMessage()
        msg.topic = topic.encode('utf-8')
        msg.payload = payload.encode('utf-8')
        c.on_message(None, None, msg)

    return c
예제 #2
0
async def test_read_write(mqtt, client_id):
    """Test MQTT transport read and write."""
    mqtt_client = mqtt.return_value
    topic = f"{IN_PREFIX}/0/255/3/1/11"
    payload = "test"
    msg = MQTTMessage()
    msg.topic = topic.encode()
    msg.payload = payload.encode()
    messages = [msg]

    async def mock_messages():
        """Mock the messages generator."""
        for message in messages:
            yield message

    @asynccontextmanager
    async def filter_messages():
        """Mock filter messages."""
        yield mock_messages()

    mqtt_client.unfiltered_messages.side_effect = filter_messages

    transport = MQTTClient(HOST, PORT, IN_PREFIX, OUT_PREFIX)

    await transport.connect()

    assert mqtt.call_count == 1
    assert mqtt.call_args == call(
        HOST,
        PORT,
        client_id=client_id,
        logger=PAHO_MQTT_LOGGER,
        clean_session=True,
    )

    await asyncio.sleep(0)
    read = await transport.read()
    assert read == "0;255;3;1;11;test"

    await transport.write(read)
    assert mqtt_client.publish.call_count == 1
    assert mqtt_client.publish.call_args == call(
        f"{OUT_PREFIX}/0/255/3/1/11", qos=1, retain=False, timeout=10, payload=payload
    )

    await transport.disconnect()

    assert mqtt_client.disconnect.call_count == 1
예제 #3
0
async def test_read_failure(mqtt, client_id):
    """Test MQTT transport read failure."""
    mqtt_client = mqtt.return_value
    topic = f"{IN_PREFIX}/0/0/0/0/0"
    payload = "test"
    msg = MQTTMessage()
    msg.topic = topic.encode()
    msg.payload = payload.encode()
    messages = [msg]

    async def mock_messages():
        """Mock the messages generator."""
        for message in messages:
            yield message

    @asynccontextmanager
    async def filter_messages():
        """Mock filter messages."""
        yield mock_messages()
        raise MqttError("Boom")

    mqtt_client.unfiltered_messages.side_effect = filter_messages

    transport = MQTTClient(HOST, PORT, IN_PREFIX, OUT_PREFIX)

    await transport.connect()

    assert mqtt.call_count == 1
    assert mqtt.call_args == call(
        HOST,
        PORT,
        client_id=client_id,
        logger=PAHO_MQTT_LOGGER,
        clean_session=True,
    )

    await asyncio.sleep(0)
    read = await transport.read()

    assert read == "0;0;0;0;0;test"

    with pytest.raises(TransportFailedError):
        await transport.read()

    await transport.disconnect()

    assert mqtt_client.disconnect.call_count == 1
예제 #4
0
def test_mqtt_handle_on_message_save_device_data(app_and_ctx, capsys):
    device_id = 9999  # not present
    tid_bi = 'b209eba637a54f1f617cf5a6f925e4eb9fc083e66029061018b369e64b9864d7'  # blind_index(hex_to_key("622c23fe2623e54ba103c13b88072ca3fdc5836fc459cb2b5c31d8df3f07ebc2"), "8")
    payload = b"{'added': 6987, 'num_data': 31164, 'data': 'gAAAAABcTFAz9Wr5ZsnMcVYbQiXlnZCvT36MfDatZNyLwDpm_ixbzkZhM1NA4w7MN2p3CW3gyTA8gYtuKtDTomhulszvLTFfPA==', 'tid': 'encrypted_tid(8)', 'tid_bi': '%b', 'correctness_hash': '$2b$12$9hxKg4pjXbm0kpbItQTd2uMICAGn2ntRw1qQskHIL/7tLa3ISIlmO'}" % tid_bi.encode(
    )

    topic = b"d:%a/server/save_data" % device_id
    msg = MQTTMessage(topic=topic)
    msg.payload = payload

    invalid_topic = b"d:%a/server/invalid" % device_id
    msg_invalid = MQTTMessage(topic=invalid_topic)
    msg_invalid.payload = payload

    from app.mqtt.mqtt import handle_on_message
    app, ctx = app_and_ctx
    with app.app_context():
        handle_on_message(None, None, msg_invalid, app, db)
        captured = capsys.readouterr()
        assert f"Invalid topic: {msg_invalid.topic}" in captured.out

        handle_on_message(None, None, msg, app, db)
        captured = capsys.readouterr()
        assert f"Device with id: {device_id} doesn't exist." in captured.out
        device_data = db.session.query(DeviceData).filter(
            and_(
                DeviceData.tid_bi == tid_bi,
                DeviceData.device_id == device_id,
            )).first()
        assert device_data is None
        msg.payload = payload  # reassign, because `handle_on_message` causes side-effect and converts it to `dict`
        device_id = 23
        topic = b"d:%a/server/save_data" % device_id
        msg.topic = topic

        handle_on_message(None, None, msg, app, db)

        device_data = db.session.query(DeviceData).filter(
            and_(
                DeviceData.tid_bi == tid_bi,
                DeviceData.device_id == device_id,
            )).first()
        assert device_data is not None
        assert device_data.added == 6987
        assert device_data.num_data == 31164
예제 #5
0
def test_subscribe(mock_broker, mocker):
    mqttc = MqttClient(port=1883, callback=callback)
    mqttc.subscribe(topic="sensor/temperature")

    # create a fake MQTT message
    msg = MQTTMessage(mid=MID)
    msg.topic = b"sensor/temperature"
    msg.payload = b"22.5"

    mocker.spy(mqttc, "callback")

    # trigger 2 messages received on the MQTT bus
    mqttc._client.on_message(mqttc._client, None, msg)
    mqttc._client.on_message(mqttc._client, None, msg)

    mqttc.stop()

    assert mqttc.callback.call_count == 2
예제 #6
0
def test_integration():
    device_discovered = Mock()
    node_discovered = Mock()
    property_discovered = Mock()
    device_updated = Mock()
    node_updated = Mock()
    property_updated = Mock()

    c = HomieClient()

    c.on_device_discovered = device_discovered
    c.on_node_discovered = node_discovered
    c.on_property_discovered = property_discovered

    c.on_device_updated = device_updated
    c.on_node_updated = node_updated
    c.on_property_updated = property_updated

    with open('tests/messages.txt') as f:
        for line in f.readlines():
            topic, payload = line.split(' ', 1)
            msg = MQTTMessage()
            msg.topic = topic.encode('utf-8')
            msg.payload = payload.strip().encode('utf-8')
            c.on_message(None, None, msg)

    assert c.sensor1.state == 'ready'
    assert len(c.sensor1.nodes) == 2
    assert len(c.sensor1.dht.properties) == 2
    assert len(c.sensor1.bmp.properties) == 1
    assert c.sensor1.dht.temperature == {
        'name': 'Temperature',
        'unit': '°C',
        'value': 19.82
    }
    assert c.sensor1.dht.humidity == {
        'name': 'Humidity',
        'unit': '%',
        'value': 61.9
    }
    assert c.sensor1.bmp.pressure == {
        'name': 'Pressure',
        'unit': 'mbar',
        'value': 1021.69
    }
    assert c.powermeter.state == 'ready'
    assert len(c.powermeter.nodes) == 1
    assert len(c.powermeter.powermeter.properties) == 2
    assert c.powermeter.powermeter.power_delivered == {
        'name': 'Power delivered',
        'unit': 'W',
        'value': 410
    }
    assert c.powermeter.powermeter.power_returned == {
        'name': 'Power returned',
        'unit': 'W',
        'value': 1500
    }

    device_discovered.assert_has_calls(
        [call(c.powermeter), call(c.sensor1)], any_order=True)
    node_discovered.assert_has_calls([
        call(node)
        for node in [c.powermeter.powermeter, c.sensor1.bmp, c.sensor1.dht]
    ],
                                     any_order=True)
    property_discovered.assert_has_calls([
        call(e[0], e[1])
        for e in [(c.sensor1.dht, 'temperature'), (c.sensor1.dht, 'humidity'),
                  (c.sensor1.bmp,
                   'pressure'), (c.powermeter.powermeter, 'power_delivered'),
                  (c.powermeter.powermeter, 'power_returned')]
    ],
                                         any_order=True)

    property_updated.assert_has_calls([
        call(e[0], e[1],
             e[2]) for e in [(c.sensor1.dht, 'temperature', {
                 'name': 'Temperature',
                 'unit': '°C',
                 'value': 20.12
             }),
                             (c.sensor1.dht, 'temperature', {
                                 'name': 'Temperature',
                                 'unit': '°C',
                                 'value': 20.08
                             }),
                             (c.sensor1.dht, 'temperature', {
                                 'name': 'Temperature',
                                 'unit': '°C',
                                 'value': 19.82
                             }),
                             (c.sensor1.dht, 'humidity', {
                                 'name': 'Humidity',
                                 'unit': '%',
                                 'value': 63.5
                             }),
                             (c.sensor1.dht, 'humidity', {
                                 'name': 'Humidity',
                                 'unit': '%',
                                 'value': 63.2
                             }),
                             (c.sensor1.dht, 'humidity', {
                                 'name': 'Humidity',
                                 'unit': '%',
                                 'value': 61.9
                             }),
                             (c.sensor1.bmp, 'pressure', {
                                 'name': 'Pressure',
                                 'unit': 'mbar',
                                 'value': 1021.7
                             }),
                             (c.sensor1.bmp, 'pressure', {
                                 'name': 'Pressure',
                                 'unit': 'mbar',
                                 'value': 1021.71
                             }),
                             (c.sensor1.bmp, 'pressure', {
                                 'name': 'Pressure',
                                 'unit': 'mbar',
                                 'value': 1021.69
                             }),
                             (c.powermeter.powermeter, 'power_delivered', {
                                 'name': 'Power delivered',
                                 'unit': 'W',
                                 'value': 356
                             }),
                             (c.powermeter.powermeter, 'power_delivered', {
                                 'name': 'Power delivered',
                                 'unit': 'W',
                                 'value': 390
                             }),
                             (c.powermeter.powermeter, 'power_delivered', {
                                 'name': 'Power delivered',
                                 'unit': 'W',
                                 'value': 410
                             }),
                             (c.powermeter.powermeter, 'power_returned', {
                                 'name': 'Power returned',
                                 'unit': 'W',
                                 'value': 1300
                             }),
                             (c.powermeter.powermeter, 'power_returned', {
                                 'name': 'Power returned',
                                 'unit': 'W',
                                 'value': 1400
                             }),
                             (c.powermeter.powermeter, 'power_returned', {
                                 'name': 'Power returned',
                                 'unit': 'W',
                                 'value': 1500
                             })]
    ],
                                      any_order=True)