added nodejs container to log configured data to influxdb and ctrl solar redistribution in case of a full storage

This commit is contained in:
Florian Weber 2024-08-12 23:00:48 +00:00
parent f356f92a1f
commit 732eb79cb3
8 changed files with 425 additions and 19 deletions

2
.gitignore vendored
View File

@ -1 +1,3 @@
/data
/nodeApp/node_modules
/nodeApp/package-lock.json

View File

@ -5,14 +5,13 @@ services:
image: influxdb:2.7-alpine
env_file:
- ./influxv2.env
restart: always
volumes:
# Mount for influxdb data directory and configuration
- /DockerDataPool/solarCtrl/influxDB:/var/lib/influxdb2:rw
- ./influxDbConfig.yml:/etc/influxdb2/config.yml
ports:
- 8080:8086
networks:
- solarctrlInternal
- 8086:8086
# telegraf:
# image: telegraf:1.27-alpine
# depends_on:
@ -32,8 +31,22 @@ services:
- ./data/mosquitto/log:/mosquitto/log
ports:
- 1883:1883
networks:
- solarctrlInternal
networks:
solarctrlInternal:
app:
depends_on:
- influxdb
- mosquitto
build: ./nodeApp
restart: always
volumes:
- ./nodeApp/config.json:/app/config.json
grafana:
image: grafana/grafana:10.3.1
container_name: grafana
depends_on:
- influxdb
restart: always
ports:
- 3000:3000
volumes:
- "/DockerDataPool/solarCtrl/grafana:/var/lib/grafana"
user: "1000"

View File

@ -1 +1,2 @@
heatCtrl:$7$101$Q0URo3gi5IgUwTLB$2M0eLGYBq4xP/RD1p3SC3IBJQs2hkEOH7HNm/DNt2/Zq+qmDMVNUic0Mom34sgXSLYKqmXfLQhVi8TjO8Tcu0Q==
heatCtrl:$7$101$7MPYVzvwM+vfvapK$gF4xFS7QLeWa3mDuWmKqnI2rNysyPZyo5ZnYyIYF+R00FmU/uHxP1me7w3xRKJlwZ28rXKX0eEaUKNluiBjToQ==
relais:$7$101$KKzqmsAlu8/mmywt$DhhJvf51AmXBrRW1BLOqhKYla/uNhRTFUbeope8/RJ2/FLfupz42spBZij3lHs8GobNrGylo9aT0NcrZcrtnvA==

7
nodeApp/Dockerfile Normal file
View File

@ -0,0 +1,7 @@
FROM node:lts-iron
WORKDIR /app
COPY package.json .
RUN npm install
COPY index.js .
ENTRYPOINT ["node"]
CMD ["index.js"]

201
nodeApp/config.json Normal file
View File

