<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de">
	<id>https://wiki.ixheim.de/index.php?action=history&amp;feed=atom&amp;title=MQTT_Projekt_Aufbau</id>
	<title>MQTT Projekt Aufbau - Versionsgeschichte</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.ixheim.de/index.php?action=history&amp;feed=atom&amp;title=MQTT_Projekt_Aufbau"/>
	<link rel="alternate" type="text/html" href="https://wiki.ixheim.de/index.php?title=MQTT_Projekt_Aufbau&amp;action=history"/>
	<updated>2026-07-06T17:43:14Z</updated>
	<subtitle>Versionsgeschichte dieser Seite in Xinux Wiki</subtitle>
	<generator>MediaWiki 1.35.1</generator>
	<entry>
		<id>https://wiki.ixheim.de/index.php?title=MQTT_Projekt_Aufbau&amp;diff=71202&amp;oldid=prev</id>
		<title>Thomas.will: Die Seite wurde neu angelegt: „=Aufbau der MQTT-Umgebung= Diese Seite beschreibt den vollständigen Aufbau der Lab-Umgebung, sodass sie von Grund auf nachgebaut werden kann. Es gibt drei Mas…“</title>
		<link rel="alternate" type="text/html" href="https://wiki.ixheim.de/index.php?title=MQTT_Projekt_Aufbau&amp;diff=71202&amp;oldid=prev"/>
		<updated>2026-06-20T14:00:03Z</updated>

		<summary type="html">&lt;p&gt;Die Seite wurde neu angelegt: „=Aufbau der MQTT-Umgebung= Diese Seite beschreibt den vollständigen Aufbau der Lab-Umgebung, sodass sie von Grund auf nachgebaut werden kann. Es gibt drei Mas…“&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Neue Seite&lt;/b&gt;&lt;/p&gt;&lt;div&gt;=Aufbau der MQTT-Umgebung=&lt;br /&gt;
