Self hosted Web App panel for MQTT
This project provides a self hostable service that connects to a MQTT broker and serves a progressive web app panel which is fully configurable via YAML. It aims to be a simple panel that gives user interactivity with MQTT topics. Suitable for standalone or MQTT microservice deployments, and can be deployed alongside home automation solutions. It does not offer higher level capabilities such as automations, integrations or scheduling.
Run
docker run -it --rm -p 8080:8080 sourcesimian/mqtt-panel:latest
and browse to http://localhost:8080 which will appear as:
Prebuilt container images are available on Docker Hub.
Run
mkdir -p $HOME/.cache/mqtt-panel
docker run -n mqtt-panel -d -it --rm -p 8080:8080 \
--volume my-config.yaml:/config.yaml:ro \
--volume $HOME/.cache/mqtt-panel:/data/cache:rw \
sourcesimian/mqtt-panel:latest
Configure your Deployment to suffice the docker configuration above. Additionally you can add a liveness endpoint at /api/health
on the configured http port. To perform SSL endpoint termination you can add an ingress controller such as Traefik which comes standard with K3s.
A typical Deployment might include:
volumes:
- name: config
configMap:
name: mqtt-panel-config
- name: data
hostPath:
path: /mnt/mqtt-panel/data
type: DirectoryOrCreate
containers:
- name: mqtt-panel
image: sourcesimian/mqtt-panel:latest
ports:
- containerPort: 8080
volumeMounts:
- name: config
mountPath: /config.yaml
subPath: config.yaml
- name: data
mountPath: /data
livenessProbe:
initialDelaySeconds: 30
periodSeconds: 30
httpGet:
path: /api/health
port: 8080
Kubernetes allows for the config file to be supplied in various ways. Using a ConfigMap the following commands are a convienient way to supply and update directly from your YAML:
kubectl -n "$NAMESPACE" delete configmap mqtt-panel-config &>/dev/null || true
kubectl -n "$NAMESPACE" create configmap mqtt-panel-config \
--from-file=config.yaml=./config-my.yaml
kubectl -n "$NAMESPACE" rollout restart deploy mqtt-panel
An installation of mqtt-panel will need a MQTT broker to connect to. There are many possibilities available. In the demo EMQ X, a free Open-Source, Cloud-Native broker, is used. YOu can subscribe to sourcesimian/mqtt-panel/demo/#
with your favourite MQTT viewer. Eclipse Mosquitto is a great self-hosted option with many ways of installation including pre-built containers on Docker Hub.
To compliment your MQTT infrastructure you may consider the following other microservices:
Service | Description |
---|---|
mqtt-gpio | Connects MQTT topics to GPIO pins. |
mqtt-ical | Publishes values to MQTT topics based on events in an iCal Calendar. |
mqtt-kube | Maps Kubernetes object values to and from topics on MQTT. |
NodeRED | A flow-based visual programming tool for wiring together devices, with built in MQTT integration and many others available. Can easily be used to add higher level behaviours. |
mqtt-panel
consumes a single YAML file. To start off you can copy config-basic.yaml
The following are conventions that are used in the YAML configuration:
Item | Description |
---|---|
<string> |
Any string of characters, preferably "quoted" to avoid YAML from interpreting in a different way. |
<icon> |
These are material-icons from the Google Fonts library. |
<color> |
Is any HTML style color, e.g. red , "#F04040" , rgb(240, 64, 64) , etc. Use "quotes" to ensure that the # is not interpreted as a comment. |
<topic> |
A MQTT topic, e.g. fizz/buzz/status . Subscriptions can also accept the * and # wildcards. Use "quotes" to ensure that the # is not interpreted as a comment. |
<identifier> |
A string of alpha-numeric characters, and _ |
mqtt:
host: <host> # optional: MQTT broker host, default: 127.0.0.1
port: <port> # optional: MQTT broker port, default 1883
client-id: mqtt-panel # MQTT client identifier, often brokers require this to be unique
topic-prefix: <topic prefix> # optional: Scopes the MQTT topic prefix
auth: # optional: Defines the authentication used to connect to the MQTT broker
type: <type> # Auth type: none|basic|mtls, default: none
... (<type> specific options)
type: basic
username: <string> # MQTT broker username
password: <string> # MQTT broker password
type: mtls
cafile: <file> # CA file used to verify the server
certfile: <file> # Certificate presented by this client
keyfile: <file> # Private key presented by this client
keyfile_password: <string> # optional: Password used to decrypt the `keyfile`
protocols:
- <string> # optional: list of ALPN protocols to add to the SSL connection
http:
bind: <bind> # optional: Interface on which web server will listen, default 0.0.0.0
port: <port> # Port on which web server will listen, default 8080
max-connections: <integer> # optional: Limit the number of concurrent connections, default 100
logging-level: <level> # optional: Select logging level of HTTP requests, default: INFO
auth: # User Auth
users: # optional: User/password auth
- username: <string>
password: <string>
cache: # Configure cache
root: <path> # optional root path, default ./cache
logging: # Logging settings
level: INFO # optional: Logging level, default DEBUG
mqtt-panel
is divided into panels, one panel is show at a time, each panel is a collection of groups.
panels:
- title: <string> # Panel title text
icon: <icon> # Icon shown on the menu bar
groups: # list of group identifiers
- <identifier> # e.g. "group_one"
... (repeat)
A group is a boxed collection of widgets. They can be reused on multiple panels.
groups:
- title: <string> # Title text
name: <identifier> # Identifier, e.g. "group_one"
widgets: # List of widgets in ths group
... (widgets)
... (repeat)
A widget is a functional element, which is used to publish and/or subscribe to MQTT topics, and display and/or input some payload.
All widgets have the following common attributes.:
- title: <string> # Title text
type: <type> # Widget type
qos: [0 | 1 | 2] # optional: MQTT QoS to use, default: 1
retain: [False | True] # optional: Publish with MQTT retain flag, default: False
cache: [False | True] # optional: Cache last seen payloads, default: False
ref: <widget reference> # optional: Identifier string for widget reuse.
retain
is a flag that is set when publishing a payload to MQTT. If set the message will persist in the broker, clients will re-receive that payload when reconnecting. This does not always give the desired behaviour.
You will note that at startup some widgets show "unknown" until a payload on the subscribed MQTT topic is recieved. To improve user experience of mqtt-panel cache: True
will preserve the last seen payload for a widget. This enables the server to immediately show the last known state after a restart, even with a MQTT topic using retain: False
.
To reuse a widget add the ref
attribute, and then add the widget to other groups as:
- ref: <widget reference> # Identifier of widget to reuse
Simply display the payload of the subscribed MQTT topic.
- title: <string> # Title text
type: text # Widget type
subscribe: <topic> # MQTT topic to listen on
color: <color> # optional: Color of the text
Example:
- title: My Text
type: text
subscribe: text/content
color: "#123456"
Display some text, an icon and color when the defined payloads are received from the subscribed topic.
- title: <string> # Title text
type: light # Widget type
subscribe: <topic> # MQTT topic to listen on
values:
- payload: <string> # Payload to match for this value
text: <string> # optional: Text shown
icon: <icon> # optional: Icon shown
color: <color> # optional: Color of icon and text
... (repeat)
Example:
- title: My Light
type: light
subscribe: light/state
values:
- payload: "false"
text: OFF
color: black
icon: light
- payload: "true"
text: ON
color: yellow
icon: light
Publish a constant value to a MQTT topic.
- title: <string> # Title text
type: button # Widget type
text: <string> # optional: Text to show on widget
publish: <topic> # MQTT topic to write to
payload: <string> # MQTT payload to publish
Example:
- title: My Button
type: button
text: Push Me
publish: button/command
payload: PRESSED
Publish the next payload in the list of values to a topic. Update the display with text, icon and color when the payload returns on the subscribed topic.
- title: <string> # Title text
type: switch # Widget type
publish: <topic> # MQTT topic to write to
subscribe: <topic> # MQTT topic to listen on
values:
- payload: <string> # Payload to match for this value
text: <string> # optional: Text shown
icon: <icon> # optional: Icon shown
color: <color> # optional: Color of icon and text
... (repeat)
Example:
- title: My Switch
type: switch
publish: widget/switch/command
subscribe: widget/switch/state
values:
- text: "Off"
payload: "false"
- text: "On"
payload: "true"
Show the received value and a vertical bar gauge where the text, icon and color will change based on the value of the subscribed payload.
- title: <string> # Title text
type: gauge # Widget type
subscribe: <topic> # MQTT topic to listen on
text: <string> # optional: The default text when not given with range
color: <color> # optional: The default color when not given with range
icon: <icon> # optional: The default icon when not given with range
ranges:
- range: [<int>, <int>] # Value for start and end of range
text: <string> # optional: Text shown when value in range
color: <color> # optional: Color shown when value in range
icon: <icon> # optional: Icon shown when value in range
... (repeat)
# max and min value will be determined from starts and ends
Example:
- title: Sound
type: gauge
subscribe: example/volume
ranges:
- range: [0, 10]
text: "Quiet"
icon: volume_off
color: "#00c000"
- range: [10, 30]
text: "Gentle"
icon: volume_mute
color: "#02b002"
- range: [30, 70]
text: "Medium"
icon: volume_down
color: "#82b002"
- range: [70, 90]
text: "Noisy"
icon: volume_up
color: "#b08a02"
- range: [90, 100]
text: "Loud"
icon: volume_up
color: "#b03c02"
Show the received value and a vertical bar gauge where the text, icon and color will change based on the value of the subscribed payload. Additionally when tapped, show a slider which can be used to input and publish a value between the max and min value.
- title: <string> # Title text
type: slider # Widget type
live: [False | True] # optional: Realtime publishing. Default: False
... (same as gauge)
Setting live: True
the current value of the slider will be published as it changes. The default behaviour is to publish only the final selected value when the slider is released.
Display some text, an icon and color when the defined payloads are received from the subscribed topic. When tapped, shows a list of the other values which can be published.
- title: <string> # Title text
type: select # Widget type
publish: <topic> # MQTT topic to write to
subscribe: <topic> # optional: MQTT topic to listen on
values:
- payload: <string> # Payload to send and match
text: <string> # optional: Text shown
icon: <icon> # optional: Icon shown
color: <color> # optional: Color of icon and text
... (repeat)
Example:
- title: My Select
type: select
publish: widget/select/command
subscribe: widget/select/state
values:
- text: "Venice"
payload: "Gondola"
icon: rowing
color: cyan
- text: "Cape Town"
payload: "Mountain"
icon: landscape
color: green
Display content in a <iframe>
. The src
attribute can be bound to a MQTT topic.
- title: <string> # Title text
type: iframe # Widget type
subscribe: <topic> # optional: MQTT topic to listen on, bound to iframe 'src'
refresh: <seconds> # optional: Interval at which to refresh the iframe
attr: # Attributes to be set on the iframe
src: <url> # optional: Can be set as a default vaule for 'src'
... # additional attributes
Example:
- title: Iframe
type: iframe
subscribe: iframe/src
attr:
src: https://www.youtube.com/embed/dQw4w9WgXcQ
width: 480px
height: 315px
title: YouTube video player
allow: accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture
allowfullscreen:
Yes sure! And please. I built mqtt-panel because I couldn't find a "I'm not ready to commit to full blown HA yet" solution that was self hosted and server side configurable. I don't know much about contemporary HTML, CSS and Typescript so I will gladly accept advice from those who know more. I want it to be a project that is quick and easy to get up and running, and helps open up MQTT to anyone. CHANGELOG.md
Before pushing a PR please consider adding unit tests and ensure that make check
and make test
are clean.
Setup the virtualenv:
python3 -m venv virtualenv
. ./virtualenv/bin/activate
python3 ./setup.py develop
Run the server:
mqtt-panel ./config-demo.yaml
In the spirit of the Hackers of the Tech Model Railroad Club from the Massachusetts Institute of Technology, who gave us all so very much to play with. The license is MIT.