@ -0,0 +1,201 @@
{
"inputs": [
{
"register": 8708,
"label": "PufferHeizraumTemperatur1",
"unitConversionDivider": 10,
"type": "s16"
},
{
"register": 8776,
"label": "PufferHeizraumTemperatur3",
"unitConversionDivider": 10,
"type": "s16"
},
{
"register": 8810,
"label": "PufferHeizraumTemperatur4",
"unitConversionDivider": 10,
"type": "s16"
},
{
"register": 8844,
"label": "PufferHeizraumTemperatur5",
"unitConversionDivider": 10,
"type": "s16"
},
{
"register": 8710,
"label": "PufferHolzraumTemperatur1",
"unitConversionDivider": 10,
"type": "s16"
},
{
"register": 8778,
"label": "PufferHolzraumTemperatur3",
"unitConversionDivider": 10,
"type": "s16"
},
{
"register": 8846,
"label": "PufferHolzraumTemperatur5",
"unitConversionDivider": 10,
"type": "s16"
},
{
"register": 8215,
"label": "Flammtemperatur",
"unitConversionDivider": 10,
"type": "s16"
},
{
"register": 8214,
"label": "Sauerstoff",
"unitConversionDivider": 10,
"type": "s16"
},
{
"register": 8242,
"label": "Primärluftklappe",
"unitConversionDivider": 10,
"type": "s16"
},
{
"register": 8243,
"label": "Sekundärluftklappe",
"unitConversionDivider": 10,
"type": "s16"
},
{
"register": 9498,
"label": "DrehzahlSaugzug",
"unitConversionDivider": 1,
"type": "s16"
},
{
"register": 8197,
"label": "Kesseltemperatur",
"unitConversionDivider": 10,
"type": "s16"
},
{
"register": 8202,
"label": "Rücklauftemperatur",
"unitConversionDivider": 10,
"type": "s16"
},
{
"register": 8212,
"label": "Kesselstatus",
"unitConversionDivider": 1,
"type": "u16"
},
{
"register": 8250,
"label": "Außentemperatur",
"unitConversionDivider": 10,
"type": "s16"
},
{
"register": 9049,
"label": "StatusSolar",
"unitConversionDivider": 1,
"type": "u16"
},
{
"register": 9080,
"label": "SolarKollektortemperatur",
"unitConversionDivider": 10,
"type": "s16"
},
{
"register": 9110,
"label": "SolarSpeichertemperatur",
"unitConversionDivider": 10,
"type": "s16"
},
{
"register": 9169,
"label": "Solarpumpe",
"unitConversionDivider": 1,
"type": "bool"
},
{
"register": 9215,
"label": "SolarWärmeleistung",
"unitConversionDivider": 1000,
"type": "u32"
},
{
"register": 9245,
"label": "SolarWärmemengeTag",
"unitConversionDivider": 1000,
"type": "u32"
},
{
"register": 9275,
"label": "SolarWärmemengeGesamt",
"unitConversionDivider": 1000,
"type": "u32"
},
{
"register": 9305,
"label": "SolarKollektorVorlauftemperatur",
"unitConversionDivider": 10,
"type": "s16"
},
{
"register": 9335,
"label": "SolarKollektorRücklauftemperatur",
"unitConversionDivider": 10,
"type": "s16"
},
{
"register": 9365,
"label": "SolarDurchfluss",
"unitConversionDivider": 100,
"type": "s16"
},
{
"register": 9466,
"label": "SolarpumpePWM",
"unitConversionDivider": 10,
"type": "u16"
},
{
"register": 8200,
"label": "Kesselpumpe",
"unitConversionDivider": 1,
"type": "bool"
},
{
"register": 8201,
"label": "KesselpumpePWM",
"unitConversionDivider": 10,
"type": "u16"
},
{
"register": 8206,
"label": "VerbraucherAnforderung",
"unitConversionDivider": 1,
"type": "u16"
}
],
"influxToken": "cMHBovac4VeXe60efDcBjItDxXz5LvQ7xcrN0R8yNMM9tEHFPK41hUSBsPsZWrVigXHm6T55EsnegIBSVeUFaQ==",
"interval": 5000,
"mqttBrokerAddress": "mqtt://192.168.1.241:1883",
"mqttUser": "heatCtrl",
"mqttPassword": "637013",
"UmschichtungSolar": {
"Notumschichtung": {
"Temperaturlabel": "PufferHeizraumTemperatur5",
"Ton": 70.0,
"Toff": 65.0,
"mqtt": {
"ctrlTopic": "RelaisUmschichtung/cmnd/POWER1",
"msgOn": "ON",
"msgOff": "OFF"
}
}
}
}

163
nodeApp/index.js Normal file
View File

