Skip to content

en_install_server

Steffen Köhler edited this page Aug 27, 2020 · 6 revisions

A Zuul Node-Red Ubuntu Server

A complete server setup exeeds the scope of the zuul documentation, so this can be seen as a sample reference guide how to create a complete server.

As this sample is made for a company environment, it contains additional elements like access control and HTTPS connections, which you (hopefully) won't need if you run Zuul in your own home

Install Ubuntu and additional packages

This sample is made based on ubuntu-20.04-live-server-amd64.iso

It should also work on RaspberryOS (aka Raspian)

Install Ubuntu or RaspberryOS first. Then add some more packages with

sudo apt install mosquitto npm

Install Node-Red

sudo npm install -g --unsafe-perm node-red node-red-admin

With the same command the package can be updated, if needed.

Following https://nodered.org/docs/getting-started/raspberrypi , we execute

bash <(curl -sL https://raw.githubusercontent.com/node-red/linux-installers/master/deb/update-nodejs-and-nodered)

The installer script creates a systemd service, which is executed by the actual user in his homedir

The following commands are provided to work with the service:

  • node-red-start - this starts the Node-RED service and displays its log output. Pressing Ctrl-C or closing the window does not stop the service; it keeps running in the background
  • node-red-stop - this stops the Node-RED service
  • node-red-restart - this stops and restarts the Node-RED service
  • node-red-log - this displays the log output of the service

Autostart on boot

If you want Node-RED to run when the computer is is turned on, or re-booted, you can enable the service to autostart by running the command:

sudo systemctl enable nodered.service

To disable the service, run the command:

sudo systemctl disable nodered.service

Generate HTTPS Keys

Node Red

If the web server shall run on https, we need some key file. These will be generated with

openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout key.pem -out server.pem && echo "saved as key.pem and server.pem"

Start and stop node-red to generate it's settings file

Then open the settings file

nano .node-red/settings.js

uncommend the adminAuth section

    adminAuth: {
        type: "credentials",
        users: [{
            username: "pi",
            password: "$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.",
            permissions: "*"
        }]
    },

adapt the https and requireHttps like this:

    https: {
        key: require("fs").readFileSync('/home/pi/key.pem'),
        cert: require("fs").readFileSync('/home/pi/server.pem')
    },

    // The following property can be used to cause insecure HTTP connections to
    // be redirected to HTTPS.
    requireHttps: true,

To calculate your own password hash, use

node-red-admin hash-pw

Mosquitto

create a new config file

sudo nano /etc/mosquitto/conf.d/zuul.conf

containing

# force login password
password_file /etc/mosquitto/passwd

# add websocket
# actual causes mosquitto to crash at start, so commended out
# listener 9001
# protocol websockets

and create a password file

mosquitto_passwd -c /etc/mosquitto/passwd pi

Install Zuul itself

git clone https://github.com/stko/zuul-ac.git

(workaround, as the zuul server has a hardcoded key file path): Copy the key file to the server

cp *.pem zuul-ac/

Install python packages

sudo apt install python3-pip

sudo pip3 install ecdsa python-telegram-bot qrcode[pil] --upgrade

Create the systemd zuul service. Do not forget to adapt user name and path to the user name you've used during the OS installation

cat << 'EOF' | sudo tee  /etc/systemd/system/zuul.service
[Unit]
Description=Zuul Telegram Bot
Wants=network.target
[Service]
User=pi
Group=pi
WorkingDirectory=/home/pi/zuul-ac/
ExecStart= /usr/bin/python3 /home/pi/zuul-ac/zuulac.py -s
Restart=always
[Install]
WantedBy=default.target
EOF

Enable zuul

sudo systemctl enable zuul

Start zuul

sudo systemctl start zuul

Configure zuul via web UI. After configuration a config file (config.json) can be found in the config subdirectory.

To protect your UI with a password, open that config.json file with a text editor and enter your password in the current_password field.

restart zuul with

sudo systemctl restart zuul

to activate your password.

When a password is given, then

  • changes through the UI will only be recocnized if the right password is given in the password field
  • the Telegram API token will not shown in the UI anymore and can only be edited directly in the config file

Demo Settings

This sample config reads the QR from the build in webcam and send a dial string to a connected modem to open the door(s).

sudo usermod -aG video $USER
sudo usermod -aG dialout $USER
sudo pip3 install paho-mqtt

log out and login again to make these changes active

The QR Code Reader

sudo apt install zbar-tools

cat << 'EOF' | tee  /home/pi/zuul-ac/qr_to_mqtt.py 
#!/usr/bin/env python
# -*- coding: utf-8 -*-

# little hack to read QR code from local webcam
# setup:
#               sudo apt-get install zbar-tools
#               sudo pip3 install paho-mqtt
# usage:
#       zbarcam [--nodisplay] | python3 qr_to_mqtt.py

import sys
import json

import paho.mqtt.client as mqtt

def on_connect(client, userdata, flags, rc):
        print("Connected with result code " + str(rc))

client = mqtt.Client()
client.username_pw_set('pi', 'YOURPASSWORD')
client.on_connect = on_connect

client.connect("localhost", 1883, 60)

client.loop_start()

for line in sys.stdin:
        elements=line.strip().split(':')
        if elements[0]=="QR-Code":
                qrcode=":".join(elements[1:])
                json_msg={'qrcode':qrcode,'doorid':sys.argv[1]}
                print(line, json.dumps(json_msg))
                client.publish("door/qrcode", json.dumps(json_msg))
EOF
cat << 'EOF' | sudo tee  /etc/systemd/system/zuul-qr.service 
[Unit]
Description=MQTT QR Code Reader
Wants=mosquitto.service
[Service]
User=pi
Group=pi
WorkingDirectory=/home/pi/zuul-ac/
ExecStart= /usr/bin/sh -c "/usr/bin/zbarcam --nodisplay | /usr/bin/python3 qr_to_mqtt.py 'yourdoorid'"
Restart=always
[Install]
WantedBy=default.target
EOF

sudo systemctl enable zuul-qr.service

The Serial Modem connection

cat << 'EOF' | tee  /home/pi/zuul-ac/mqtt_to_serial.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

# little hack to write MQTT to serial
# setup:
#               sudo -H pip3 install paho-mqtt
# usage:
#        python3 MQTT_to_serial.py

import sys
import paho.mqtt.client as mqtt
import serial
import time

def on_connect(client, userdata, flags, rc):
        print("Connected with result code " + str(rc))

def on_message(client, userdata, message):
    print("message received  ",str(message.payload.decode("utf-8")), "topic",message.topic,"retained ",message.retain)
    send_serial(message.payload + b'\r\n')

def send_serial(cmd):
    global ser
    ser.write(cmd)     # write the cmd
    line = ser.readline()   # read a '\n' terminated line
    while line:
        print(line.decode().replace('\r',''), end='')
        line = ser.readline()   # read a '\n' terminated line

client = mqtt.Client()
client.username_pw_set('pi', 'YOURPASSWORD')
client.on_connect = on_connect
client.on_message = on_message

client.connect("localhost", 1883, 60)
client.subscribe('serial/dial')

client.loop_start()

ser= serial.Serial(sys.argv[1], 9600, timeout=1)
print(ser.name)         # check which port was really used
send_serial(b'AT S7=45 L1 V1 X4 E1\r\n')     # write a string

while True:
    time.sleep(1)
ser.close()             # close portfor line in sys.stdin:
EOF
cat << 'EOF' | sudo tee  /etc/systemd/system/zuul-serial.service 
[Unit]
Description=MQTT Serial Output
Wants=mosquitto.service
[Service]
User=pi
Group=pi
WorkingDirectory=/home/pi/zuul-ac/
ExecStart= /usr/bin/python3 mqtt_to_serial.py /dev/ttyUSB0
Restart=always
[Install]
WantedBy=default.target
EOF

sudo systemctl enable zuul-serial.service

The System Console as Status Screen

cat << 'EOF' | tee  /home/pi/zuul-ac/mqtt_to_console.py 
#!/usr/bin/env python
# -*- coding: utf-8 -*-

# little hack to write MQTT to system console
# usage:
#        python3 MQTT_to_console.py

import sys
import os
import paho.mqtt.client as mqtt
import time
import json

## colors can be found here: https://stackoverflow.com/a/51708889

def on_connect(client, userdata, flags, rc):
        print("Connected with result code " + str(rc))

def on_message(client, userdata, message):
    #print("message received  ",str(message.payload.decode("utf-8")), "topic",message.topic,"retained ",message.retain)
    data=json.loads(str(message.payload.decode("utf-8")))
    startcolor=data['startcolor'].replace('ESC','\x1b')
    endcolor=data['endcolor'].replace('ESC','\x1b')
    messagetext=data['text']
    print(startcolor)
    os.system("/usr/bin/banner "+ messagetext)
    print(endcolor)

client = mqtt.Client()
client.username_pw_set('pi', 'YOURPASSWORD')
client.on_connect = on_connect
client.on_message = on_message

client.connect("localhost", 1883, 60)
client.subscribe('console/out')

client.loop_start()


while True:
    time.sleep(1)
EOF
cat << 'EOF' | sudo tee  /etc/systemd/system/zuul-console.service 
[Unit]
Description=MQTT to Console
Wants=mosquitto.service
[Service]
StandardOutput=tty
User=pi
Group=pi
WorkingDirectory=/home/pi/zuul-ac/
ExecStart= /usr/bin/python3 mqtt_to_console.py 
Restart=always
[Install]
WantedBy=default.target
EOF

sudo systemctl enable zuul-console.service

The Node-Red Flow

And this Node-Red Flow makes it all work:

[
    {
        "id": "ec4b3da9.4704c",
        "type": "tab",
        "label": "Zuul- Websocket",
        "disabled": false,
        "info": ""
    },
    {
        "id": "a57cd6a1.14bb38",
        "type": "websocket out",
        "z": "ec4b3da9.4704c",
        "name": "zuul",
        "server": "",
        "client": "e837f008.6ba9b",
        "x": 1090,
        "y": 140,
        "wires": []
    },
    {
        "id": "92c0a055.c2151",
        "type": "websocket in",
        "z": "ec4b3da9.4704c",
        "name": "Zuul",
        "server": "",
        "client": "e837f008.6ba9b",
        "x": 70,
        "y": 240,
        "wires": [
            [
                "ed415b1a.f7e578",
                "8e5b87bd.ea3dc8"
            ]
        ]
    },
    {
        "id": "331b8c0a.1ca674",
        "type": "debug",
        "z": "ec4b3da9.4704c",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "x": 990,
        "y": 80,
        "wires": []
    },
    {
        "id": "7d1829ba.0991d8",
        "type": "comment",
        "z": "ec4b3da9.4704c",
        "name": "Client",
        "info": "",
        "x": 90,
        "y": 60,
        "wires": []
    },
    {
        "id": "4a1b1808.e71a28",
        "type": "json",
        "z": "ec4b3da9.4704c",
        "name": "",
        "property": "payload",
        "action": "",
        "pretty": false,
        "x": 370,
        "y": 240,
        "wires": [
            [
                "4325f655.0e83f8",
                "8e5b87bd.ea3dc8"
            ]
        ]
    },
    {
        "id": "23160e29.073202",
        "type": "change",
        "z": "ec4b3da9.4704c",
        "name": "Set OTP Parameters",
        "rules": [
            {
                "t": "set",
                "p": "payload.type",
                "pt": "msg",
                "to": "ac_otprequest",
                "tot": "str"
            },
            {
                "t": "set",
                "p": "payload.config.result",
                "pt": "msg",
                "to": "zuul_active",
                "tot": "flow"
            },
            {
                "t": "set",
                "p": "payload.config.msg",
                "pt": "msg",
                "to": "zuul_msg",
                "tot": "flow"
            },
            {
                "t": "set",
                "p": "payload.config.type",
                "pt": "msg",
                "to": "qrcode",
                "tot": "str"
            },
            {
                "t": "set",
                "p": "payload.config.keypadchars",
                "pt": "msg",
                "to": "1234567890ABCD",
                "tot": "str"
            },
            {
                "t": "set",
                "p": "payload.config.length",
                "pt": "msg",
                "to": "10",
                "tot": "num"
            },
            {
                "t": "set",
                "p": "payload.config.valid_time",
                "pt": "msg",
                "to": "60",
                "tot": "num"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 760,
        "y": 220,
        "wires": [
            [
                "331b8c0a.1ca674",
                "a57cd6a1.14bb38"
            ]
        ]
    },
    {
        "id": "4325f655.0e83f8",
        "type": "switch",
        "z": "ec4b3da9.4704c",
        "name": "OTP Request",
        "property": "payload",
        "propertyType": "msg",
        "rules": [
            {
                "t": "jsonata_exp",
                "v": "payload.type = \"otprequest\"",
                "vt": "jsonata"
            },
            {
                "t": "jsonata_exp",
                "v": "payload.type = \"tokenstate\"  and  payload.config.valid = true",
                "vt": "jsonata"
            },
            {
                "t": "jsonata_exp",
                "v": "payload.type = \"tokenstate\"  and  payload.config.valid = false",
                "vt": "jsonata"
            },
            {
                "t": "else"
            }
        ],
        "checkall": "false",
        "repair": false,
        "outputs": 4,
        "x": 520,
        "y": 240,
        "wires": [
            [
                "23160e29.073202"
            ],
            [
                "8f9e515e.98f4e",
                "6314622f.dc5d6c",
                "1ea807e8.ca9a78"
            ],
            [
                "1931ffd3.4d8f7"
            ],
            []
        ]
    },
    {
        "id": "ed415b1a.f7e578",
        "type": "function",
        "z": "ec4b3da9.4704c",
        "name": "Kill Session",
        "func": "msg._session = \"\";\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "x": 230,
        "y": 240,
        "wires": [
            [
                "4a1b1808.e71a28"
            ]
        ]
    },
    {
        "id": "5efc758.108a58c",
        "type": "inject",
        "z": "ec4b3da9.4704c",
        "name": "TRUE",
        "repeat": "",
        "crontab": "",
        "once": true,
        "onceDelay": 0.1,
        "topic": "A",
        "payload": "true",
        "payloadType": "bool",
        "x": 110,
        "y": 540,
        "wires": [
            [
                "107dfb8c.62def4"
            ]
        ]
    },
    {
        "id": "d02bf871.39f4d8",
        "type": "inject",
        "z": "ec4b3da9.4704c",
        "name": "FALSE",
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "A",
        "payload": "false",
        "payloadType": "bool",
        "x": 110,
        "y": 600,
        "wires": [
            [
                "107dfb8c.62def4"
            ]
        ]
    },
    {
        "id": "107dfb8c.62def4",
        "type": "rbe",
        "z": "ec4b3da9.4704c",
        "name": "Detect Change",
        "func": "rbe",
        "gap": "",
        "start": "",
        "inout": "out",
        "property": "payload",
        "x": 300,
        "y": 560,
        "wires": [
            [
                "306b57e1.49fd08",
                "1c2473d6.c9eccc"
            ]
        ]
    },
    {
        "id": "306b57e1.49fd08",
        "type": "change",
        "z": "ec4b3da9.4704c",
        "name": "zuul_active",
        "rules": [
            {
                "t": "set",
                "p": "zuul_active",
                "pt": "flow",
                "to": "payload",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 730,
        "y": 560,
        "wires": [
            []
        ]
    },
    {
        "id": "c6bc57cd.8f1ae8",
        "type": "change",
        "z": "ec4b3da9.4704c",
        "name": "zuul_msg_Ok",
        "rules": [
            {
                "t": "set",
                "p": "zuul_msg",
                "pt": "flow",
                "to": "Hinein spaziert! Dein QRCode ist {0} Sekunden gültig",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 740,
        "y": 600,
        "wires": [
            []
        ]
    },
    {
        "id": "1c2473d6.c9eccc",
        "type": "switch",
        "z": "ec4b3da9.4704c",
        "name": "Set OTP Message",
        "property": "payload",
        "propertyType": "msg",
        "rules": [
            {
                "t": "true"
            },
            {
                "t": "false"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 510,
        "y": 620,
        "wires": [
            [
                "c6bc57cd.8f1ae8"
            ],
            [
                "bc26f0cd.c14c4"
            ]
        ]
    },
    {
        "id": "bc26f0cd.c14c4",
        "type": "change",
        "z": "ec4b3da9.4704c",
        "name": "zuul_msg_Denied",
        "rules": [
            {
                "t": "set",
                "p": "zuul_msg",
                "pt": "flow",
                "to": "Passt gerade nicht..",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 750,
        "y": 640,
        "wires": [
            []
        ]
    },
    {
        "id": "5656393a.02e438",
        "type": "template",
        "z": "ec4b3da9.4704c",
        "name": "Send Token",
        "field": "payload",
        "fieldType": "msg",
        "format": "handlebars",
        "syntax": "mustache",
        "template": "{\"type\": \"ac_tokenquery\", \"config\":{ \"token\": \"{{{payload.qrcode}}}\", \"doorid\": \"{{{payload.doorid}}}\" } }",
        "output": "json",
        "x": 730,
        "y": 140,
        "wires": [
            [
                "331b8c0a.1ca674",
                "a57cd6a1.14bb38",
                "e9c73fda.d586c"
            ]
        ]
    },
    {
        "id": "8f9e515e.98f4e",
        "type": "trigger",
        "z": "ec4b3da9.4704c",
        "name": "Door Opener 3 Secs",
        "op1": "ON",
        "op2": "OFF",
        "op1type": "str",
        "op2type": "str",
        "duration": "3",
        "extend": false,
        "units": "s",
        "reset": "",
        "bytopic": "all",
        "outputs": 1,
        "x": 760,
        "y": 340,
        "wires": [
            [
                "df79b20d.83f3a",
                "8cb2f7f6.140528",
                "5b4ed54a.c43e2c"
            ]
        ]
    },
    {
        "id": "df79b20d.83f3a",
        "type": "mqtt out",
        "z": "ec4b3da9.4704c",
        "name": "",
        "topic": "cmnd/tasmota/POWER",
        "qos": "",
        "retain": "",
        "broker": "1d164033.b808e",
        "x": 1030,
        "y": 340,
        "wires": []
    },
    {
        "id": "8e5b87bd.ea3dc8",
        "type": "debug",
        "z": "ec4b3da9.4704c",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "x": 510,
        "y": 340,
        "wires": []
    },
    {
        "id": "f8391752.3ddb88",
        "type": "mqtt in",
        "z": "ec4b3da9.4704c",
        "name": "",
        "topic": "door/qrcode",
        "qos": "2",
        "datatype": "json",
        "broker": "1d164033.b808e",
        "x": 150,
        "y": 140,
        "wires": [
            [
                "5656393a.02e438",
                "331b8c0a.1ca674"
            ]
        ]
    },
    {
        "id": "6314622f.dc5d6c",
        "type": "change",
        "z": "ec4b3da9.4704c",
        "name": "Filter Door-ID",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "payload.config.msg.doorid",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 740,
        "y": 480,
        "wires": [
            [
                "9d550ee6.4a84d",
                "5b4ed54a.c43e2c"
            ]
        ]
    },
    {
        "id": "9d550ee6.4a84d",
        "type": "mqtt out",
        "z": "ec4b3da9.4704c",
        "name": "Modem",
        "topic": "serial/dial",
        "qos": "",
        "retain": "",
        "broker": "1d164033.b808e",
        "x": 980,
        "y": 480,
        "wires": []
    },
    {
        "id": "5b4ed54a.c43e2c",
        "type": "debug",
        "z": "ec4b3da9.4704c",
        "name": "",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "x": 1130,
        "y": 560,
        "wires": []
    },
    {
        "id": "8cb2f7f6.140528",
        "type": "ui_led",
        "z": "ec4b3da9.4704c",
        "group": "a681ccbf.f47c8",
        "order": 0,
        "width": 0,
        "height": 0,
        "label": "Tür-Öffner",
        "labelPlacement": "left",
        "labelAlignment": "left",
        "colorForValue": [
            {
                "color": "green",
                "value": "ON",
                "valueType": "str"
            },
            {
                "color": "grey",
                "value": "OFF",
                "valueType": "str"
            }
        ],
        "allowColorForValueInMessage": false,
        "name": "",
        "x": 980,
        "y": 440,
        "wires": []
    },
    {
        "id": "d831a280.a5dfa",
        "type": "mqtt out",
        "z": "ec4b3da9.4704c",
        "name": "",
        "topic": "console/out",
        "qos": "",
        "retain": "",
        "broker": "1d164033.b808e",
        "x": 1250,
        "y": 260,
        "wires": []
    },
    {
        "id": "1931ffd3.4d8f7",
        "type": "change",
        "z": "ec4b3da9.4704c",
        "name": "Leider abgelehnt",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "{\"startcolor\":\"ESC[41m\",\"text\":\"Leider abgelehnt\",\"endcolor\":\"ESC[49m\"}",
                "tot": "json"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 750,
        "y": 300,
        "wires": [
            [
                "d831a280.a5dfa",
                "542ada55.a5bb24"
            ]
        ]
    },
    {
        "id": "1ea807e8.ca9a78",
        "type": "change",
        "z": "ec4b3da9.4704c",
        "name": "Tuer geht auf",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "{\"startcolor\":\"ESC[42m\",\"text\":\"Tuer oeffnet sich in ein paar Sekunden\",\"endcolor\":\"ESC[49m\"}",
                "tot": "json"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 730,
        "y": 260,
        "wires": [
            [
                "d831a280.a5dfa",
                "542ada55.a5bb24"
            ]
        ]
    },
    {
        "id": "e9c73fda.d586c",
        "type": "change",
        "z": "ec4b3da9.4704c",
        "name": "Code wird geprueft",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "{\"startcolor\":\"ESC[104m\",\"text\":\"Code wird geprueft\",\"endcolor\":\"ESC[49m\"}",
                "tot": "json"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 1030,
        "y": 220,
        "wires": [
            [
                "d831a280.a5dfa",
                "542ada55.a5bb24"
            ]
        ]
    },
    {
        "id": "542ada55.a5bb24",
        "type": "debug",
        "z": "ec4b3da9.4704c",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "x": 1290,
        "y": 340,
        "wires": []
    },
    {
        "id": "e837f008.6ba9b",
        "type": "websocket-client",
        "z": "",
        "path": "wss://localhost:8000/ws/requests",
        "tls": "5291fdc.dc4a604",
        "wholemsg": "false"
    },
    {
        "id": "1d164033.b808e",
        "type": "mqtt-broker",
        "z": "",
        "name": "docker mqtt",
        "broker": "localhost",
        "port": "1883",
        "clientid": "",
        "usetls": false,
        "compatmode": false,
        "keepalive": "60",
        "cleansession": true,
        "birthTopic": "",
        "birthQos": "0",
        "birthPayload": "",
        "closeTopic": "",
        "closeQos": "0",
        "closePayload": "",
        "willTopic": "",
        "willQos": "0",
        "willPayload": ""
    },
    {
        "id": "a681ccbf.f47c8",
        "type": "ui_group",
        "z": "",
        "name": "Tür",
        "tab": "4cb4268d.fe44f8",
        "order": 1,
        "disp": true,
        "width": "6",
        "collapse": false
    },
    {
        "id": "5291fdc.dc4a604",
        "type": "tls-config",
        "z": "",
        "name": "",
        "cert": "",
        "key": "",
        "ca": "",
        "certname": "",
        "keyname": "",
        "caname": "",
        "servername": "",
        "verifyservercert": false
    },
    {
        "id": "4cb4268d.fe44f8",
        "type": "ui_tab",
        "z": "",
        "name": "Company",
        "icon": "dashboard",
        "disabled": false,
        "hidden": false
    }
]