compose.yml für dockge dazu
This commit is contained in:
17
Dockerfile
Normal file
17
Dockerfile
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
FROM node:20-alpine
|
||||||
|
|
||||||
|
WORKDIR /opt/app
|
||||||
|
|
||||||
|
RUN apk add --no-cache tzdata
|
||||||
|
ENV TZ=Europe/Berlin
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN npm ci --omit=dev
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
EXPOSE 3011
|
||||||
|
|
||||||
|
CMD ["npm", "start"]
|
||||||
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
FROM node:9-alpine
|
|
||||||
|
|
||||||
WORKDIR /opt/app
|
|
||||||
ADD . /opt/app
|
|
||||||
|
|
||||||
RUN apk add --no-cache tzdata
|
|
||||||
ENV TZ Europe/Berlin
|
|
||||||
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
|
||||||
|
|
||||||
RUN touch cmds.sh \
|
|
||||||
&& echo 'npm start' >>cmds.sh
|
|
||||||
|
|
||||||
EXPOSE 3011
|
|
||||||
|
|
||||||
CMD sh ./cmds.sh
|
|
||||||
|
|
||||||
36
compose.yml
Normal file
36
compose.yml
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# Docker Compose für Production Server mit Traefik
|
||||||
|
services:
|
||||||
|
laufschrift:
|
||||||
|
image: docker.citysensor.de/laufschrift:latest
|
||||||
|
container_name: laufschrift
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=production
|
||||||
|
- MQTTHOST=${MQTTHOST:-localhost}
|
||||||
|
- MQTTPORT=${MQTTPORT:-1883}
|
||||||
|
- MQTTUSR=${MQTTUSR:-}
|
||||||
|
- MQTTPWD=${MQTTPWD:-}
|
||||||
|
- TOPIC=${TOPIC:-sonoff}
|
||||||
|
- SWITCH_API_TOKEN=${SWITCH_API_TOKEN:-}
|
||||||
|
labels:
|
||||||
|
- traefik.enable=true
|
||||||
|
- traefik.http.routers.laufschrift.entrypoints=http
|
||||||
|
- traefik.http.routers.laufschrift.rule=Host(`laufschrift.fuerst-stuttgart.de`)
|
||||||
|
- traefik.http.middlewares.laufschrift-https-redirect.redirectscheme.scheme=https
|
||||||
|
- traefik.http.routers.laufschrift.middlewares=laufschrift-https-redirect
|
||||||
|
- traefik.http.routers.laufschrift-secure.entrypoints=https
|
||||||
|
- traefik.http.routers.laufschrift-secure.rule=Host(`laufschrift.fuerst-stuttgart.de`)
|
||||||
|
- traefik.http.routers.laufschrift-secure.tls=true
|
||||||
|
- traefik.http.routers.laufschrift-secure.tls.certresolver=letsencrypt
|
||||||
|
- traefik.http.routers.laufschrift-secure.service=laufschrift
|
||||||
|
- traefik.http.services.laufschrift.loadbalancer.server.port=3000
|
||||||
|
networks:
|
||||||
|
- proxy
|
||||||
|
- gitea-internal
|
||||||
|
networks:
|
||||||
|
proxy:
|
||||||
|
name: dockge_default
|
||||||
|
external: true
|
||||||
|
gitea-internal:
|
||||||
|
name: gitea_gitea-internal
|
||||||
|
external: true
|
||||||
66
deploy.sh
Executable file
66
deploy.sh
Executable file
@@ -0,0 +1,66 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Deploy Script für laufschrift
|
||||||
|
# Baut das Docker Image und lädt es zu docker.citysensor.de hoch
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Konfiguration
|
||||||
|
REGISTRY="docker.citysensor.de"
|
||||||
|
IMAGE_NAME="laufschrift"
|
||||||
|
TAG="${TAG:-$(date +%Y%m%d%H%M)}" # default Datum
|
||||||
|
FULL_IMAGE="${REGISTRY}/${IMAGE_NAME}:${TAG}"
|
||||||
|
|
||||||
|
# Build-Datum
|
||||||
|
BUILD_DATE=$(date +%d.%m.%Y)
|
||||||
|
|
||||||
|
echo "=========================================="
|
||||||
|
echo "Laufschrift Deploy Script"
|
||||||
|
echo "=========================================="
|
||||||
|
echo "Registry: ${REGISTRY}"
|
||||||
|
echo "Image: ${IMAGE_NAME}"
|
||||||
|
echo "Tag: ${TAG}"
|
||||||
|
echo "Build-Datum: ${BUILD_DATE}"
|
||||||
|
echo "=========================================="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 1. Login zur Registry (falls noch nicht eingeloggt)
|
||||||
|
echo ">>> Login zu ${REGISTRY}..."
|
||||||
|
docker login "${REGISTRY}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 2. Multiplatform Builder einrichten (docker-container driver erforderlich)
|
||||||
|
echo ">>> Richte Multiplatform Builder ein..."
|
||||||
|
if ! docker buildx inspect multiplatform-builder &>/dev/null; then
|
||||||
|
docker buildx create --name multiplatform-builder --driver docker-container --bootstrap
|
||||||
|
fi
|
||||||
|
docker buildx use multiplatform-builder
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 3. Docker Image bauen und pushen (Multiplatform)
|
||||||
|
echo ">>> Baue Multiplatform Docker Image und pushe zu Registry..."
|
||||||
|
docker buildx build \
|
||||||
|
--platform linux/amd64,linux/arm64 \
|
||||||
|
--build-arg BUILD_DATE="${BUILD_DATE}" \
|
||||||
|
-t "${FULL_IMAGE}" \
|
||||||
|
--push \
|
||||||
|
.
|
||||||
|
|
||||||
|
# 4. Keep :latest in sync for simple rollbacks and manual usage.
|
||||||
|
echo ">>> Tagge das image zusätzlich als :latest ..."
|
||||||
|
docker buildx imagetools create \
|
||||||
|
-t "${REGISTRY}/${IMAGE_NAME}:latest" \
|
||||||
|
"${FULL_IMAGE}"
|
||||||
|
|
||||||
|
|
||||||
|
echo ">>> Build und Push erfolgreich!"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=========================================="
|
||||||
|
echo "✓ Deploy erfolgreich abgeschlossen!"
|
||||||
|
echo "=========================================="
|
||||||
|
echo ""
|
||||||
|
echo "Auf dem Server ausführen:"
|
||||||
|
echo " docker pull ${FULL_IMAGE}"
|
||||||
|
echo " docker-compose -f docker-compose.prod.yml up -d"
|
||||||
|
echo ""
|
||||||
60
deploy_citysensor.sh
Executable file
60
deploy_citysensor.sh
Executable file
@@ -0,0 +1,60 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Deploys laufschrift to docker.citysensor.de by:
|
||||||
|
# 1) building and pushing a multi-platform image
|
||||||
|
# 2) syncing compose/env files to the remote host
|
||||||
|
# 3) pulling and restarting the service remotely
|
||||||
|
|
||||||
|
REMOTE_HOST="${REMOTE_HOST:-docker.citysensor.de}"
|
||||||
|
REMOTE_USER="${REMOTE_USER:-rxf}"
|
||||||
|
REMOTE_PORT="${REMOTE_PORT:-22022}"
|
||||||
|
REMOTE_DIR="${REMOTE_DIR:-/opt/laufschrift}"
|
||||||
|
|
||||||
|
IMAGE_REPO="${IMAGE_REPO:-gitea.fuerst-stuttgart.de/admin/laufschrift}"
|
||||||
|
IMAGE_TAG="${IMAGE_TAG:-$(date +%Y%m%d%H%M)}"
|
||||||
|
PLATFORMS="${PLATFORMS:-linux/amd64,linux/arm64}"
|
||||||
|
|
||||||
|
DEPLOY_ENV_FILE="${DEPLOY_ENV_FILE:-.env}"
|
||||||
|
BUILD_PUSH="${BUILD_PUSH:-1}"
|
||||||
|
|
||||||
|
FULL_IMAGE="${IMAGE_REPO}:${IMAGE_TAG}"
|
||||||
|
REMOTE="${REMOTE_USER}@${REMOTE_HOST}"
|
||||||
|
|
||||||
|
if [[ ! -f "docker-compose.yml" ]]; then
|
||||||
|
echo "ERROR: docker-compose.yml not found in current directory."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -f "${DEPLOY_ENV_FILE}" ]]; then
|
||||||
|
echo "ERROR: ${DEPLOY_ENV_FILE} not found."
|
||||||
|
echo " Create it first, e.g. from .env.example"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${BUILD_PUSH}" == "1" ]]; then
|
||||||
|
echo "==> Building and pushing multi-platform image: ${FULL_IMAGE}"
|
||||||
|
docker buildx build \
|
||||||
|
--platform "${PLATFORMS}" \
|
||||||
|
-f Dockerfile_laufschrift \
|
||||||
|
-t "${FULL_IMAGE}" \
|
||||||
|
--push \
|
||||||
|
.
|
||||||
|
|
||||||
|
# Keep :latest in sync for simple rollbacks and manual usage.
|
||||||
|
docker buildx imagetools create \
|
||||||
|
-t "${IMAGE_REPO}:latest" \
|
||||||
|
"${FULL_IMAGE}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "==> Syncing compose and env to ${REMOTE}:${REMOTE_DIR}"
|
||||||
|
ssh -p "${REMOTE_PORT}" "${REMOTE}" "mkdir -p '${REMOTE_DIR}'"
|
||||||
|
scp -P "${REMOTE_PORT}" docker-compose.yml "${REMOTE}:${REMOTE_DIR}/docker-compose.yml"
|
||||||
|
scp -P "${REMOTE_PORT}" "${DEPLOY_ENV_FILE}" "${REMOTE}:${REMOTE_DIR}/.env"
|
||||||
|
|
||||||
|
echo "==> Deploying ${FULL_IMAGE} on ${REMOTE_HOST}"
|
||||||
|
ssh -p "${REMOTE_PORT}" "${REMOTE}" "cd '${REMOTE_DIR}' && IMAGE='${FULL_IMAGE}' docker compose pull app && IMAGE='${FULL_IMAGE}' docker compose up -d --no-build --remove-orphans"
|
||||||
|
|
||||||
|
echo "==> Deployment finished"
|
||||||
|
echo " Host: ${REMOTE_HOST}"
|
||||||
|
echo " Image: ${FULL_IMAGE}"
|
||||||
20
docker-compose.yml
Normal file
20
docker-compose.yml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
image: docker.citysensor.de/laufschrift:latest
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
platforms:
|
||||||
|
- linux/amd64
|
||||||
|
- linux/arm64
|
||||||
|
ports:
|
||||||
|
- "3011:3011"
|
||||||
|
environment:
|
||||||
|
MQTTHOST: ${MQTTHOST:-localhost}
|
||||||
|
MQTTPORT: ${MQTTPORT:-1883}
|
||||||
|
MQTTUSR: ${MQTTUSR:-}
|
||||||
|
MQTTPWD: ${MQTTPWD:-}
|
||||||
|
TOPIC: ${TOPIC:-sonoff}
|
||||||
|
SWITCH_API_TOKEN: ${SWITCH_API_TOKEN:-}
|
||||||
|
restart: unless-stopped
|
||||||
2831
package-lock.json
generated
2831
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
21
package.json
21
package.json
@@ -1,19 +1,18 @@
|
|||||||
{
|
{
|
||||||
"name": "laufschrift",
|
"name": "laufschrift",
|
||||||
"version": "0.0.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node ./bin/www >>/var/log/laufschrift.log 2>&1"
|
"start": "node ./bin/www"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"await-fs": "^1.0.0",
|
"cookie-parser": "^1.4.7",
|
||||||
"cookie-parser": "~1.4.3",
|
"debug": "^4.4.3",
|
||||||
"debug": "~2.6.9",
|
"express": "^4.22.1",
|
||||||
"express": "~4.16.0",
|
"http-errors": "^2.0.1",
|
||||||
"http-errors": "~1.6.2",
|
"moment": "^2.30.1",
|
||||||
"moment": "^2.24.0",
|
"morgan": "^1.10.1",
|
||||||
"morgan": "~1.9.0",
|
"mqtt": "^5.15.0",
|
||||||
"mqtt": "^2.18.8",
|
"pug": "^3.0.3"
|
||||||
"pug": "^2.0.3"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,16 @@ $(document).ready(function() {
|
|||||||
console.log("Es geht los");
|
console.log("Es geht los");
|
||||||
|
|
||||||
const URL = "switch/";
|
const URL = "switch/";
|
||||||
const checktime = 5000000; // alle 30sec Zustand prüfen
|
const POLL_INTERVAL_MS = 1000;
|
||||||
|
const queryToken = new URLSearchParams(window.location.search).get('token');
|
||||||
|
const storedToken = localStorage.getItem('switchApiToken');
|
||||||
|
const API_TOKEN = queryToken || storedToken || '';
|
||||||
|
let interval = null;
|
||||||
let status = "";
|
let status = "";
|
||||||
let tr;
|
|
||||||
|
if (queryToken) {
|
||||||
|
localStorage.setItem('switchApiToken', queryToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
$('#versn').html("V " + VERSION + ' ' + VDATE); // Vesion anzeigen
|
$('#versn').html("V " + VERSION + ' ' + VDATE); // Vesion anzeigen
|
||||||
@@ -19,13 +25,13 @@ $(document).ready(function() {
|
|||||||
// sendCommand(URL,"PowerOnState%200"); // OFF bei Power ON
|
// sendCommand(URL,"PowerOnState%200"); // OFF bei Power ON
|
||||||
// sendCommand(URL,"PulseTime%20"+(brenndauer+100)); // Brenndauer einstellen
|
// sendCommand(URL,"PulseTime%20"+(brenndauer+100)); // Brenndauer einstellen
|
||||||
|
|
||||||
let interval = setInterval(sendTimedCommand, 1000); // alle Sekunde pollen
|
startPolling();
|
||||||
|
|
||||||
document.addEventListener('visibilitychange', function (event) {
|
document.addEventListener('visibilitychange', function (event) {
|
||||||
if (!document.hidden) {
|
if (!document.hidden) {
|
||||||
interval = setInterval(sendTimedCommand, 1000);
|
startPolling();
|
||||||
} else {
|
} else {
|
||||||
clearTimeout(interval); // The page is hidden.
|
stopPolling();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -33,8 +39,7 @@ $(document).ready(function() {
|
|||||||
sendCommand(URL, "get_status")
|
sendCommand(URL, "get_status")
|
||||||
|
|
||||||
$("#schalter").click(function () {
|
$("#schalter").click(function () {
|
||||||
let message;
|
if (status === 'ON') {
|
||||||
if (status == 'ON') {
|
|
||||||
console.log("Sende OFF")
|
console.log("Sende OFF")
|
||||||
sendCommand(URL, "switch_off")
|
sendCommand(URL, "switch_off")
|
||||||
} else {
|
} else {
|
||||||
@@ -45,18 +50,36 @@ $(document).ready(function() {
|
|||||||
|
|
||||||
function sendCommand(url, cmnd) {
|
function sendCommand(url, cmnd) {
|
||||||
console.log("sendCommand", cmnd);
|
console.log("sendCommand", cmnd);
|
||||||
const p = new Promise((resolve, reject) => {
|
let endpoint = url + cmnd;
|
||||||
$.getJSON(url + cmnd, function (data, err) { // AJAX Call
|
if (API_TOKEN) {
|
||||||
if (err != 'success') {
|
endpoint += '?token=' + encodeURIComponent(API_TOKEN);
|
||||||
reject(err);
|
}
|
||||||
alert("Fehler <br />" + err); // if error, show it
|
|
||||||
} else {
|
return new Promise((resolve, reject) => {
|
||||||
|
$.getJSON(endpoint)
|
||||||
|
.done(function (data) {
|
||||||
console.log("gekommen: ", data);
|
console.log("gekommen: ", data);
|
||||||
resolve(data);
|
resolve(data);
|
||||||
}
|
})
|
||||||
});
|
.fail(function (_jqXHR, textStatus, errorThrown) {
|
||||||
|
const msg = errorThrown || textStatus || "Unbekannter Fehler";
|
||||||
|
reject(msg);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
return p;
|
}
|
||||||
|
|
||||||
|
function startPolling() {
|
||||||
|
if (interval !== null) {
|
||||||
|
clearInterval(interval);
|
||||||
|
}
|
||||||
|
interval = setInterval(sendTimedCommand, POLL_INTERVAL_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopPolling() {
|
||||||
|
if (interval !== null) {
|
||||||
|
clearInterval(interval);
|
||||||
|
interval = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendTimedCommand() {
|
function sendTimedCommand() {
|
||||||
@@ -65,13 +88,12 @@ $(document).ready(function() {
|
|||||||
.then(data => {
|
.then(data => {
|
||||||
// console.log("Timed Status= ", st);
|
// console.log("Timed Status= ", st);
|
||||||
status = data.relais;
|
status = data.relais;
|
||||||
if (status != 'pending') {
|
if (status !== 'pending') {
|
||||||
clearTimeout(tr);
|
if (status === 'ON') {
|
||||||
if (status == 'ON') {
|
|
||||||
$('#schalter').html('Laufschrift <b>AUS</b> schalten');
|
$('#schalter').html('Laufschrift <b>AUS</b> schalten');
|
||||||
$('#status').text('EIN');
|
$('#status').text('EIN');
|
||||||
$('#laufzeile').addClass('machrot');
|
$('#laufzeile').addClass('machrot');
|
||||||
if(data.offtime != undefined) {
|
if (data.offtime !== undefined) {
|
||||||
$('#auszeit').text(data.offtime);
|
$('#auszeit').text(data.offtime);
|
||||||
$('#auszeile').show();
|
$('#auszeile').show();
|
||||||
}
|
}
|
||||||
@@ -82,6 +104,9 @@ $(document).ready(function() {
|
|||||||
$('#auszeile').hide();
|
$('#auszeile').hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.log('Statusabfrage fehlgeschlagen:', err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
245
routes/switch.js
245
routes/switch.js
@@ -1,118 +1,169 @@
|
|||||||
var express = require('express');
|
var express = require('express');
|
||||||
let router = express.Router();
|
var router = express.Router();
|
||||||
let mqtt = require('mqtt');
|
var mqtt = require('mqtt');
|
||||||
let moment = require('moment');
|
var moment = require('moment');
|
||||||
const fs = require('await-fs');
|
var fs = require('fs/promises');
|
||||||
|
var crypto = require('crypto');
|
||||||
|
|
||||||
|
var DEFAULT_BRENNDAUER = 300;
|
||||||
|
var brenndauer = DEFAULT_BRENNDAUER;
|
||||||
|
|
||||||
const configName = "config.json"
|
var MQTTHOST = process.env.MQTTHOST || 'localhost';
|
||||||
|
var MQTTPORT = process.env.MQTTPORT || 1883;
|
||||||
|
var MQTTUSR = process.env.MQTTUSR || '';
|
||||||
|
var MQTTPWD = process.env.MQTTPWD || '';
|
||||||
|
var TOPIC = process.env.TOPIC || 'sonoff';
|
||||||
|
var SWITCH_API_TOKEN = process.env.SWITCH_API_TOKEN || '';
|
||||||
|
|
||||||
|
var switchOffTimer;
|
||||||
|
var state = {
|
||||||
|
connect: 'disconnected',
|
||||||
|
relais: 'UNKNOWN',
|
||||||
|
offtime: undefined,
|
||||||
|
lastError: undefined
|
||||||
|
};
|
||||||
|
|
||||||
let brenndauer = 300; // 20sec Relais ON
|
var client = mqtt.connect('mqtt://' + MQTTHOST + ':' + MQTTPORT, {
|
||||||
|
username: MQTTUSR,
|
||||||
|
password: MQTTPWD
|
||||||
|
});
|
||||||
|
|
||||||
// MQTT:
|
console.log('Start: ', moment().format('YYYY-MM-DD HH:mm'));
|
||||||
let MQTTHOST = process.env.MQTTHOST;
|
|
||||||
let MQTTPORT = process.env.MQTTPORT;
|
|
||||||
let MQTTUSR = process.env.MQTTUSR;
|
|
||||||
let MQTTPWD = process.env.MQTTPWD;
|
|
||||||
let TOPIC=process.env.TOPIC;
|
|
||||||
|
|
||||||
|
|
||||||
if (MQTTHOST === undefined) { MQTTHOST = 'localhost';}
|
|
||||||
if (MQTTPORT === undefined) { MQTTPORT = 1883;}
|
|
||||||
if (MQTTUSR === undefined) { MQTTUSR = "";}
|
|
||||||
if (MQTTPWD === undefined) { MQTTPWD = "";}
|
|
||||||
if (TOPIC === undefined) { TOPIC = "sonoff";}
|
|
||||||
|
|
||||||
/*
|
|
||||||
if (MQTTHOST === undefined) { MQTTHOST = 'laufschrift.rexfue.de';}
|
|
||||||
if (MQTTPORT === undefined) { MQTTPORT = 1883;}
|
|
||||||
if (MQTTUSR === undefined) { MQTTUSR = "stwLauf";}
|
|
||||||
if (MQTTPWD === undefined) { MQTTPWD = "37CrNcgP";}
|
|
||||||
*/
|
|
||||||
// console.log("H",MQTTHOST," P",MQTTPORT," U",MQTTUSR," p",MQTTPWD);
|
|
||||||
|
|
||||||
let tio;
|
|
||||||
let connected = false;
|
|
||||||
let relais = 'MIST';
|
|
||||||
let client = mqtt.connect("mqtt://" + MQTTHOST + ":" + MQTTPORT,{username: MQTTUSR, password: MQTTPWD});
|
|
||||||
let offtime = "";
|
|
||||||
let retur = {};
|
|
||||||
|
|
||||||
console.log("Start: ", moment().format("YYYY-MM-DD HH:mm"));
|
|
||||||
|
|
||||||
client.on('connect', function() {
|
client.on('connect', function() {
|
||||||
connected = true;
|
state.connect = 'connected';
|
||||||
retur.connect = 'connected';
|
state.lastError = undefined;
|
||||||
client.subscribe('stat/'+TOPIC+'/POWER');
|
client.subscribe('stat/' + TOPIC + '/POWER', function(err) {
|
||||||
})
|
if (err) {
|
||||||
|
state.lastError = 'subscribe failed: ' + err.message;
|
||||||
client.on('message',function(topix,message) {
|
|
||||||
relais = message.toString();
|
|
||||||
console.log("Status = ",relais);
|
|
||||||
if (relais == 'OFF') {
|
|
||||||
delete retur.offtime;
|
|
||||||
}
|
|
||||||
retur.relais = relais;
|
|
||||||
});
|
|
||||||
|
|
||||||
client.on('reconnect', function(){
|
|
||||||
retur.connect = 'reconnect';
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// Konfig einlesen und entsprechende Variablen einstellen
|
|
||||||
(async () => {
|
|
||||||
try{
|
|
||||||
let json = await fs.readFile('config/config.json','utf8')
|
|
||||||
js = JSON.parse(json);
|
|
||||||
if(js.brenndauer != undefined) {
|
|
||||||
brenndauer = js.brenndauer;
|
|
||||||
}
|
}
|
||||||
console.log(brenndauer);
|
});
|
||||||
}catch(err){
|
});
|
||||||
console.log(err)
|
|
||||||
|
client.on('reconnect', function() {
|
||||||
|
state.connect = 'reconnect';
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('offline', function() {
|
||||||
|
state.connect = 'offline';
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('error', function(err) {
|
||||||
|
state.lastError = err.message;
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('message', function(_topic, message) {
|
||||||
|
state.relais = message.toString();
|
||||||
|
if (state.relais === 'OFF') {
|
||||||
|
state.offtime = undefined;
|
||||||
}
|
}
|
||||||
})()
|
});
|
||||||
|
|
||||||
|
|
||||||
// beim start mal den Zustand abfragen
|
|
||||||
doPublish("");
|
|
||||||
|
|
||||||
|
function getResponse() {
|
||||||
|
return {
|
||||||
|
connect: state.connect,
|
||||||
|
relais: state.relais,
|
||||||
|
offtime: state.offtime,
|
||||||
|
lastError: state.lastError
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function doPublish(payload) {
|
function doPublish(payload) {
|
||||||
// retur.relais='pending';
|
client.publish('cmnd/' + TOPIC + '/Power', payload, function(err) {
|
||||||
client.publish('cmnd/'+TOPIC+'/Power',payload);
|
if (err) {
|
||||||
if (payload == 'On') {
|
state.lastError = err.message;
|
||||||
offtime = moment().add(brenndauer, 's').format("HH.mm");
|
}
|
||||||
tio = setTimeout(doPublish, brenndauer * 1000, "Off");
|
});
|
||||||
retur.offtime = offtime;
|
|
||||||
} else if (payload == 'Off') {
|
if (payload === 'On') {
|
||||||
clearTimeout(tio);
|
clearTimeout(switchOffTimer);
|
||||||
|
state.offtime = moment().add(brenndauer, 's').format('HH.mm');
|
||||||
|
switchOffTimer = setTimeout(doPublish, brenndauer * 1000, 'Off');
|
||||||
|
} else if (payload === 'Off') {
|
||||||
|
clearTimeout(switchOffTimer);
|
||||||
|
state.offtime = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// end MQTT
|
|
||||||
|
|
||||||
/* GET switch data */
|
// Read optional runtime config.
|
||||||
router.get('/:cmd', function(req, res, next) {
|
(async function loadConfig() {
|
||||||
let cmd = req.params.cmd;
|
try {
|
||||||
let wert = req.query.wert;
|
var json = await fs.readFile('config/config.json', 'utf8');
|
||||||
|
var cfg = JSON.parse(json);
|
||||||
if (cmd == 'get_status') {
|
if (cfg.brenndauer !== undefined) {
|
||||||
doPublish("");
|
brenndauer = Number(cfg.brenndauer) || DEFAULT_BRENNDAUER;
|
||||||
} else if (cmd == 'switch_on') {
|
}
|
||||||
doPublish("On");
|
} catch (err) {
|
||||||
} else if (cmd == 'switch_off') {
|
state.lastError = 'config load failed: ' + err.message;
|
||||||
doPublish("Off");
|
|
||||||
} else if (cmd == 'check') {
|
|
||||||
|
|
||||||
} else {
|
|
||||||
retur = { error: "invalid command"};
|
|
||||||
res.json(retur);
|
|
||||||
}
|
}
|
||||||
console.log("return: ",retur)
|
})();
|
||||||
res.json(retur);
|
|
||||||
|
// Query initial state once at startup.
|
||||||
|
doPublish('');
|
||||||
|
|
||||||
|
function timingSafeEqualString(a, b) {
|
||||||
|
var aBuf = Buffer.from(String(a));
|
||||||
|
var bBuf = Buffer.from(String(b));
|
||||||
|
if (aBuf.length !== bBuf.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return crypto.timingSafeEqual(aBuf, bBuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractRequestToken(req) {
|
||||||
|
var authHeader = req.get('authorization') || '';
|
||||||
|
if (authHeader.startsWith('Bearer ')) {
|
||||||
|
return authHeader.slice(7).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
var apiKeyHeader = req.get('x-api-key');
|
||||||
|
if (apiKeyHeader) {
|
||||||
|
return apiKeyHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof req.query.token === 'string') {
|
||||||
|
return req.query.token;
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
router.use(function(req, res, next) {
|
||||||
|
if (!SWITCH_API_TOKEN) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
var providedToken = extractRequestToken(req);
|
||||||
|
if (!providedToken || !timingSafeEqualString(providedToken, SWITCH_API_TOKEN)) {
|
||||||
|
return res.status(401).json({ error: 'unauthorized' });
|
||||||
|
}
|
||||||
|
|
||||||
|
return next();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.get('/:cmd', function(req, res) {
|
||||||
|
var cmd = req.params.cmd;
|
||||||
|
|
||||||
|
if (cmd === 'get_status') {
|
||||||
|
doPublish('');
|
||||||
|
return res.json(getResponse());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd === 'switch_on') {
|
||||||
|
doPublish('On');
|
||||||
|
return res.json(getResponse());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd === 'switch_off') {
|
||||||
|
doPublish('Off');
|
||||||
|
return res.json(getResponse());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd === 'check') {
|
||||||
|
return res.json(getResponse());
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status(400).json({ error: 'invalid command' });
|
||||||
|
});
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|||||||
Reference in New Issue
Block a user