Compare commits
No commits in common. "de58f0c8f5fc9dda3514ce7b5d01b5ca64008360" and "ee90c99080404563b25465b4786b31916d4fe2f5" have entirely different histories.
de58f0c8f5
...
ee90c99080
18 changed files with 7 additions and 511 deletions
|
@ -12,6 +12,7 @@ trusted_ranges:
|
||||||
- { v: ipv6, cidr: "::1" }
|
- { v: ipv6, cidr: "::1" }
|
||||||
- { v: ipv6, cidr: "fe80::/10" }
|
- { v: ipv6, cidr: "fe80::/10" }
|
||||||
- { v: ipv6, cidr: "2a02:166b:92::/64" }
|
- { v: ipv6, cidr: "2a02:166b:92::/64" }
|
||||||
|
node_exporter: no
|
||||||
|
|
||||||
mqtt_internal_host: mqtt.bitlair.nl
|
mqtt_internal_host: mqtt.bitlair.nl
|
||||||
mqtt_public_host: bitlair.nl
|
mqtt_public_host: bitlair.nl
|
||||||
|
|
|
@ -26,9 +26,6 @@ all:
|
||||||
mqtt_internal:
|
mqtt_internal:
|
||||||
hosts:
|
hosts:
|
||||||
mqtt.bitlair.nl:
|
mqtt.bitlair.nl:
|
||||||
monitoring:
|
|
||||||
hosts:
|
|
||||||
monitoring.bitlair.nl:
|
|
||||||
music:
|
music:
|
||||||
hosts:
|
hosts:
|
||||||
music.bitlair.nl:
|
music.bitlair.nl:
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
---
|
|
||||||
- hosts: monitoring
|
|
||||||
roles:
|
|
||||||
- common
|
|
||||||
- acme
|
|
||||||
- monitoring
|
|
|
@ -1,11 +0,0 @@
|
||||||
monitoring_mqtt_exporter_port: 9883
|
|
||||||
prometheus_config_dir: /etc/prometheus
|
|
||||||
prometheus_web_listen_address: "[::1]:9090"
|
|
||||||
prometheus_storage_retention: 730d
|
|
||||||
blackbox_exporter_web_listen_address: "[::1]:9115"
|
|
||||||
grafana_url: "https://{{ monitoring_domain }}"
|
|
||||||
grafana_domain: "{{ monitoring_domain }}"
|
|
||||||
grafana_address: "::1"
|
|
||||||
grafana_port: 9000
|
|
||||||
grafana_admin_password: "{{ lookup('password', '/tmp/monitoring_grafana_password length=32') }}"
|
|
||||||
grafana_data_dir: /var/lib/grafana
|
|
|
@ -1,17 +0,0 @@
|
||||||
---
|
|
||||||
- import_tasks: ../../common/handlers/main.yaml
|
|
||||||
|
|
||||||
- name: restart mqtt_exporter
|
|
||||||
systemd:
|
|
||||||
name: mqtt_exporter
|
|
||||||
state: restarted
|
|
||||||
|
|
||||||
- name: restart prometheus
|
|
||||||
systemd:
|
|
||||||
name: prometheus
|
|
||||||
state: restarted
|
|
||||||
|
|
||||||
- name: restart grafana
|
|
||||||
systemd:
|
|
||||||
name: grafana-server
|
|
||||||
state: restarted
|
|
|
@ -1,4 +0,0 @@
|
||||||
---
|
|
||||||
- name: Install blackbox exporter
|
|
||||||
apt:
|
|
||||||
name: prometheus-blackbox-exporter
|
|
|
@ -1,46 +0,0 @@
|
||||||
---
|
|
||||||
- name: Add key
|
|
||||||
get_url:
|
|
||||||
url: https://apt.grafana.com/gpg.key
|
|
||||||
dest: /etc/apt/keyrings/grafana.asc
|
|
||||||
notify: apt update
|
|
||||||
|
|
||||||
- name: Grafana source
|
|
||||||
copy:
|
|
||||||
dest: /etc/apt/sources.list.d/grafana.list
|
|
||||||
content: "deb [signed-by=/etc/apt/keyrings/grafana.asc] https://apt.grafana.com stable main"
|
|
||||||
notify: apt update
|
|
||||||
|
|
||||||
- meta: flush_handlers
|
|
||||||
|
|
||||||
- name: Install Grafana
|
|
||||||
apt:
|
|
||||||
name: grafana
|
|
||||||
|
|
||||||
- name: Configure grafana
|
|
||||||
template:
|
|
||||||
src: "{{ item.src }}"
|
|
||||||
dest: "{{ item.dest }}"
|
|
||||||
owner: root
|
|
||||||
group: root
|
|
||||||
mode: 0644
|
|
||||||
notify: restart grafana
|
|
||||||
with_items:
|
|
||||||
- { src: grafana.ini, dest: /etc/grafana/grafana.ini }
|
|
||||||
- { src: grafana-ldap.toml, dest: /etc/grafana/ldap.toml }
|
|
||||||
|
|
||||||
- name: Configure grafana data source
|
|
||||||
template:
|
|
||||||
src: grafana-data-source.yml
|
|
||||||
dest: "/etc/grafana/provisioning/datasources/{{ item.name | lower }}.yaml"
|
|
||||||
owner: root
|
|
||||||
group: root
|
|
||||||
mode: 0644
|
|
||||||
notify: restart grafana
|
|
||||||
with_items:
|
|
||||||
- name: Prometheus
|
|
||||||
type: prometheus
|
|
||||||
access: proxy
|
|
||||||
url: 'http://{{ prometheus_web_listen_address }}/prometheus'
|
|
||||||
basicAuth: false
|
|
||||||
isDefault: true
|
|
|
@ -1,66 +0,0 @@
|
||||||
---
|
|
||||||
- name: monitoring
|
|
||||||
tags: monitoring
|
|
||||||
block:
|
|
||||||
- name: Install dependencies
|
|
||||||
apt:
|
|
||||||
name: nginx
|
|
||||||
state: present
|
|
||||||
|
|
||||||
- name: Clear default nginx site
|
|
||||||
file:
|
|
||||||
state: absent
|
|
||||||
path: /etc/nginx/sites-enabled/default
|
|
||||||
notify: reload nginx
|
|
||||||
|
|
||||||
- name: Install nginx site
|
|
||||||
template:
|
|
||||||
src: nginx-site.conf
|
|
||||||
dest: /etc/nginx/sites-available/monitoring
|
|
||||||
owner: root
|
|
||||||
group: root
|
|
||||||
mode: 0644
|
|
||||||
notify: reload nginx
|
|
||||||
|
|
||||||
- name: Enable nginx site
|
|
||||||
file:
|
|
||||||
src: /etc/nginx/sites-available/monitoring
|
|
||||||
dest: /etc/nginx/sites-enabled/monitoring
|
|
||||||
state: link
|
|
||||||
notify: reload nginx
|
|
||||||
|
|
||||||
- name: Start nginx
|
|
||||||
systemd:
|
|
||||||
name: nginx
|
|
||||||
state: started
|
|
||||||
enabled: yes
|
|
||||||
|
|
||||||
- name: Allow HTTP/HTTPS
|
|
||||||
iptables:
|
|
||||||
chain: INPUT
|
|
||||||
protocol: tcp
|
|
||||||
destination_port: "{{ item.port }}"
|
|
||||||
ctstate: NEW
|
|
||||||
jump: ACCEPT
|
|
||||||
ip_version: "{{ item.ip }}"
|
|
||||||
action: insert
|
|
||||||
with_items:
|
|
||||||
- { ip: ipv6, port: 80 }
|
|
||||||
- { ip: ipv6, port: 443 }
|
|
||||||
notify: persist iptables
|
|
||||||
|
|
||||||
- name: mqtt_exporter
|
|
||||||
tags: mqtt_exporter
|
|
||||||
import_tasks: mqtt_exporter.yaml
|
|
||||||
|
|
||||||
- name: blackbox
|
|
||||||
tags: blackbox
|
|
||||||
import_tasks: blackbox.yaml
|
|
||||||
|
|
||||||
- name: prometheus
|
|
||||||
tags: prometheus
|
|
||||||
import_tasks: prometheus.yaml
|
|
||||||
|
|
||||||
- name: grafana
|
|
||||||
tags: grafana
|
|
||||||
import_tasks: grafana.yaml
|
|
|
@ -1,46 +0,0 @@
|
||||||
---
|
|
||||||
- name: Clone source
|
|
||||||
git:
|
|
||||||
repo: https://github.com/polyfloyd/mqtt-exporter.git
|
|
||||||
version: main
|
|
||||||
dest: /opt/mqtt_exporter
|
|
||||||
accept_hostkey: yes
|
|
||||||
notify: restart mqtt_exporter
|
|
||||||
|
|
||||||
- name: Install apt dependencies
|
|
||||||
apt:
|
|
||||||
name:
|
|
||||||
- python3-paho-mqtt
|
|
||||||
- python3-prometheus-client
|
|
||||||
- python3-yaml
|
|
||||||
state: present
|
|
||||||
|
|
||||||
- name: Install service
|
|
||||||
template:
|
|
||||||
src: mqtt_exporter.service
|
|
||||||
dest: /etc/systemd/system/mqtt_exporter.service
|
|
||||||
owner: root
|
|
||||||
group: root
|
|
||||||
mode: 0644
|
|
||||||
notify:
|
|
||||||
- daemon reload
|
|
||||||
- restart mqtt_exporter
|
|
||||||
|
|
||||||
- name: Install config file
|
|
||||||
template:
|
|
||||||
src: mqtt_exporter_config.yaml
|
|
||||||
dest: /etc/mqtt_exporter.yaml
|
|
||||||
owner: root
|
|
||||||
group: root
|
|
||||||
mode: 0644
|
|
||||||
notify:
|
|
||||||
- daemon reload
|
|
||||||
- restart mqtt_exporter
|
|
||||||
|
|
||||||
- meta: flush_handlers
|
|
||||||
|
|
||||||
- name: Start service
|
|
||||||
systemd:
|
|
||||||
name: mqtt_exporter
|
|
||||||
state: started
|
|
||||||
enabled: true
|
|
|
@ -1,27 +0,0 @@
|
||||||
---
|
|
||||||
- name: Install dependencies
|
|
||||||
apt:
|
|
||||||
name: prometheus
|
|
||||||
|
|
||||||
- name: Configure Prometheus
|
|
||||||
template:
|
|
||||||
src: prometheus.yml
|
|
||||||
dest: "{{ prometheus_config_dir }}/prometheus.yml"
|
|
||||||
owner: root
|
|
||||||
group: root
|
|
||||||
mode: 0644
|
|
||||||
notify: restart prometheus
|
|
||||||
|
|
||||||
- name: Configure Prometheus args
|
|
||||||
lineinfile:
|
|
||||||
path: /etc/default/prometheus
|
|
||||||
line: >-
|
|
||||||
ARGS="
|
|
||||||
--storage.tsdb.retention.time={{ prometheus_storage_retention }}
|
|
||||||
--storage.tsdb.retention.size=0
|
|
||||||
--web.listen-address={{ prometheus_web_listen_address }}
|
|
||||||
--web.external-url=prometheus
|
|
||||||
--config.file={{ prometheus_config_dir }}/prometheus.yml
|
|
||||||
"
|
|
||||||
regexp: '^ARGS'
|
|
||||||
notify: restart prometheus
|
|
|
@ -1,4 +0,0 @@
|
||||||
apiVersion: 1
|
|
||||||
deleteDatasources: []
|
|
||||||
datasources:
|
|
||||||
{{ [item] | to_nice_yaml | indent(2) }}
|
|
|
@ -1,85 +0,0 @@
|
||||||
# Managed by Ansible
|
|
||||||
|
|
||||||
# Set to true to log user information returned from LDAP
|
|
||||||
verbose_logging = true
|
|
||||||
|
|
||||||
[[servers]]
|
|
||||||
# Ldap server host (specify multiple hosts space separated)
|
|
||||||
host = "ldap.bitlair.nl"
|
|
||||||
# Default port is 389 or 636 if use_ssl = true
|
|
||||||
port = 636
|
|
||||||
# Set to true if ldap server supports TLS
|
|
||||||
use_ssl = true
|
|
||||||
# Set to true if connect ldap server with STARTTLS pattern (create connection in insecure, then upgrade to secure connection with TLS)
|
|
||||||
start_tls = false
|
|
||||||
# set to true if you want to skip ssl cert validation
|
|
||||||
ssl_skip_verify = false
|
|
||||||
# set to the path to your root CA certificate or leave unset to use system defaults
|
|
||||||
# root_ca_cert = /path/to/certificate.crt
|
|
||||||
|
|
||||||
# Search user bind dn
|
|
||||||
bind_dn = "cn=grafana,ou=System,dc=bitlair,dc=nl"
|
|
||||||
# Search user bind password
|
|
||||||
bind_password = 'VHfVwAYB6tsEZHuX'
|
|
||||||
|
|
||||||
# User search filter, for example "(cn=%s)" or "(sAMAccountName=%s)" or "(uid=%s)"
|
|
||||||
search_filter = "(uid=%s)"
|
|
||||||
|
|
||||||
# An array of base dns to search through
|
|
||||||
search_base_dns = ["dc=bitlair,dc=nl"]
|
|
||||||
|
|
||||||
# In POSIX LDAP schemas, without memberOf attribute a secondary query must be made for groups.
|
|
||||||
# This is done by enabling group_search_filter below. You must also set member_of= "cn"
|
|
||||||
# in [servers.attributes] below.
|
|
||||||
|
|
||||||
# Users with nested/recursive group membership and an LDAP server that supports LDAP_MATCHING_RULE_IN_CHAIN
|
|
||||||
# can set group_search_filter, group_search_filter_user_attribute, group_search_base_dns and member_of
|
|
||||||
# below in such a way that the user's recursive group membership is considered.
|
|
||||||
#
|
|
||||||
# Nested Groups + Active Directory (AD) Example:
|
|
||||||
#
|
|
||||||
# AD groups store the Distinguished Names (DNs) of members, so your filter must
|
|
||||||
# recursively search your groups for the authenticating user's DN. For example:
|
|
||||||
#
|
|
||||||
# group_search_filter = "(member:1.2.840.113556.1.4.1941:=%s)"
|
|
||||||
# group_search_filter_user_attribute = "distinguishedName"
|
|
||||||
# group_search_base_dns = ["ou=groups,dc=grafana,dc=org"]
|
|
||||||
#
|
|
||||||
# [servers.attributes]
|
|
||||||
# ...
|
|
||||||
# member_of = "distinguishedName"
|
|
||||||
|
|
||||||
## Group search filter, to retrieve the groups of which the user is a member (only set if memberOf attribute is not available)
|
|
||||||
group_search_filter = "(&(objectClass=posixGroup)(memberUid=%s))"
|
|
||||||
## Group search filter user attribute defines what user attribute gets substituted for %s in group_search_filter.
|
|
||||||
## Defaults to the value of username in [server.attributes]
|
|
||||||
## Valid options are any of your values in [servers.attributes]
|
|
||||||
## If you are using nested groups you probably want to set this and member_of in
|
|
||||||
## [servers.attributes] to "distinguishedName"
|
|
||||||
# group_search_filter_user_attribute = "distinguishedName"
|
|
||||||
## An array of the base DNs to search through for groups. Typically uses ou=groups
|
|
||||||
group_search_base_dns = ["ou=Groups,dc=bitlair,dc=nl"]
|
|
||||||
|
|
||||||
# Specify names of the ldap attributes your ldap uses
|
|
||||||
[servers.attributes]
|
|
||||||
name = "givenName"
|
|
||||||
surname = "sn"
|
|
||||||
username = "uid"
|
|
||||||
email = "mail"
|
|
||||||
member_of = "cn"
|
|
||||||
|
|
||||||
# Map ldap groups to grafana org roles
|
|
||||||
[[servers.group_mappings]]
|
|
||||||
group_dn = "Admins"
|
|
||||||
org_role = "Admin"
|
|
||||||
# The Grafana organization database id, optional, if left out the default org (id 1) will be used
|
|
||||||
# org_id = 1
|
|
||||||
|
|
||||||
[[servers.group_mappings]]
|
|
||||||
group_dn = "Members"
|
|
||||||
org_role = "Editor"
|
|
||||||
|
|
||||||
[[servers.group_mappings]]
|
|
||||||
# If you want to match all (or no ldap groups) then you can use wildcard
|
|
||||||
group_dn = "*"
|
|
||||||
org_role = "Viewer"
|
|
|
@ -1,85 +0,0 @@
|
||||||
# Managed by Asnible
|
|
||||||
|
|
||||||
app_mode = production
|
|
||||||
instance_name = monitoring
|
|
||||||
|
|
||||||
# Directories
|
|
||||||
[paths]
|
|
||||||
data = /var/lib/grafana
|
|
||||||
logs = /var/log/grafana
|
|
||||||
plugins = /var/lib/grafana/plugins
|
|
||||||
|
|
||||||
# HTTP options
|
|
||||||
[server]
|
|
||||||
http_addr = {{ grafana_address }}
|
|
||||||
http_port = {{ grafana_port }}
|
|
||||||
domain = {{ grafana_domain }}
|
|
||||||
root_url = {{ grafana_url }}
|
|
||||||
protocol = http
|
|
||||||
enforce_domain = False
|
|
||||||
socket =
|
|
||||||
cert_key =
|
|
||||||
cert_file =
|
|
||||||
enable_gzip = False
|
|
||||||
static_root_path = public
|
|
||||||
router_logging = False
|
|
||||||
serve_from_sub_path = False
|
|
||||||
|
|
||||||
# Database
|
|
||||||
[database]
|
|
||||||
type = sqlite3
|
|
||||||
|
|
||||||
# Remote cache
|
|
||||||
[remote_cache]
|
|
||||||
|
|
||||||
# Security
|
|
||||||
[security]
|
|
||||||
admin_user = admin
|
|
||||||
admin_password = {{ grafana_admin_password }}
|
|
||||||
|
|
||||||
# Users management and registration
|
|
||||||
[users]
|
|
||||||
allow_sign_up = False
|
|
||||||
auto_assign_org_role = Viewer
|
|
||||||
default_theme = dark
|
|
||||||
|
|
||||||
[emails]
|
|
||||||
welcome_email_on_sign_up = False
|
|
||||||
|
|
||||||
# Analytics
|
|
||||||
[analytics]
|
|
||||||
reporting_enabled = "True"
|
|
||||||
|
|
||||||
# Dashboards
|
|
||||||
[dashboards]
|
|
||||||
versions_to_keep = 20
|
|
||||||
|
|
||||||
[dashboards.json]
|
|
||||||
enabled = true
|
|
||||||
path = /var/lib/grafana/dashboards
|
|
||||||
|
|
||||||
# Alerting
|
|
||||||
[alerting]
|
|
||||||
enabled = true
|
|
||||||
execute_alerts = True
|
|
||||||
|
|
||||||
# SMTP and email config
|
|
||||||
|
|
||||||
# Logging
|
|
||||||
[log]
|
|
||||||
mode = console, file
|
|
||||||
level = info
|
|
||||||
|
|
||||||
# Grafana.com configuration
|
|
||||||
[grafana_com]
|
|
||||||
url = https://grafana.com
|
|
||||||
|
|
||||||
[auth.anonymous]
|
|
||||||
enabled = true
|
|
||||||
org_name = Bitlair
|
|
||||||
org_role = Viewer
|
|
||||||
|
|
||||||
[auth.ldap]
|
|
||||||
enabled = true
|
|
||||||
config_file = /etc/grafana/ldap.toml
|
|
||||||
allow_sign_up = true
|
|
|
@ -1,14 +0,0 @@
|
||||||
[Unit]
|
|
||||||
Description=Prometheus exporter for MQTT
|
|
||||||
After=network.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=simple
|
|
||||||
ExecStart=/opt/mqtt_exporter/mqtt-exporter.py /etc/mqtt_exporter.yaml
|
|
||||||
Restart=always
|
|
||||||
RestartSec=10
|
|
||||||
DynamicUser=true
|
|
||||||
AmbientCapabilities=CAP_NET_RAW,CAP_NET_ADMIN+eip
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
|
@ -1,25 +0,0 @@
|
||||||
mqtt:
|
|
||||||
host: {{ mqtt_public_host }}
|
|
||||||
port: 1883
|
|
||||||
|
|
||||||
prometheus:
|
|
||||||
port: {{ monitoring_mqtt_exporter_port }}
|
|
||||||
|
|
||||||
export:
|
|
||||||
- subscribe: bitlair/#
|
|
||||||
- subscribe: bitlair/climate/+location/#
|
|
||||||
- subscribe: bitlair/wifi/+ssid/#
|
|
||||||
- subscribe: bitlair/state
|
|
||||||
value_map:
|
|
||||||
open: 1
|
|
||||||
closed: 0
|
|
||||||
- subscribe: bitlair/pos/product
|
|
||||||
metric_type: counter
|
|
||||||
labels:
|
|
||||||
product: enum
|
|
||||||
- subscribe: bitlair/collectd/bitlair-5406/snmp/if_octets-traffic.D15
|
|
||||||
metric_name: bitlair_internet_rx
|
|
||||||
value_regex: "^.+:(.+):"
|
|
||||||
- subscribe: bitlair/collectd/bitlair-5406/snmp/if_octets-traffic.D15
|
|
||||||
metric_name: bitlair_internet_tx
|
|
||||||
value_regex: "^.+:.+:([\\d\\.]+)"
|
|
|
@ -1,45 +0,0 @@
|
||||||
server {
|
|
||||||
listen 443 ssl http2 default_server;
|
|
||||||
listen [::]:443 ssl http2;
|
|
||||||
server_name {{ monitoring_domain }};
|
|
||||||
|
|
||||||
{% if monitoring_bootstrap_cert %}
|
|
||||||
include "snippets/snakeoil.conf";
|
|
||||||
{% else %}
|
|
||||||
ssl_certificate "/var/lib/dehydrated/certs/{{ monitoring_domain }}/fullchain.pem";
|
|
||||||
ssl_certificate_key "/var/lib/dehydrated/certs/{{ monitoring_domain }}/privkey.pem";
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
add_header X-Robots-Tag noindex;
|
|
||||||
|
|
||||||
location / {
|
|
||||||
proxy_pass http://localhost:9000/;
|
|
||||||
include proxy_params;
|
|
||||||
}
|
|
||||||
|
|
||||||
location /prometheus/ {
|
|
||||||
proxy_pass http://localhost:9090/prometheus/;
|
|
||||||
include proxy_params;
|
|
||||||
|
|
||||||
{% for range in trusted_ranges %}
|
|
||||||
allow "{{ range.cidr }}";
|
|
||||||
{% endfor %}
|
|
||||||
allow "127.0.0.1";
|
|
||||||
allow "::1";
|
|
||||||
deny all;
|
|
||||||
}
|
|
||||||
|
|
||||||
include "snippets/acme.conf";
|
|
||||||
}
|
|
||||||
|
|
||||||
server {
|
|
||||||
listen 80 default_server;
|
|
||||||
listen [::]:80;
|
|
||||||
server_name {{ monitoring_domain }};
|
|
||||||
|
|
||||||
location / {
|
|
||||||
rewrite ^/(.*) https://$server_name$request_uri? redirect;
|
|
||||||
}
|
|
||||||
|
|
||||||
include "snippets/acme.conf";
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
# Managed by Ansible
|
|
||||||
|
|
||||||
global:
|
|
||||||
evaluation_interval: 1m
|
|
||||||
scrape_interval: 1m
|
|
||||||
|
|
||||||
external_labels:
|
|
||||||
environment: monitoring
|
|
||||||
|
|
||||||
rule_files:
|
|
||||||
- /etc/prometheus/rules/*.rules
|
|
||||||
|
|
||||||
scrape_configs:
|
|
||||||
- job_name: "prometheus"
|
|
||||||
metrics_path: "/prometheus/metrics"
|
|
||||||
static_configs:
|
|
||||||
- targets: [ "localhost:9090" ]
|
|
||||||
{{ prometheus_scrape_configs | to_nice_yaml | indent(2) }}
|
|
|
@ -3,15 +3,12 @@
|
||||||
connection public-bridge
|
connection public-bridge
|
||||||
address {{ mqtt_public_host }}
|
address {{ mqtt_public_host }}
|
||||||
|
|
||||||
topic bitlair/climate/# out
|
|
||||||
topic bitlair/collectd/bitlair-5406/snmp/# out
|
|
||||||
topic bitlair/entrysensor/# out
|
|
||||||
topic bitlair/humidity/+ out
|
|
||||||
topic bitlair/lasercutter/+ out
|
|
||||||
topic bitlair/music/+/state out
|
|
||||||
topic bitlair/music/+/volume out
|
|
||||||
topic bitlair/photos out
|
|
||||||
topic bitlair/pos/product out
|
|
||||||
topic bitlair/state out
|
topic bitlair/state out
|
||||||
topic bitlair/state/djo out
|
topic bitlair/state/djo out
|
||||||
|
topic bitlair/music/+/state out
|
||||||
|
topic bitlair/music/+/volume out
|
||||||
topic bitlair/wifi/+/online out
|
topic bitlair/wifi/+/online out
|
||||||
|
topic bitlair/climate/# out
|
||||||
|
topic bitlair/humidity/+ out
|
||||||
|
topic bitlair/lasercutter/+ out
|
||||||
|
topic bitlair/photos out
|
||||||
|
|
Loading…
Add table
Reference in a new issue