Giter VIP home page Giter VIP logo

matter-mqtt-bridge's Introduction

Matter MQTT Bridge

This is an example implementation for a Matter Bridge that forwards Matter commands as MQTT messages. The messages are serialized in JSON using a custom data model and published to the MQTT broker with a structured topic.

The MQTT Topic uses the following scheme: <prefix>/<endpointID>/<clusterName>/<attributeName>, allowing:

Fine-grained subscription:

  • Device 1 subscribes to all its commands with filter: matter/1/#
  • Device 2 subscribes to all write requests to MoveToLevel attribute from the LevelControl cluster with filter: matter/1/LevelControl/MoveToLevel
  • A monitoring tool subscribes to all LevelControl commands: matter/+/LevelControl/+
  • A monitoring tool subscribes to all Matter messages: matter/#

QoS and message retainment:

  • Device 1 wakes up from sleep and wants to receive the last OnOff: matter/1/OnOff/OnOff
  • Device 1 wakes up from sleep and wants to receive the last command under each of its own sub-topic: matter/1/#

The prefix is configurable. Each endpoint corresponds to a single device.

The application is a fork of the connectedhomeip project's Linux bridge-app.

The application is packaged as a Snap. The following are the build and usage instructions for the snapped application. During the build, the files under app directory get copied into CHIP project's examples/bridge-app/linux. The reason for this approach is to maintain the simplicity of this example and rely on the existing upstream build configuration and scripts.

Build

snapcraft -v

This will download >500MB and requires around 8GB of disk space.

To build for other architectures, customize the architectures field inside the snapcraft.yaml and use snapcraft's Remote build.

Install

sudo snap install --dangerous *.snap

Configure

View default configurations

$ sudo snap get matter-mqtt-bridge
Key              Value
total-endpoints  1

Setting MQTT Server Address

sudo snap set matter-mqtt-bridge server-address="tcp://localhost:1883"

Setting MQTT Topic Prefix

sudo snap set matter-mqtt-bridge topic-prefix="test-topic-prefix"

This step is optional; the default topic prefix is "matter-bridge".

To see the list of all flags and SDK default, run the help app:

$ matter-mqtt-bridge.help 
...
Usage: /snap/matter-mqtt-bridge/x1/bin/chip-bridge-app [options

GENERAL OPTIONS

  --ble-device <number>
       The device number for CHIPoBLE, without 'hci' prefix, can be found by hciconfig.

  --wifi
       Enable WiFi management via wpa_supplicant.

  --thread
       Enable Thread management via ot-agent.
...

Setting the total number of endpoints

sudo snap set matter-mqtt-bridge total-endpoints=5

This step is optional; the default value of total endpoints is 1.

The bridge can be configured to support dynamic devices. For more information on using dynamic endpoints, please refer to here.

Grant access

The snap uses interfaces to allow access to external resources. Depending on the use case, you need to "connect" certain interfaces to grant the necessary access.

DNS-SD

The avahi-control is necessary to allow discovery of the application via DNS-SD:

sudo snap connect matter-mqtt-bridge:avahi-control

Note
To make DNS-SD discovery work, the host also needs to have a running avahi-daemon which can be installed with sudo apt install avahi-daemon or sudo snap install avahi.

BLE

Connect the bluez interface for device discovery over Bluetooth Low Energy (BLE):

sudo snap connect matter-mqtt-bridge:bluez

Run

sudo snap start matter-mqtt-bridge

Add --enable to make the service automatically start at boot.

Query and follow the logs:

sudo snap logs -n 100 -f matter-mqtt-bridge

Control with Chip Tool

For the following examples, we use the Chip Tool snap to commission and control the bridge app.

Commissioning

sudo snap connect chip-tool:avahi-observe
sudo chip-tool pairing onnetwork 110 20202021

where:

  • 110 is the assigned node id
  • 20202021 is the default passcode (pin code) for the lighting app

Command

Switching on/off:

sudo chip-tool onoff toggle 110 3 # toggle is stateless and recommended
sudo chip-tool onoff on 110 3
sudo chip-tool onoff off 110 3

where:

  • onoff is the matter cluster name
  • on/off/toggle is the command name
  • 110 is the node id of the bridge assigned during the commissioning
  • 3 is the endpoint of the configured device

Usage example

In this example, we control the matter-mqtt-bridge using chip-tool Matter Controller. The bridge is configured (as described above) to have a few endpoints and to forward and publish Matter commands to a local Mosquitto MQTT broker. We will use the Mosquitto MQTT client to subscribe to the messages which are the incoming matter commands.

  1. Subscribe to MQTT messages

Before sending a command via chip-tool, subscribe to MQTT messages using the mosquitto_sub MQTT client.

If you don't have a locally running MQTT broker, you can install the Mosquitto snap with the following command:

sudo snap install mosquitto

Then, subscribe to all MQTT topics to monitor incoming messages:

mosquitto_sub -h localhost -t "#" -v
  1. Follow the bridge logs

In a separate terminal window, you can check the logs from the bridge using the following command:

sudo snap logs -n 100 -f matter-mqtt-bridge
  1. Send commands to the bridge via chip-tool

Toggle a couple of devices at the corresponding endpoints:

sudo chip-tool onoff toggle 110 3
sudo chip-tool onoff toggle 110 5
  1. Check the bridge logs

After sending commands, you can monitor the bridge logs for relevant messages:

...
CHIP:DMG: AccessControl: allowed
CHIP:DMG: Received command for Endpoint=3 Cluster=0x0000_0006 Command=0x0000_0002
CHIP:DL: HandleReadOnOffAttribute: attrId=0, maxReadLength=1
CHIP:ZCL: Toggle ep3 on/off from state 0 to 1
CHIP:DL: HandleWriteOnOffAttribute: attrId=0
CHIP:DL: [MQTT] Using TOPIC_PREFIX: test-topic-prefix
CHIP:DL: Device[Light 1]: ON
CHIP:DL: [MQTT] Publishing message...
CHIP:DL: [MQTT] Message published.
...
CHIP:DMG: AccessControl: allowed
CHIP:DMG: Received command for Endpoint=5 Cluster=0x0000_0006 Command=0x0000_0002
CHIP:DL: HandleReadOnOffAttribute: attrId=0, maxReadLength=1
CHIP:ZCL: Toggle ep5 on/off from state 0 to 1
CHIP:DL: HandleWriteOnOffAttribute: attrId=0
CHIP:DL: [MQTT] Using TOPIC_PREFIX: test-topic-prefix
CHIP:DL: Device[Light 3]: ON
CHIP:DL: [MQTT] Publishing message...
CHIP:DL: [MQTT] Message published.
...
  1. Check MQTT messages

Simultaneously, you should also see messages being received by the MQTT client:

test-topic-prefix/3/OnOff/OnOff {
        "attributeId" : 0,
        "clusterId" : 6,
        "command" : "on",
        "deviceName" : "Light 1",
        "endpointId" : 3,
        "location" : "Office",
        "parentEndpointId" : 1,
        "zone" : ""
}
...
test-topic-prefix/5/OnOff/OnOff {
        "attributeId" : 0,
        "clusterId" : 6,
        "command" : "on",
        "deviceName" : "Light 3",
        "endpointId" : 5,
        "location" : "Office",
        "parentEndpointId" : 1,
        "zone" : ""
}

matter-mqtt-bridge's People

Contributors

farshidtz avatar monicaisher avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

matter-mqtt-bridge's Issues

Return fatal error in ApplicationInit()

void ApplicationInit()
{
const bool kThreadEnabled = {
#if CHIP_DEVICE_CONFIG_ENABLE_THREAD
LinuxDeviceOptions::GetInstance().mThread
#else
false
#endif
};
const bool kWiFiEnabled = {
#if CHIP_DEVICE_CONFIG_ENABLE_WIFI
LinuxDeviceOptions::GetInstance().mWiFi
#else
false
#endif
};
if (kThreadEnabled && kWiFiEnabled)
{
// Just use the Thread one.
#if CHIP_DEVICE_CONFIG_ENABLE_THREAD
sThreadNetworkCommissioningInstance.Init();
#endif
}
else if (kThreadEnabled)
{
#if CHIP_DEVICE_CONFIG_ENABLE_THREAD
sThreadNetworkCommissioningInstance.Init();
#endif
}
else if (kWiFiEnabled)
{
#if CHIP_DEVICE_CONFIG_ENABLE_WIFI
sWiFiNetworkCommissioningInstance.Init();
#endif
}
else
{
sEthernetNetworkCommissioningInstance.Init();
}
// MOTT Init
char *envSERVER_ADDRESS = std::getenv(SERVER_ADDRESS);
if (envSERVER_ADDRESS == nullptr || strlen(envSERVER_ADDRESS) == 0)
{
ChipLogProgress(DeviceLayer, "[MQTT] Environment variable not set or empty: %s", SERVER_ADDRESS);
ChipLogProgress(DeviceLayer, "[MQTT] Initialization failed due to missing or empty SERVER_ADDRESS");
return;

This should be a fatal error.

Originally posted by @farshidtz in #1 (comment)

In order to return a fatal error, we might consider modifying the return value of ApplicationInit(). Another option could be to throw an exception or return the fatal error via an out parameter and handle it accordingly.

Check AppArmor denials

Thanks. Works as expected.
I noticed the following error and AppArmor denials, but they don't seem to affect functionality of the text cases.

2023-08-25T13:42:31+02:00 matter-mqtt-bridge.matter-mqtt-bridge[58118]: [1692963751.168702][58118:58119] CHIP:DL: TRACE: Bus acquired for name MATTER-3840
2023-08-25T13:42:31+02:00 matter-mqtt-bridge.matter-mqtt-bridge[58118]: [1692963751.168752][58118:58119] CHIP:DL: CREATE service object at /chipoble/e306/service
2023-08-25T13:42:31+02:00 matter-mqtt-bridge.matter-mqtt-bridge[58118]: [1692963751.168917][58118:58119] CHIP:DL: Create characteristic object at /chipoble/e306/service/c1
2023-08-25T13:42:31+02:00 matter-mqtt-bridge.matter-mqtt-bridge[58118]: [1692963751.169052][58118:58119] CHIP:DL: Create characteristic object at /chipoble/e306/service/c2
2023-08-25T13:42:31+02:00 matter-mqtt-bridge.matter-mqtt-bridge[58118]: [1692963751.169112][58118:58119] CHIP:DL: CHIP BTP C1 /chipoble/e306/service
2023-08-25T13:42:31+02:00 matter-mqtt-bridge.matter-mqtt-bridge[58118]: [1692963751.169115][58118:58119] CHIP:DL: CHIP BTP C2 /chipoble/e306/service
2023-08-25T13:42:31+02:00 matter-mqtt-bridge.matter-mqtt-bridge[58118]: [1692963751.169117][58118:58119] CHIP:DL: CHIP_ENABLE_ADDITIONAL_DATA_ADVERTISING is FALSE
2023-08-25T13:42:31+02:00 matter-mqtt-bridge.matter-mqtt-bridge[58118]: [1692963751.169997][58118:58119] CHIP:DL: FAIL: Error getting object manager client: GDBus.Error:org.freedesktop.DBus.Error.AccessDenied: An AppArmor policy prevents this sender from sending this message to this recipient; type="method_call", sender=":1.164" (uid=0 pid=58118 comm="/snap/matter-mqtt-bridge/x1/bin/chip-bridge-app" label="snap.matter-mqtt-bridge.matter-mqtt-bridge (enforce)") interface="org.freedesktop.DBus.ObjectManager" member="GetManagedObjects" error name="(unset)" requested_reply="0" destination=":1.12" (uid=0 pid=1296 comm="/usr/lib/bluetooth/bluetoothd" label="unconfined")
= Seccomp =
Time: 2023-08-25T13:4
Log: auid=4294967295 uid=0 gid=0 ses=4294967295 subj=snap.matter-mqtt-bridge.matter-mqtt-bridge pid=57974 comm="gmain-matter" exe="/snap/matter-mqtt-bridge/x1/bin/chip-bridge-app" sig=0 arch=c000003e 314(sched_setattr) compat=0 ip=0x7fb696f4ca3d code=0x50000
Syscall: sched_setattr
Suggestion:
* add 'process-control' to 'plugs'

= AppArmor =
Time: 2023-08-25T13:4
Log: apparmor="DENIED" operation="dbus_method_call"  bus="system" path="/" interface="org.freedesktop.DBus.ObjectManager" member="GetManagedObjects" mask="send" name=":1.12" pid=57974 label="snap.matter-mqtt-bridge.matter-mqtt-bridge" peer_pid=1296 peer_label="unconfined"
DBus access

= Seccomp =
Time: 2023-08-25T13:4
Log: auid=4294967295 uid=0 gid=0 ses=4294967295 subj=snap.matter-mqtt-bridge.matter-mqtt-bridge pid=58118 comm="gmain-matter" exe="/snap/matter-mqtt-bridge/x1/bin/chip-bridge-app" sig=0 arch=c000003e 314(sched_setattr) compat=0 ip=0x7f00ef6dda3d code=0x50000
Syscall: sched_setattr
Suggestion:
* add 'process-control' to 'plugs'

= AppArmor =
Time: 2023-08-25T13:4
Log: apparmor="DENIED" operation="dbus_method_call"  bus="system" path="/" interface="org.freedesktop.DBus.ObjectManager" member="GetManagedObjects" mask="send" name=":1.12" pid=58118 label="snap.matter-mqtt-bridge.matter-mqtt-bridge" peer_pid=1296 peer_label="unconfined"
DBus access

Originally posted by @farshidtz in #1 (review)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.