This is a simple CoAP broker implemented in Python using the aiocoap
library. The broker creates and manages resources for storing data by implementing various Resource classes provided by aiocoap. The script follows publish-subscribe architecture for the Constrained Application Protocol (CoAP) defined at draft-ietf-core-coap-pubsub.
- Python 3.12 or higher
aiocoap
librarycbor2
librarycbor-diag
requires Rust and Cargo- You need to install the latest development version of aiocoap, which supports iPATCH.
pip3 install --upgrade "git+https://github.com/chrysn/aiocoap#egg=aiocoap[all]"
Run the CoAP broker:
python3 broker.py
The broker will start listening on 127.0.0.1:5683
, which you may want to update on your etc/hosts
to something like iot.dev
or similar.
You could use poetry
to manage dependencies. Simply run poetry install
to install all required packages, and then use poetry run python3 broker.py
to start the broker.
You may then run the simple demo and follow the instructions.
sh simple-demo.sh iot.dev
A CoAP server exposes a collection of topics as resources, each with a topic resource for administration and a topic-data resource for publishing and subscribing.
A client can create a topic as "admin":
poetry run python3 client.py -m POST coap://127.0.0.1:5683/ps --payload '{
"topic-name": "Room Temperature Sensor",
"resource-type": "core.ps.conf",
"media-type": "application/json",
"topic-type": "temperature",
"expiration-date": "2023-04-05T23:59:59Z",
"max-subscribers": 200,
"observer-check": 86400
}'
The broker will show:
Response arrived from different address; base URI is coap://127.0.0.1/ps
Location options indicate new resource: /ps/e99889
JSON re-formated and indented
{
"topic-name": "Room Temperature Sensor",
"topic-data": "ps/data/08dd75d",
"resource-type": "core.ps.conf",
"media-type": "application/json",
"topic-type": "temperature",
"expiration-date": "2023-04-05T23:59:59Z",
"max-subscribers": 200,
"observer-check": 86400
}
The broker will create the resource paths for both the topic and topic-data (ps/data/08dd75d
) resources.
Discover topics either via .well-known/core
or by querying the collection resource ps
.
You may discover the following resource types:
core.ps.coll
- the topic collection resource.core.ps.conf
- the topic resource.core.ps.data
- the topic-data resource.
❯ poetry run python3 client.py -m GET coap://127.0.0.1/ps
<ps/4fb3de>;rt="core.ps.conf"
or
poetry run python3 client.py -m GET coap://127.0.0.1/.well-known/core
application/link-format content was re-formatted
</.well-known/core>; ct=40,
</ps>; rt=core.ps.coll,
</ps/4fb3de>; ct=application/link-format; rt=core.ps.conf; obs,
</ps/data/a08b18d>; rt=core.ps.data; obs,
<https://christian.amsuess.com/tools/aiocoap/#version-0.4.4.post0>; rel=impl-info
or by rt
poetry run python3 client.py -m GET 'coap://127.0.0.1/.well-known/core?rt=core.ps.conf'
application/link-format content was re-formatted
</ps/dd4494>; ct=None; rt=core.ps.conf; obs,
</ps/cdc49a>; ct=application/json; rt=core.ps.conf; obs
or
poetry run python3 client.py -m GET 'coap://127.0.0.1/.well-known/core?rt=core.ps.coll'
application/link-format content was re-formatted
</ps>; rt=core.ps.coll
Any topic can be retrieved via its corresponding URI.
poetry run python3 client.py -m GET 'coap://127.0.0.1/ps/e99889'
{"topic-name": "Room Temperature Sensor", "topic-data": "ps/data/08dd75d", "resource-type": "core.ps.conf", "media-type": "application/json", "topic-type": "temperature", "expiration-date": "2023-04-05T23:59:59Z", "max-subscribers": 200, "observer-check": 86400}
From it, the associated topic-data can be interacted with providing it is FULLY created. For that a publisher needs to publish.
Properties of a topic can be updated on its corresponding URI.
poetry run python3 client.py -m PUT coap://127.0.0.1:5683/ps/b616a3 --payload "{\"max-subscribers\": 200}"
Response arrived from different address; base URI is coap://127.0.0.1/ps/b616a3
JSON re-formated and indented
{
"topic-name": "Room Temperature Sensor",
"topic-data": "ps/data/957d7fd",
"resource-type": "core.ps.conf",
"media-type": "application/json",
"topic-type": "temperature",
"expiration-date": "2023-04-05T23:59:59Z",
"max-subscribers": 200,
"observer-check": 86400
}
Properties of a topic can be updated on its corresponding URI.
poetry run python3 client.py -m iPATCH coap://127.0.0.1/ps/e99889 --payload "{\"max-subscribers\": 300}"
JSON re-formated and indented
{
"topic-name": "Room Temperature Sensor",
"topic-data": "ps/data/08dd75d",
"resource-type": "core.ps.conf",
"media-type": "application/json",
"topic-type": "temperature",
"expiration-date": "2023-04-05T23:59:59Z",
"max-subscribers": 300,
"observer-check": 86400
}
A client can filter a collection of topics with a topic filter in a FETCH request to the topic collection URI.
poetry run python3 client.py -m FETCH 'coap://127.0.0.1/ps' --content-format 'application/cbor' --payload '{"max-subscribers": 300}'
application/link-format content was re-formatted
<ps/e99889>; rt=core.ps.conf
As we mentioned above, topic and topic-data are different resources, topic is to configure the topic behaviour and topic-data is to perform the publish and subscribe operations.
Publishers use PUT to send data to a topic-data resource which is part of a topic. Subscribers use GET with Observe set to 0 to receive updates. A topic-data resource is created only after initial data is published. Before that, GET requests return 4.04 (Not Found). In this implementation URIs for topic resources are broker-generated, but they could also be hosted elsewhere.
A CoAP client can act as publisher by sending a CoAP PUT to a topic-data resource.
poetry run python3 client.py -m PUT coap://127.0.0.1:5683/ps/data/08dd75d --payload '{
"n": "temperature",
"u": "Cel",
"t": 1621452122,
"v": 21.3
}'
Response arrived from different address; base URI is coap://127.0.0.1/ps/data/08dd75d
{n: temperature,u: Cel,t: 1621452122,v: 21.3}
Subscribe to a topic by using CoAP GET
with --observe
option on the topic-data:
poetry run python3 client.py -m GET --observe coap://127.0.0.1:5683/ps/data/08dd75d
Response arrived from different address; base URI is coap://127.0.0.1/ps/data/08dd75d
{n: temperature,u: Cel,t: 1621452122,v: 21.3}
---
{n: temperature,u: Cel,t: 1621452122,v: 21.3}
The broker implements the following resource classes:
- CollectionResource: The collection resource
/ps
for storing topics. - TopicResource: A resource for topics.
- TopicDataResource: A resource for topic-data and for the publish-subscribe interactions over CoAP.
- Discovery
- GET /.well-known/core to discover collection
- Well known discovery with rt
- Topic Collection discovery
- Update to current list of Topic Properties on draft
- GET topic to discover topic configuration
- GET /ps to retrieve all topics
- FETCH
- multicast
- GET /.well-known/core to discover collection
- Configuration
- POST topic to create topic
- PUT to update topic configuration
- iPATCH to partially update topic configuration
- DELETE topic to delete topic
- Client defined topic-data url
- Topic Data
- PUT on topic-data to publish
- GET + observe on topic-data to Subscribe
- GET on topic-data to get last measurement
- Delete to delete topic-data
- Other
- Improve Broker Logic
- Fix Scripts
Disclaimer: There is lots of hardcoded stuff, as this was quickly developed during the IETF116 nad IETF118 hackathons.