@ -0,0 +1,163 @@
const Modbus = require('jsmodbus')
var fs = require("fs")
const net = require('net')
const mqtt = require("mqtt");
const { InfluxDB, Point } = require('@influxdata/influxdb-client')
const socket = new net.Socket()
const modbusClient = new Modbus.client.TCP(socket, 0);
let mqttClient = null;
const options = {
'host': '192.168.178.63',
'port': 502
};
let config = null;
let influxOrg = 'heatctrlOrg'
let influxBucket = 'measurements'
const influxUrl = 'http://influxdb:8086'
let influxClient = null;
let influxWriteClient = null;
let inputValues = {};
let configuredInputs = {};
// for reconnecting see node-net-reconnect npm module
// use socket.on('open', ...) when using serialport
socket.on('connect', function () {
// make some calls
setInterval(() => {
requestTemps();
}, config.interval);
});
fs.readFile('config.json', function (err, data) {
if (err) {
return console.error(err);
} else {
config = JSON.parse(data.toString());
configuredInputs = config.inputs;
configuredInputs.forEach(element => {
inputValues[element.label] = { value: null, synced: false, unitConversionDivider: 1 };
});
influxClient = new InfluxDB({ url: influxUrl, token: config.influxToken });
influxWriteClient = influxClient.getWriteApi(influxOrg, influxBucket, 'ms');
socket.connect(options);
mqttClient = mqtt.connect(config.mqttBrokerAddress, {
username: config.mqttUser,
password: config.mqttPassword
});
}
});
async function dispatchModbus() {
//console.log(CF2values);
calculateLogic();
writeToInflux();
clearSynced();
}
async function clearSynced() {
for (const key in inputValues) {
if (Object.hasOwnProperty.call(inputValues, key)) {
inputValues[key].synced = false;
}
}
}
async function requestTemps() {
configuredInputs.forEach(element => {
modbusClient.readInputRegisters(element.register, (element.type === "u32" | element.type === "s32") ? 2 : 1).then(function (resp) {
let value = null;
switch (element.type) {
case "u32": {
value = resp.response._body._valuesAsBuffer.readUInt32BE() / element.unitConversionDivider;
} break;
case "s32": {
value = resp.response._body._valuesAsBuffer.readInt32BE() / element.unitConversionDivider;
} break;
case "u16": {
value = resp.response._body._valuesAsBuffer.readUInt16BE() / element.unitConversionDivider;
} break;
case "s16": {
value = resp.response._body._valuesAsBuffer.readInt16BE() / element.unitConversionDivider;
} break;
case "bool": {
value = resp.response._body._values[0] / element.unitConversionDivider;
} break;
default: {
value = resp.response._body._values[0] / element.unitConversionDivider;
} break;
}
inputValues[element.label] = { value: value, synced: true };
let allSynced = true;
/*
if (resp.request._body.start === 9169) {
console.log(resp);
console.log(resp.response._body._values[0]);
}
*/
for (const key in inputValues) {
if (Object.hasOwnProperty.call(inputValues, key)) {
const element = inputValues[key];
allSynced &= element.synced;
}
}
if (allSynced) {
dispatchModbus();
}
}, console.error);
});
}
async function writeToInflux() {
for (const key in inputValues) {
if (Object.hasOwnProperty.call(inputValues, key)) {
let point = new Point('CF2').floatField(key, inputValues[key].value);
influxWriteClient.writePoint(point);
influxWriteClient.flush();
}
}
}
async function sendMQTT(topic, msg) {
let notumschichtung = config.UmschichtungSolar.Notumschichtung
if (typeof mqttClient !== 'undefined' || mqttClient !== null) {
if (mqttClient.connected) {
mqttClient.publish(topic, msg, { retain: true });
console.log(msg);
} else {
console.log("client not connected to the mqtt broker");
}
} else {
console.log("mqttClient is undefined or null");
}
//send status over mqtt
let val = 0.0;
if (msg == notumschichtung.mqtt.msgOn) {
val = 100.0;
} else if (msg == notumschichtung.mqtt.msgOff) {
val = 0.0;
}
let point = new Point('Solarumschichtung').floatField("pumpePercent", val);
influxWriteClient.writePoint(point);
influxWriteClient.flush();
}
async function calculateLogic() {
let notumschichtung = config.UmschichtungSolar.Notumschichtung
let temp = inputValues[notumschichtung.Temperaturlabel];
let topic = notumschichtung.mqtt.ctrlTopic;
let msgOn = notumschichtung.mqtt.msgOn;
let msgOff = notumschichtung.mqtt.msgOff;
let Ton = notumschichtung.Ton;
let Toff = notumschichtung.Toff;
if (temp.value >= Ton) {
sendMQTT(topic, msgOn);
} else if (temp.value <= Toff) {
sendMQTT(topic, msgOff);
}
}

3
nodeApp/ips Normal file
View File

@ -0,0 +1,3 @@
192.168.178.63 heizung relais
192.168.1.19 umschichtung relais
pw mqtt: 637013

16
nodeApp/package.json Normal file
View File

@ -0,0 +1,16 @@
{
"name": "solarctrl",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"@influxdata/influxdb-client": "^1.33.2",
"jsmodbus": "^4.0.10",
"mqtt": "^5.9.1"
}
}