Diese Seite beschreibt den vollständigen Aufbau der Lab-Umgebung, sodass sie von Grund auf nachgebaut werden kann. Es gibt drei Maschinen:&lt;br /&gt;
&lt;br /&gt;
;mqtt.dkbi.com&lt;br /&gt;
:Der Mosquitto-Broker. Vermittelt die Nachrichten zwischen Sensor und Aktor.&lt;br /&gt;
;sensor.dkbi.com (Verzeichnis /usr/local/control-switch)&lt;br /&gt;
:Sendet Schaltbefehle (publish) und zeigt den Status an (subscribe). Web-Oberfläche mit klickbaren Schaltflächen.&lt;br /&gt;
;aktor.dkbi.com (Verzeichnis /usr/local/control)&lt;br /&gt;
:Zeigt den Status der Geräte an (nur subscribe). Read-only Web-Oberfläche.&lt;br /&gt;
&lt;br /&gt;
Die eigentliche Absicherung (ohne Passwort, mit Passwort, TLS, 2FA) wird über die Datei '''.env''' gesteuert. Der Programmcode bleibt über alle vier Stufen identisch; umgeschaltet wird nur die .env.&lt;br /&gt;
&lt;br /&gt;
=Voraussetzungen=&lt;br /&gt;
==Installation von Node.js und npm==&lt;br /&gt;
;Alte Node.js-Version entfernen, falls vorhanden&lt;br /&gt;
*sudo apt update &amp;amp;&amp;amp; sudo apt upgrade&lt;br /&gt;
*sudo apt install curl&lt;br /&gt;
*sudo apt remove nodejs&lt;br /&gt;
;Repository für die aktuelle LTS-Version (22.x) hinzufügen&lt;br /&gt;
*curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -&lt;br /&gt;
;Node.js (Version 22.x) und npm installieren&lt;br /&gt;
*sudo apt install -y nodejs&lt;br /&gt;
;Installation überprüfen&lt;br /&gt;
*node -v&lt;br /&gt;
*npm -v&lt;br /&gt;
;MQTT-Kommandozeilenwerkzeuge installieren&lt;br /&gt;
*sudo apt install mosquitto-clients&lt;br /&gt;
&lt;br /&gt;
=Sensor (control-switch)=&lt;br /&gt;
==Projekt anlegen==&lt;br /&gt;
;Verzeichnis erstellen&lt;br /&gt;
*sudo mkdir /usr/local/control-switch&lt;br /&gt;
*cd /usr/local/control-switch&lt;br /&gt;
;Node.js-Projekt initialisieren&lt;br /&gt;
*sudo npm init -y&lt;br /&gt;
;Benötigte Pakete installieren&lt;br /&gt;
*sudo npm install mqtt express dotenv&lt;br /&gt;
&lt;br /&gt;
==server.js==&lt;br /&gt;
Erstelle die Datei '''/usr/local/control-switch/server.js''' mit folgendem Inhalt:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
require('dotenv').config();&lt;br /&gt;
const mqtt = require('mqtt');&lt;br /&gt;
const express = require('express');&lt;br /&gt;
const path = require('path');&lt;br /&gt;
const fs = require('fs');&lt;br /&gt;
&lt;br /&gt;
const app = express();&lt;br /&gt;
const port = 3000;&lt;br /&gt;
&lt;br /&gt;
let status = {&lt;br /&gt;
  livingRoom: 'off',&lt;br /&gt;
  bedroom: 'off',&lt;br /&gt;
  kitchen: 'off',&lt;br /&gt;
  frontDoor: 'closed'&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
// Prüfe, ob TLS aktiv ist&lt;br /&gt;
const useTLS = process.env.MQTT_TLS === 'true';&lt;br /&gt;
&lt;br /&gt;
// MQTT-Verbindungsoptionen aus .env&lt;br /&gt;
const options = {&lt;br /&gt;
  host: process.env.MQTT_HOST,&lt;br /&gt;
  port: Number(process.env.MQTT_PORT),&lt;br /&gt;
  protocol: useTLS ? 'mqtts' : 'mqtt',&lt;br /&gt;
  reconnectPeriod: 5000&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
// Passwort hinzufügen, falls gesetzt.&lt;br /&gt;
if (process.env.MQTT_PASS) {&lt;br /&gt;
  options.username = process.env.MQTT_USER || undefined;&lt;br /&gt;
  options.password = process.env.MQTT_PASS;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Falls TLS aktiv ist, CA-Zertifikat laden&lt;br /&gt;
if (useTLS &amp;amp;&amp;amp; process.env.MQTT_CA) {&lt;br /&gt;
  try {&lt;br /&gt;
    options.ca = fs.readFileSync(process.env.MQTT_CA);&lt;br /&gt;
  } catch (err) {&lt;br /&gt;
    console.error('Fehler beim Laden der CA-Datei:', err.message);&lt;br /&gt;
    process.exit(1);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Falls Client-Zertifikat gesetzt ist (Stufe 4: 2FA), Cert + Key laden&lt;br /&gt;
if (useTLS &amp;amp;&amp;amp; process.env.MQTT_CERT &amp;amp;&amp;amp; process.env.MQTT_KEY) {&lt;br /&gt;
  try {&lt;br /&gt;
    options.cert = fs.readFileSync(process.env.MQTT_CERT);&lt;br /&gt;
    options.key  = fs.readFileSync(process.env.MQTT_KEY);&lt;br /&gt;
  } catch (err) {&lt;br /&gt;
    console.error('Fehler beim Laden von Client-Cert/Key:', err.message);&lt;br /&gt;
    process.exit(1);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
const client = mqtt.connect(options);&lt;br /&gt;
&lt;br /&gt;
client.on('connect', () =&amp;gt; {&lt;br /&gt;
  console.log(`Mit MQTT-Broker verbunden: ${process.env.MQTT_HOST}:${process.env.MQTT_PORT}`);&lt;br /&gt;
  client.subscribe('home/+/status', (err) =&amp;gt; {&lt;br /&gt;
    if (!err) {&lt;br /&gt;
      console.log('Abonniert: home/+/status');&lt;br /&gt;
    } else {&lt;br /&gt;
      console.error('Fehler beim Abonnieren:', err.message);&lt;br /&gt;
    }&lt;br /&gt;
  });&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
client.on('message', (topic, message) =&amp;gt; {&lt;br /&gt;
  const room = topic.split('/')[1];&lt;br /&gt;
  status[room] = message.toString();&lt;br /&gt;
  console.log(`Status von ${room}: ${status[room]}`);&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
client.on('error', (err) =&amp;gt; {&lt;br /&gt;
  console.error(`MQTT-Fehler: ${err.message}`);&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
client.on('offline', () =&amp;gt; {&lt;br /&gt;
  console.warn('MQTT-Broker nicht erreichbar. Warte auf Wiederverbindung...');&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
client.on('reconnect', () =&amp;gt; {&lt;br /&gt;
  console.log('Versuche, die MQTT-Verbindung wiederherzustellen...');&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
// Route zum Abfragen des Status&lt;br /&gt;
app.get('/status', (req, res) =&amp;gt; {&lt;br /&gt;
  res.json(status);&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
// Route zum Schalten eines Geräts&lt;br /&gt;
app.post('/toggle/:device', (req, res) =&amp;gt; {&lt;br /&gt;
  const device = req.params.device;&lt;br /&gt;
  const currentStatus = status[device];&lt;br /&gt;
&lt;br /&gt;
  if (!currentStatus) {&lt;br /&gt;
    res.status(400).send({ error: 'Invalid device' });&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  let newStatus;&lt;br /&gt;
  if (device === 'frontDoor') {&lt;br /&gt;
    newStatus = currentStatus === 'closed' ? 'open' : 'closed';&lt;br /&gt;
  } else {&lt;br /&gt;
    newStatus = currentStatus === 'on' ? 'off' : 'on';&lt;br /&gt;
  }&lt;br /&gt;
  status[device] = newStatus;&lt;br /&gt;
&lt;br /&gt;
  const topic = `home/${device}/status`;&lt;br /&gt;
  client.publish(topic, newStatus, (err) =&amp;gt; {&lt;br /&gt;
    if (err) {&lt;br /&gt;
      console.log(`Fehler beim Senden an ${device}: ${err.message}`);&lt;br /&gt;
      res.status(500).send({ success: false, message: 'Error sending message' });&lt;br /&gt;
    } else {&lt;br /&gt;
      console.log(`Nachricht gesendet: ${device} ist jetzt ${newStatus}`);&lt;br /&gt;
      res.send({ success: true, message: `Device ${device} successfully toggled` });&lt;br /&gt;
    }&lt;br /&gt;
  });&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
app.use(express.static(path.join(__dirname)));&lt;br /&gt;
&lt;br /&gt;
app.listen(port, () =&amp;gt; {&lt;br /&gt;
  console.log(`Sensor Control Center läuft auf: http://0.0.0.0:${port}`);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==index.html==&lt;br /&gt;
Erstelle die Datei '''/usr/local/control-switch/index.html''' mit folgendem Inhalt:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;title&amp;gt;Sensor Control Center&amp;lt;/title&amp;gt;&lt;br /&gt;
  &amp;lt;style&amp;gt;&lt;br /&gt;
    .device {&lt;br /&gt;
      margin: 20px;&lt;br /&gt;
      padding: 10px;&lt;br /&gt;
      border-radius: 10px;&lt;br /&gt;
      text-align: center;&lt;br /&gt;
      font-size: 1.5em;&lt;br /&gt;
      cursor: pointer;&lt;br /&gt;
    }&lt;br /&gt;
    .on { background-color: green; color: black; }&lt;br /&gt;
    .off { background-color: red; color: white; }&lt;br /&gt;
    .open { background-color: red; color: white; }&lt;br /&gt;
    .closed { background-color: green; color: white; }&lt;br /&gt;
  &amp;lt;/style&amp;gt;&lt;br /&gt;
  &amp;lt;script&amp;gt;&lt;br /&gt;
    function fetchStatus() {&lt;br /&gt;
      fetch('/status')&lt;br /&gt;
        .then(response =&amp;gt; response.json())&lt;br /&gt;
        .then(data =&amp;gt; {&lt;br /&gt;
          document.getElementById('livingRoom').className = 'device ' + (data.livingRoom === 'on' ? 'on' : 'off');&lt;br /&gt;
          document.getElementById('livingRoom').textContent = 'Living Room Light: ' + (data.livingRoom === 'on' ? 'On' : 'Off');&lt;br /&gt;
          document.getElementById('bedroom').className = 'device ' + (data.bedroom === 'on' ? 'on' : 'off');&lt;br /&gt;
          document.getElementById('bedroom').textContent = 'Bedroom Light: ' + (data.bedroom === 'on' ? 'On' : 'Off');&lt;br /&gt;
          document.getElementById('kitchen').className = 'device ' + (data.kitchen === 'on' ? 'on' : 'off');&lt;br /&gt;
          document.getElementById('kitchen').textContent = 'Kitchen Light: ' + (data.kitchen === 'on' ? 'On' : 'Off');&lt;br /&gt;
          document.getElementById('frontDoor').className = 'device ' + (data.frontDoor === 'open' ? 'open' : 'closed');&lt;br /&gt;
          document.getElementById('frontDoor').textContent = 'Front Door: ' + (data.frontDoor === 'open' ? 'Open' : 'Closed');&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function toggleDevice(device) {&lt;br /&gt;
      fetch(`/toggle/${device}`, { method: 'POST' })&lt;br /&gt;
        .then(fetchStatus);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    setInterval(fetchStatus, 1000);&lt;br /&gt;
  &amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
  &amp;lt;h1&amp;gt;Sensor Control Center&amp;lt;/h1&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;livingRoom&amp;quot; class=&amp;quot;device off&amp;quot; onclick=&amp;quot;toggleDevice('livingRoom')&amp;quot;&amp;gt;Living Room Light: Off&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;bedroom&amp;quot; class=&amp;quot;device off&amp;quot; onclick=&amp;quot;toggleDevice('bedroom')&amp;quot;&amp;gt;Bedroom Light: Off&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;kitchen&amp;quot; class=&amp;quot;device off&amp;quot; onclick=&amp;quot;toggleDevice('kitchen')&amp;quot;&amp;gt;Kitchen Light: Off&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;frontDoor&amp;quot; class=&amp;quot;device closed&amp;quot; onclick=&amp;quot;toggleDevice('frontDoor')&amp;quot;&amp;gt;Front Door: Closed&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==.env-Vorlagen==&lt;br /&gt;
Für jede Stufe gibt es eine Vorlage. Die aktive Stufe wird durch Kopieren auf '''.env''' gesetzt (z. B. &amp;lt;code&amp;gt;cp env.mit-tls .env&amp;lt;/code&amp;gt;), danach Service neu starten.&lt;br /&gt;
;env.ohne-passwd (Stufe 1)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MQTT_HOST=mqtt.dkbi.com&lt;br /&gt;
MQTT_PORT=1883&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
;env.mit-passwd (Stufe 2)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MQTT_HOST=mqtt.dkbi.com&lt;br /&gt;
MQTT_PORT=1883&lt;br /&gt;
MQTT_USER=kit&lt;br /&gt;
MQTT_PASS=123Start$&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
;env.mit-tls (Stufe 3)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MQTT_HOST=mqtt.dkbi.com&lt;br /&gt;
MQTT_PORT=8883&lt;br /&gt;
MQTT_USER=kit&lt;br /&gt;
MQTT_PASS=123Start$&lt;br /&gt;
MQTT_TLS=true&lt;br /&gt;
MQTT_CA=/usr/local/control-switch/kit-ca.crt&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
;env.mit-2fa (Stufe 4)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MQTT_HOST=mqtt.dkbi.com&lt;br /&gt;
MQTT_PORT=8883&lt;br /&gt;
MQTT_USER=sensor&lt;br /&gt;
MQTT_PASS=123Start$&lt;br /&gt;
MQTT_TLS=true&lt;br /&gt;
MQTT_CA=/usr/local/control-switch/kit-ca.crt&lt;br /&gt;
MQTT_CERT=/usr/local/control-switch/sensor.crt&lt;br /&gt;
MQTT_KEY=/usr/local/control-switch/sensor.key&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==systemd-Unit==&lt;br /&gt;
Erstelle die Datei '''/etc/systemd/system/control-switch.service''':&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[Unit]&lt;br /&gt;
Description=Home Control Switch Server (Sensor)&lt;br /&gt;
After=network.target&lt;br /&gt;
&lt;br /&gt;
[Service]&lt;br /&gt;
Type=simple&lt;br /&gt;
WorkingDirectory=/usr/local/control-switch&lt;br /&gt;
ExecStart=/usr/bin/node server.js&lt;br /&gt;
Restart=on-failure&lt;br /&gt;
&lt;br /&gt;
[Install]&lt;br /&gt;
WantedBy=multi-user.target&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
;Dienst starten und beim Booten aktivieren&lt;br /&gt;
*sudo systemctl daemon-reload&lt;br /&gt;
*sudo systemctl enable --now control-switch.service&lt;br /&gt;
&lt;br /&gt;
=Aktor (control)=&lt;br /&gt;
Der Aktor ist genauso aufgebaut wie der Sensor, mit zwei Unterschieden: Er ist '''read-only''' (keine Schaltflächen, keine /toggle-Route) und liegt im Verzeichnis '''/usr/local/control'''.&lt;br /&gt;
&lt;br /&gt;
==Projekt anlegen==&lt;br /&gt;
*sudo mkdir /usr/local/control&lt;br /&gt;
*cd /usr/local/control&lt;br /&gt;
*sudo npm init -y&lt;br /&gt;
*sudo npm install mqtt express dotenv&lt;br /&gt;
&lt;br /&gt;
==server.js==&lt;br /&gt;
Identisch zum Sensor, aber '''ohne''' die /toggle-Route (der Aktor schaltet nicht, er zeigt nur an). Erstelle die Datei '''/usr/local/control/server.js''':&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
require('dotenv').config();&lt;br /&gt;
const mqtt = require('mqtt');&lt;br /&gt;
const express = require('express');&lt;br /&gt;
const path = require('path');&lt;br /&gt;
const fs = require('fs');&lt;br /&gt;
&lt;br /&gt;
const app = express();&lt;br /&gt;
const port = 3000;&lt;br /&gt;
&lt;br /&gt;
let status = {&lt;br /&gt;
  livingRoom: 'off',&lt;br /&gt;
  bedroom: 'off',&lt;br /&gt;
  kitchen: 'off',&lt;br /&gt;
  frontDoor: 'closed'&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
const useTLS = process.env.MQTT_TLS === 'true';&lt;br /&gt;
&lt;br /&gt;
const options = {&lt;br /&gt;
  host: process.env.MQTT_HOST,&lt;br /&gt;
  port: Number(process.env.MQTT_PORT),&lt;br /&gt;
  protocol: useTLS ? 'mqtts' : 'mqtt',&lt;br /&gt;
  reconnectPeriod: 5000&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
if (process.env.MQTT_PASS) {&lt;br /&gt;
  options.username = process.env.MQTT_USER || undefined;&lt;br /&gt;
  options.password = process.env.MQTT_PASS;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if (useTLS &amp;amp;&amp;amp; process.env.MQTT_CA) {&lt;br /&gt;
  try {&lt;br /&gt;
    options.ca = fs.readFileSync(process.env.MQTT_CA);&lt;br /&gt;
  } catch (err) {&lt;br /&gt;
    console.error('Fehler beim Laden der CA-Datei:', err.message);&lt;br /&gt;
    process.exit(1);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if (useTLS &amp;amp;&amp;amp; process.env.MQTT_CERT &amp;amp;&amp;amp; process.env.MQTT_KEY) {&lt;br /&gt;
  try {&lt;br /&gt;
    options.cert = fs.readFileSync(process.env.MQTT_CERT);&lt;br /&gt;
    options.key  = fs.readFileSync(process.env.MQTT_KEY);&lt;br /&gt;
  } catch (err) {&lt;br /&gt;
    console.error('Fehler beim Laden von Client-Cert/Key:', err.message);&lt;br /&gt;
    process.exit(1);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
const client = mqtt.connect(options);&lt;br /&gt;
&lt;br /&gt;
client.on('connect', () =&amp;gt; {&lt;br /&gt;
  console.log(`Mit MQTT-Broker verbunden: ${process.env.MQTT_HOST}:${process.env.MQTT_PORT}`);&lt;br /&gt;
  client.subscribe('home/+/status', (err) =&amp;gt; {&lt;br /&gt;
    if (!err) {&lt;br /&gt;
      console.log('Abonniert: home/+/status');&lt;br /&gt;
    } else {&lt;br /&gt;
      console.error('Fehler beim Abonnieren:', err.message);&lt;br /&gt;
    }&lt;br /&gt;
  });&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
client.on('message', (topic, message) =&amp;gt; {&lt;br /&gt;
  const room = topic.split('/')[1];&lt;br /&gt;
  status[room] = message.toString();&lt;br /&gt;
  console.log(`Status von ${room}: ${status[room]}`);&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
client.on('error', (err) =&amp;gt; {&lt;br /&gt;
  console.error(`MQTT-Fehler: ${err.message}`);&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
client.on('offline', () =&amp;gt; {&lt;br /&gt;
  console.warn('MQTT-Broker nicht erreichbar. Warte auf Wiederverbindung...');&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
client.on('reconnect', () =&amp;gt; {&lt;br /&gt;
  console.log('Versuche, die MQTT-Verbindung wiederherzustellen...');&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
app.get('/status', (req, res) =&amp;gt; {&lt;br /&gt;
  res.json(status);&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
app.use(express.static(path.join(__dirname)));&lt;br /&gt;
&lt;br /&gt;
app.listen(port, () =&amp;gt; {&lt;br /&gt;
  console.log(`Aktor läuft auf: http://0.0.0.0:${port}`);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==index.html==&lt;br /&gt;
Erstelle die Datei '''/usr/local/control/index.html''' (read-only, keine onclick-Schaltflächen):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;title&amp;gt;Aktor Devices&amp;lt;/title&amp;gt;&lt;br /&gt;
  &amp;lt;style&amp;gt;&lt;br /&gt;
    .device {&lt;br /&gt;
      margin: 20px;&lt;br /&gt;
      padding: 10px;&lt;br /&gt;
      border-radius: 10px;&lt;br /&gt;
      text-align: center;&lt;br /&gt;
      font-size: 1.5em;&lt;br /&gt;
    }&lt;br /&gt;
    .lamp-on { background-color: yellow; color: black; }&lt;br /&gt;
    .lamp-off { background-color: gray; color: white; }&lt;br /&gt;
    .door-open { background-color: red; color: white; }&lt;br /&gt;
    .door-closed { background-color: gray; color: white; }&lt;br /&gt;
  &amp;lt;/style&amp;gt;&lt;br /&gt;
  &amp;lt;script&amp;gt;&lt;br /&gt;
    function fetchStatus() {&lt;br /&gt;
      fetch('/status')&lt;br /&gt;
        .then(response =&amp;gt; response.json())&lt;br /&gt;
        .then(data =&amp;gt; {&lt;br /&gt;
          document.getElementById('livingRoom').className = 'device ' + (data.livingRoom === 'on' ? 'lamp-on' : 'lamp-off');&lt;br /&gt;
          document.getElementById('bedroom').className = 'device ' + (data.bedroom === 'on' ? 'lamp-on' : 'lamp-off');&lt;br /&gt;
          document.getElementById('kitchen').className = 'device ' + (data.kitchen === 'on' ? 'lamp-on' : 'lamp-off');&lt;br /&gt;
          document.getElementById('frontDoor').className = 'device ' + (data.frontDoor === 'open' ? 'door-open' : 'door-closed');&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    setInterval(fetchStatus, 1000);&lt;br /&gt;
  &amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
  &amp;lt;h1&amp;gt;Aktor Devices&amp;lt;/h1&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;livingRoom&amp;quot; class=&amp;quot;device lamp-off&amp;quot;&amp;gt;Living Room Light&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;bedroom&amp;quot; class=&amp;quot;device lamp-off&amp;quot;&amp;gt;Bedroom Light&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;kitchen&amp;quot; class=&amp;quot;device lamp-off&amp;quot;&amp;gt;Kitchen Light&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;frontDoor&amp;quot; class=&amp;quot;device door-closed&amp;quot;&amp;gt;Front Door&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==.env-Vorlagen (Aktor)==&lt;br /&gt;
Wie beim Sensor, aber die Pfade zeigen auf /usr/local/control und in Stufe 4 auf das Aktor-Zertifikat.&lt;br /&gt;
;env.mit-2fa (Stufe 4)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MQTT_HOST=mqtt.dkbi.com&lt;br /&gt;
MQTT_PORT=8883&lt;br /&gt;
MQTT_USER=aktor&lt;br /&gt;
MQTT_PASS=123Start$&lt;br /&gt;
MQTT_TLS=true&lt;br /&gt;
MQTT_CA=/usr/local/control/kit-ca.crt&lt;br /&gt;
MQTT_CERT=/usr/local/control/aktor.crt&lt;br /&gt;
MQTT_KEY=/usr/local/control/aktor.key&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==systemd-Unit==&lt;br /&gt;
Erstelle die Datei '''/etc/systemd/system/control.service''':&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[Unit]&lt;br /&gt;
Description=Home Control Server (Aktor)&lt;br /&gt;
After=network.target&lt;br /&gt;
&lt;br /&gt;
[Service]&lt;br /&gt;
Type=simple&lt;br /&gt;
WorkingDirectory=/usr/local/control&lt;br /&gt;
ExecStart=/usr/bin/node server.js&lt;br /&gt;
Restart=on-failure&lt;br /&gt;
&lt;br /&gt;
[Install]&lt;br /&gt;
WantedBy=multi-user.target&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
;Dienst starten und beim Booten aktivieren&lt;br /&gt;
*sudo systemctl daemon-reload&lt;br /&gt;
*sudo systemctl enable --now control.service&lt;br /&gt;
&lt;br /&gt;
=Zugriff per HTTPS (nginx)=&lt;br /&gt;
Vor beide Node-Backends (Port 3000) ist ein nginx als Reverse-Proxy mit TLS geschaltet, sodass die Oberflächen unter https://sensor.dkbi.com und https://aktor.dkbi.com erreichbar sind. Die Zertifikate stammen aus der lab-eigenen CA (kit-ca).&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Node.js]]&lt;br /&gt;
[[Kategorie:MQTT]]&lt;/div&gt;</summary>
		<author><name>Thomas.will</name></author>
	</entry>
</feed>