added nodejs container to log configured data to influxdb and ctrl solar redistribution in case of a full storage
This commit is contained in:
parent
f356f92a1f
commit
732eb79cb3
2
.gitignore
vendored
2
.gitignore
vendored
@ -1 +1,3 @@
|
|||||||
/data
|
/data
|
||||||
|
/nodeApp/node_modules
|
||||||
|
/nodeApp/package-lock.json
|
||||||
@ -5,14 +5,13 @@ services:
|
|||||||
image: influxdb:2.7-alpine
|
image: influxdb:2.7-alpine
|
||||||
env_file:
|
env_file:
|
||||||
- ./influxv2.env
|
- ./influxv2.env
|
||||||
|
restart: always
|
||||||
volumes:
|
volumes:
|
||||||
# Mount for influxdb data directory and configuration
|
# Mount for influxdb data directory and configuration
|
||||||
- /DockerDataPool/solarCtrl/influxDB:/var/lib/influxdb2:rw
|
- /DockerDataPool/solarCtrl/influxDB:/var/lib/influxdb2:rw
|
||||||
- ./influxDbConfig.yml:/etc/influxdb2/config.yml
|
- ./influxDbConfig.yml:/etc/influxdb2/config.yml
|
||||||
ports:
|
ports:
|
||||||
- 8080:8086
|
- 8086:8086
|
||||||
networks:
|
|
||||||
- solarctrlInternal
|
|
||||||
# telegraf:
|
# telegraf:
|
||||||
# image: telegraf:1.27-alpine
|
# image: telegraf:1.27-alpine
|
||||||
# depends_on:
|
# depends_on:
|
||||||
@ -32,8 +31,22 @@ services:
|
|||||||
- ./data/mosquitto/log:/mosquitto/log
|
- ./data/mosquitto/log:/mosquitto/log
|
||||||
ports:
|
ports:
|
||||||
- 1883:1883
|
- 1883:1883
|
||||||
networks:
|
app:
|
||||||
- solarctrlInternal
|
depends_on:
|
||||||
networks:
|
- influxdb
|
||||||
solarctrlInternal:
|
- 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"
|
||||||
|
|||||||
@ -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
7
nodeApp/Dockerfile
Normal 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
201
nodeApp/config.json
Normal 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
163
nodeApp/index.js
Normal 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
3
nodeApp/ips
Normal 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
16
nodeApp/package.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user