Dateien nach "workflows/Heizung" hochladen

This commit is contained in:
2025-12-27 19:18:10 +01:00
parent 4d06cc80e0
commit f0c649b091
4 changed files with 1505 additions and 0 deletions

View File

@@ -0,0 +1,273 @@
{
"name": "CMI Canbus",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"field": "minutes"
}
]
}
},
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [
-672,
288
],
"id": "dcd1c622-b477-485f-8d04-ea672a82ad4d",
"name": "Schedule Trigger"
},
{
"parameters": {
"url": "http://192.168.2.186/INCLUDE/devpagex.cgi?pagex2=01025800&_=1759560490401",
"authentication": "genericCredentialType",
"genericAuthType": "httpBasicAuth",
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "admin",
"value": "admin"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
-448,
288
],
"id": "a6b369cd-d21d-4da7-8480-30e1f30f824b",
"name": "get Data from CMI Interface local",
"credentials": {
"httpBasicAuth": {
"id": "iJkXePPAceSyENTx",
"name": "CMI CAN Bus local"
}
}
},
{
"parameters": {
"jsCode": "// INPUT: items = [{ json: { data: \"<div class=...>...\" } }]\n// OUTPUT: [{ json: { flat, units, items } }]\n\nfunction parseValue(raw) {\n if (!raw) return { value: null, unit: null, state: null, on: null };\n const t = raw.trim().replace(/&nbsp;/g, ' ');\n if (/^(AUS|OFF|EIN|ON)$/i.test(t)) {\n return { value: null, unit: null, state: t.toUpperCase(), on: /^(EIN|ON)$/i.test(t) };\n }\n const m = t.match(/-?\\d+(?:[.,]\\d+)?/);\n const value = m ? parseFloat(m[0].replace(',', '.')) : null;\n const unit = m ? t.replace(m[0], '').trim() : null;\n return { value, unit: unit || null, state: null, on: null };\n}\n\nfunction toKey(label) {\n return label\n .normalize('NFD').replace(/[\\u0300-\\u036f]/g, '')\n .replace(/ä/g,'ae').replace(/ö/g,'oe').replace(/ü/g,'ue')\n .replace(/Ä/g,'Ae').replace(/Ö/g,'Oe').replace(/Ü/g,'Ue')\n .replace(/ß/g,'ss')\n .replace(/[^A-Za-z0-9]+/g,'_')\n .replace(/^_+|_+$/g,'');\n}\n\n// 1) HTML aus allen Eingangs-Items zusammenführen (meist ist es nur eines)\nconst html = items.map(i => i.json?.data ?? i.json?.body ?? i.json?.html ?? '').join('\\n');\n\n// 2) BOXEN parsen\nconst re = /<div class=\"BOX BOX_IN\">[\\s\\S]*?<div class=\"bez\">(\\d+):\\s*([^<]+)<\\/div>(?:[\\s\\S]*?<div class=\"val\">\\s*<div>([^<]*)<\\/div>\\s*<\\/div>)?/g;\n\nconst parsed = [];\nfor (const m of html.matchAll(re)) {\n const index = Number(m[1]);\n const label = m[2].trim();\n const raw = (m[3] ?? '').trim() || null;\n const { value, unit, state, on } = parseValue(raw);\n const key = `${String(index).padStart(2,'0')}_${toKey(label)}`;\n parsed.push({ index, label, key, raw, value, unit, state, on });\n}\n\n// 3) Kompaktes Objekt + Einheiten-Mapping bauen\nconst flat = {};\nconst units = {};\nfor (const p of parsed) {\n flat[p.key] = (p.value ?? p.state ?? p.raw ?? null);\n if (p.unit) units[p.key] = p.unit;\n}\n\n// 4) Ein einziges Ergebnis-Item zurückgeben\nreturn [{ json: { flat, units, items: parsed } }];\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-224,
288
],
"id": "509329e8-02f5-473f-a09c-f334bc441340",
"name": "transfer data to readable format"
},
{
"parameters": {
"url": "http://192.168.2.68:8093/v1/state/0_userdata.0.haus.heizung.pelletheizung.hot_water_temperature",
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "value",
"value": "={{ $('transfer data to readable format').item.json.flat['08_unbenutzt']"
}
]
},
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "value",
"value": "={{ $('transfer data to readable format').item.json.flat['08_unbenutzt'] }}"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
0,
384
],
"id": "ee3b68e3-7b85-41c8-9a2d-276e8bb8da17",
"name": "set Warmwasser Temperatur"
},
{
"parameters": {
"url": "http://192.168.2.68:8093/v1/state/0_userdata.0.haus.heizung.pelletheizung.heating_circuit_temperature",
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "value",
"value": "={{ $('transfer data to readable format').item.json.flat['03_unbenutzt'] }}"
}
]
},
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "value",
"value": "={{ $('transfer data to readable format').item.json.flat['03_unbenutzt'] }}"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
0,
576
],
"id": "5b95f25f-a896-46ef-a455-cb915a76b388",
"name": "set Heizkreis Temperatur"
},
{
"parameters": {
"url": "http://192.168.2.68:8093/v1/state/0_userdata.0.haus.heizung.pelletheizung.heat_pump_consumption",
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "value",
"value": "={{ $json.flat['16_Stromzahler_WP'] }}"
}
]
},
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "value",
"value": "={{ $json.flat['16_Stromzahler_WP'] }}"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
0,
192
],
"id": "52d65947-58f8-423d-a746-ba108d74dd06",
"name": "set Verbrauch Waermepumpe"
},
{
"parameters": {
"url": "http://192.168.2.68:8093/v1/state/0_userdata.0.haus.heizung.outer_temperature",
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "value",
"value": "={{ $json.flat['06_T_Aussen'] }}"
}
]
},
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "value",
"value": "={{ $json.flat['06_T_Aussen'] }}"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
0,
0
],
"id": "e895b3a4-620c-4623-9267-6cab2d353f50",
"name": "set outer temperature"
}
],
"pinData": {},
"connections": {
"Schedule Trigger": {
"main": [
[
{
"node": "get Data from CMI Interface local",
"type": "main",
"index": 0
}
]
]
},
"get Data from CMI Interface local": {
"main": [
[
{
"node": "transfer data to readable format",
"type": "main",
"index": 0
}
]
]
},
"transfer data to readable format": {
"main": [
[
{
"node": "set Warmwasser Temperatur",
"type": "main",
"index": 0
},
{
"node": "set Verbrauch Waermepumpe",
"type": "main",
"index": 0
},
{
"node": "set Heizkreis Temperatur",
"type": "main",
"index": 0
},
{
"node": "set outer temperature",
"type": "main",
"index": 0
}
]
]
}
},
"active": true,
"settings": {
"executionOrder": "v1",
"timeSavedMode": "fixed",
"callerPolicy": "workflowsFromSameOwner",
"availableInMCP": false
},
"versionId": "ff580cc5-a6c7-49be-8ac9-7a607d4537ed",
"meta": {
"instanceId": "3cc6c4256e2bbdec1b3708c49dc3889c7266357f3e239651c157c2de760e6c65"
},
"id": "ygJYiiFM97WiWeY5",
"tags": [
{
"updatedAt": "2025-12-20T20:03:34.739Z",
"createdAt": "2025-12-20T20:03:34.739Z",
"id": "i5rtQijMRj2sx5Zj",
"name": "Heizung"
},
{
"updatedAt": "2025-12-20T19:56:59.512Z",
"createdAt": "2025-12-20T19:56:59.512Z",
"id": "9G8OthZJpXo6BPbV",
"name": "Smart Home"
}
]
}

View File

@@ -0,0 +1,195 @@
{
"name": "Pelletheizung: Abfrage Kessel",
"nodes": [
{
"parameters": {
"url": "https://stokercloud.dk/v16bckbeta/dataout2/controllerdata2.php?screen=b1,0,b2,5,b3,4,b4,6,b5,12,b6,14,b7,15,b8,16,b9,9,b10,0,d1,3,d2,4,d3,0,d4,0,d5,0,d6,0,d7,0,d8,0,d9,0,d10,0,h1,2,h2,3,h3,4,h4,7,h5,8,h6,1,h7,5,h8,13,h9,0,h10,0,w1,2,w2,3,w3,9,w4,0,w5,0&token=958f4bcf6d8251da548e773176ca0925ff7840bdce7daeb8bcf72f00cf5201087fd760aeae882f66729695445feb8bbd55307f9279da03ae211ce9e4dd5229f7e950b3008574b55ea095001693fa7765fe09a1ab6553a3a8d8e70a4cb1916d43",
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
-224,
192
],
"id": "ae5635a9-5b88-485d-8d46-b20bee634452",
"name": "get Values from Stokercloud"
},
{
"parameters": {
"url": "http://192.168.2.68:8093/v1/state/0_userdata.0.haus.heizung.pelletheizung.boiler_temperature",
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "value",
"value": "={{ $json.frontdata[1].value }}"
}
]
},
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "value",
"value": "={{ $json.frontdata[1].value }}"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
0,
0
],
"id": "23782057-3c0d-4a49-b475-d74abcc4a459",
"name": "set boiler temperature"
},
{
"parameters": {
"url": "http://192.168.2.68:8093/v1/state/0_userdata.0.haus.heizung.pelletheizung.boiler_power",
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "value",
"value": "={{ $json.boilerdata[1].value }}"
}
]
},
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "value",
"value": "={{ $json.boilerdata[1].value }}"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
0,
192
],
"id": "eaffbad5-8bb2-484a-8cd5-34fca3c97f23",
"name": "set boiler power"
},
{
"parameters": {
"rule": {
"interval": [
{
"field": "minutes"
}
]
}
},
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [
-448,
192
],
"id": "6f6781a6-95fa-4ad7-991d-deb66d86be90",
"name": "Trigger 5 minutes"
},
{
"parameters": {
"url": "http://192.168.2.68:8093/v1/state/0_userdata.0.haus.heizung.pelletheizung.boiler_power_kw",
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "value",
"value": "={{ $json.boilerdata[0].value }}"
}
]
},
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "value",
"value": "={{ $json.boilerdata[0].value }}"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
0,
384
],
"id": "7826f720-ad10-487c-95b3-6b21a74c2284",
"name": "set boiler power kw"
}
],
"pinData": {},
"connections": {
"get Values from Stokercloud": {
"main": [
[
{
"node": "set boiler temperature",
"type": "main",
"index": 0
},
{
"node": "set boiler power",
"type": "main",
"index": 0
},
{
"node": "set boiler power kw",
"type": "main",
"index": 0
}
]
]
},
"Trigger 5 minutes": {
"main": [
[
{
"node": "get Values from Stokercloud",
"type": "main",
"index": 0
}
]
]
}
},
"active": true,
"settings": {
"executionOrder": "v1",
"timeSavedMode": "fixed",
"callerPolicy": "workflowsFromSameOwner",
"availableInMCP": false
},
"versionId": "4d76bfdb-5d2c-4eae-8d58-14d28cb18a7d",
"meta": {
"instanceId": "3cc6c4256e2bbdec1b3708c49dc3889c7266357f3e239651c157c2de760e6c65"
},
"id": "L5LZdEzKKQZWaHMd",
"tags": [
{
"updatedAt": "2025-12-20T20:03:34.739Z",
"createdAt": "2025-12-20T20:03:34.739Z",
"id": "i5rtQijMRj2sx5Zj",
"name": "Heizung"
},
{
"updatedAt": "2025-12-20T19:56:59.512Z",
"createdAt": "2025-12-20T19:56:59.512Z",
"id": "9G8OthZJpXo6BPbV",
"name": "Smart Home"
}
]
}

View File

@@ -0,0 +1,461 @@
{
"name": "Pelletheizung: Abfrage Verbrauch",
"nodes": [
{
"parameters": {
"url": "https://stokercloud.dk/v16bckbeta/dataout2/controllerdata2.php?screen=b1,0,b2,5,b3,4,b4,6,b5,12,b6,14,b7,15,b8,16,b9,9,b10,0,d1,3,d2,4,d3,0,d4,0,d5,0,d6,0,d7,0,d8,0,d9,0,d10,0,h1,2,h2,3,h3,4,h4,7,h5,8,h6,1,h7,5,h8,13,h9,0,h10,0,w1,2,w2,3,w3,9,w4,0,w5,0&token=958f4bcf6d8251da548e773176ca0925ff7840bdce7daeb8bcf72f00cf5201087fd760aeae882f66729695445feb8bbd55307f9279da03ae211ce9e4dd5229f7e950b3008574b55ea095001693fa7765fe09a1ab6553a3a8d8e70a4cb1916d43",
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
-224,
288
],
"id": "c09de7e8-760d-4716-a908-382d43c2258a",
"name": "HTTP Request"
},
{
"parameters": {
"chatId": "749279624",
"text": "=Der aktuelle Füllstand des Pelletsilos beträgt {{ $('HTTP Request').item.json.hopperdata[5].value }} kg. Der heutige Verbrauch liegt bei {{ $('HTTP Request').item.json.hopperdata[1].value }} kg.",
"additionalFields": {}
},
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
224,
576
],
"id": "953efaeb-f1e5-4515-b58a-46cbc6b72fbc",
"name": "Telegram",
"webhookId": "ddd87e24-21a8-4037-a549-8991f0c31d97",
"credentials": {
"telegramApi": {
"id": "EUy2hT71fSdnEr2w",
"name": "Telegram account"
}
},
"disabled": true
},
{
"parameters": {
"operation": "upsert",
"table": {
"__rl": true,
"value": "Pellets",
"mode": "list",
"cachedResultName": "Pellets"
},
"dataMode": "defineBelow",
"columnToMatchOn": "Datum",
"valueToMatchOn": "={{ $now.toFormat('yyyy-MM-dd')}}",
"valuesToSend": {
"values": [
{
"column": "Datum",
"value": "={{ $now.toFormat('yyyy-MM-dd')}}"
},
{
"column": "Fuellstand",
"value": "={{ $json.hopperdata[5].value }}"
},
{
"column": "Pellets Verbrauch [kg]",
"value": "={{ $json.hopperdata[1].value }}"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.mySql",
"typeVersion": 2.4,
"position": [
0,
576
],
"id": "25651cad-8917-4aff-a0e7-9aff0dd9352f",
"name": "MySQL",
"credentials": {
"mySql": {
"id": "GvOnFzLTFlbKtgUM",
"name": "MySQL account - homelab"
}
}
},
{
"parameters": {
"url": "http://192.168.2.68:8093/v1/state/0_userdata.0.haus.heizung.pelletheizung.pellet_fuellstand_silo",
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "value",
"value": "={{ $json.hopperdata[5].value }}"
}
]
},
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "value",
"value": "={{ $json.hopperdata[5].value }}"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
0,
384
],
"id": "d365485b-573f-4a41-b139-81d64160cd58",
"name": "schreibe Füllstand Silo"
},
{
"parameters": {
"url": "http://192.168.2.68:8093/v1/state/0_userdata.0.haus.heizung.pelletheizung.pellet_verbrauch_tag",
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "value",
"value": "={{ $json.hopperdata[1].value }}"
}
]
},
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "value",
"value": "={{ $json.hopperdata[5].value }}"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
0,
192
],
"id": "2111a02b-c534-4d47-85c2-0df590f91cb4",
"name": "schreibe Verbrauch 24h"
},
{
"parameters": {
"rule": {
"interval": [
{
"triggerAtHour": 23,
"triggerAtMinute": 59
}
]
}
},
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [
-448,
384
],
"id": "6aa9d692-e5c7-488e-a9cd-c9999852fa59",
"name": "Trigger 23:59"
},
{
"parameters": {
"rule": {
"interval": [
{
"field": "minutes",
"minutesInterval": 30
}
]
}
},
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [
-448,
192
],
"id": "9f19dd5b-d172-4668-9325-829f8278fdec",
"name": "Trigger 30 minutes"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose",
"version": 2
},
"conditions": [
{
"id": "b00d73a4-b3aa-496e-9730-fb3110944925",
"leftValue": "={{ $('HTTP Request').item.json.hopperdata[5].value }}",
"rightValue": 1000,
"operator": {
"type": "number",
"operation": "lt"
}
}
],
"combinator": "and"
},
"looseTypeValidation": true,
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
224,
384
],
"id": "a091b1e3-bad8-4e2a-9fa5-3892fa8eb3d9",
"name": "If1"
},
{
"parameters": {
"fromEmail": "pelletlager@scheidel.biz",
"toEmail": "michael.scheidel@scheidel.biz",
"subject": "=Es ist Zeit Pellets nachzubestellen. Stand: {{ $('HTTP Request').item.json.hopperdata[5].value }} kg",
"html": "=Der aktuelle Silostand liegt bei {{ $('HTTP Request').item.json.hopperdata[5].value }} kg. Es ist Zeit nachzubestllen",
"options": {}
},
"type": "n8n-nodes-base.emailSend",
"typeVersion": 2.1,
"position": [
448,
384
],
"id": "7f87bdc8-a659-401a-9d92-8751587aded2",
"name": "Send email",
"webhookId": "3ea799df-e2df-4291-a530-7534fd6edd34",
"credentials": {
"smtp": {
"id": "wgI3pOhkySCL5ngP",
"name": "SMTP account"
}
}
},
{
"parameters": {
"url": "http://192.168.2.68:8093/v1/state/0_userdata.0.haus.heizung.pelletheizung.connection_status",
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "value",
"value": "={{ $json.notconnected }}"
}
]
},
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "value",
"value": "={{ $json.notconnected }}"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
0,
0
],
"id": "3859cbb8-e79d-4852-8dec-735d725e5c8e",
"name": "schreibe connection_staus"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose",
"version": 2
},
"conditions": [
{
"id": "d887854d-7feb-480e-8adc-d8352db92ef2",
"leftValue": "={{ $json.val }}",
"rightValue": 0,
"operator": {
"type": "number",
"operation": "notEquals"
}
}
],
"combinator": "and"
},
"looseTypeValidation": true,
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
224,
0
],
"id": "4668bc29-c9f1-4820-980f-f0f999887da9",
"name": "If2"
},
{
"parameters": {
"chatId": "749279624",
"text": "=Die Netzverbindung zur Heizung ist unterbrochen!",
"additionalFields": {}
},
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
448,
0
],
"id": "741ca3bb-f78c-4027-ac72-4f9220b1bd91",
"name": "Telegram1",
"webhookId": "ddd87e24-21a8-4037-a549-8991f0c31d97",
"credentials": {
"telegramApi": {
"id": "EUy2hT71fSdnEr2w",
"name": "Telegram account"
}
}
}
],
"pinData": {},
"connections": {
"HTTP Request": {
"main": [
[
{
"node": "MySQL",
"type": "main",
"index": 0
},
{
"node": "schreibe Füllstand Silo",
"type": "main",
"index": 0
},
{
"node": "schreibe Verbrauch 24h",
"type": "main",
"index": 0
},
{
"node": "schreibe connection_staus",
"type": "main",
"index": 0
}
]
]
},
"MySQL": {
"main": [
[
{
"node": "Telegram",
"type": "main",
"index": 0
}
]
]
},
"schreibe Füllstand Silo": {
"main": [
[
{
"node": "If1",
"type": "main",
"index": 0
}
]
]
},
"Trigger 23:59": {
"main": [
[
{
"node": "HTTP Request",
"type": "main",
"index": 0
}
]
]
},
"Trigger 30 minutes": {
"main": [
[
{
"node": "HTTP Request",
"type": "main",
"index": 0
}
]
]
},
"If1": {
"main": [
[
{
"node": "Send email",
"type": "main",
"index": 0
}
]
]
},
"schreibe connection_staus": {
"main": [
[
{
"node": "If2",
"type": "main",
"index": 0
}
]
]
},
"If2": {
"main": [
[
{
"node": "Telegram1",
"type": "main",
"index": 0
}
]
]
}
},
"active": true,
"settings": {
"executionOrder": "v1"
},
"versionId": "ef221069-21a2-4c44-b925-b20fb23cf4a4",
"meta": {
"templateCredsSetupCompleted": true,
"instanceId": "3cc6c4256e2bbdec1b3708c49dc3889c7266357f3e239651c157c2de760e6c65"
},
"id": "t3q9pyWyzAdiHiyl",
"tags": [
{
"updatedAt": "2025-12-20T19:56:59.512Z",
"createdAt": "2025-12-20T19:56:59.512Z",
"id": "9G8OthZJpXo6BPbV",
"name": "Smart Home"
}
]
}

View File

@@ -0,0 +1,576 @@
{
"name": "Wärmepumpe: Tagesverbrauch",
"nodes": [
{
"parameters": {
"jsCode": "// INPUT: items = [{ json: { data: \"<div id='centerf'>...</div>\" } }]\n// OUTPUT: items = [{ json: { Adresse, Bezeichnung, Wert, Einheit, Waehrung, Raw, Key } }, ...]\n\n// kleine Helfer\nfunction slug(s) {\n return String(s)\n .normalize('NFD').replace(/[\\u0300-\\u036f]/g, '')\n .replace(/ä/g,'ae').replace(/ö/g,'oe').replace(/ü/g,'ue')\n .replace(/Ä/g,'Ae').replace(/Ö/g,'Oe').replace(/Ü/g,'Ue')\n .replace(/ß/g,'ss')\n .replace(/[^A-Za-z0-9]+/g,'_')\n .replace(/^_+|_+$/g,'')\n .toLowerCase();\n}\n\nfunction parseNumUnit(raw) {\n if (!raw) return { value: null, unit: null, currency: null };\n let t = raw.replace(/&nbsp;/g, ' ').trim();\n\n // Währung vorne oder hinten (€, $, £)\n let currency = null;\n const curLead = t.match(/^[€$£]/);\n const curTrail = t.match(/[€$£]$/);\n if (curLead) { currency = curLead[0]; t = t.slice(1).trim(); }\n else if (curTrail) { currency = curTrail[0]; t = t.slice(0, -1).trim(); }\n\n // Zahl (Komma oder Punkt)\n const numMatch = t.match(/-?\\d+(?:[.,]\\d+)?/);\n const value = numMatch ? parseFloat(numMatch[0].replace(',', '.')) : null;\n const unit = numMatch ? (t.replace(numMatch[0], '').trim() || null) : null;\n\n return { value, unit, currency };\n}\n\n// 1) HTML einsammeln (idR nur ein Item)\nconst html = items.map(i => i.json?.data ?? i.json?.body ?? i.json?.html ?? '').join('\\n');\n\n// 2) VALUE-Blöcke parsen\nconst re = /<div class=\"VALUE\">\\s*<div class=\"VALUE_LINK L2\"\\s+adresse=\"([^\"]+)\">([^<]+)<\\/div>\\s*<div class=\"VALUE_B\">([^<]+)<\\/div>\\s*<\\/div>/g;\n\nconst rows = [];\nfor (const m of html.matchAll(re)) {\n const adresse = m[1].trim();\n const label = m[2].trim();\n const raw = m[3].trim();\n\n const { value, unit, currency } = parseNumUnit(raw);\n const key = slug(label);\n\n rows.push({\n Adresse: adresse,\n Bezeichnung: label,\n Wert: (value !== null ? value : raw), // Zahl bevorzugt, sonst Rohstring\n Einheit: unit || '',\n Waehrung: currency || '',\n Raw: raw,\n Key: key\n });\n}\n\n// 3) Als **einzelne Items** zurückgeben (ein Item pro Wert/Zeile)\nreturn rows.map(r => ({ json: r }));\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
224,
0
],
"id": "05b8699b-62c3-4f52-9829-03ebba00f470",
"name": "Code in JavaScript"
},
{
"parameters": {
"jsCode": "// INPUT: 19 Items vom Parser (Adresse, Bezeichnung, Wert, Einheit, Waehrung, Key)\n// OUTPUT: 1 Item mit vielen Feldern: leistung, tageszaehlerstand, ...\n\nconst out = {};\nconst units = {}; // falls du Einheiten brauchst\n\nfor (const it of items) {\n const j = it.json;\n // Zahl bevorzugt, sonst Rohstring\n out[j.Key] = (j.Wert ?? j.Raw ?? null);\n if (j.Einheit) units[j.Key] = j.Einheit;\n if (j.Waehrung) units[j.Key] = (units[j.Key] ? units[j.Key] + ' ' : '') + j.Waehrung;\n}\n\n// Ein einziges Item zurückgeben\nreturn [{ json: { ...out, _units: units } }];\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
448,
0
],
"id": "61d9d196-8224-4363-b46c-2186979f9d4e",
"name": "Code in JavaScript1"
},
{
"parameters": {
"rule": {
"interval": [
{
"field": "minutes"
}
]
}
},
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [
-224,
96
],
"id": "8c6654b8-560d-4126-96d4-372d2567e8e0",
"name": "Schedule Trigger"
},
{
"parameters": {
"url": "http://192.168.2.68:8093/v1/state/0_userdata.0.haus.heizung.waermepumpe.heat_pump_daily_consumption",
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "value",
"value": "={{ $('Code in JavaScript1').item.json.tageszahlerstand }}"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
896,
0
],
"id": "ab729e47-cce9-4cf0-a5b3-56c8e5c1def3",
"name": "update Tageszählerstand iobroker"
},
{
"parameters": {
"url": "http://192.168.2.186/INCLUDE/devpagex.cgi?pagex2=01055c07&_=1759570693948",
"authentication": "genericCredentialType",
"genericAuthType": "httpBasicAuth",
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "admin",
"value": "admin"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
0,
0
],
"id": "59028407-fb4b-4ee0-a539-3505cf2fffd3",
"name": "get Tageszählerstand WP",
"credentials": {
"httpBasicAuth": {
"id": "iJkXePPAceSyENTx",
"name": "CMI CAN Bus local"
}
}
},
{
"parameters": {
"url": "http://192.168.2.186/INCLUDE/devpagex.cgi?pagex2=01145c07&_=1759576852400",
"authentication": "genericCredentialType",
"genericAuthType": "httpBasicAuth",
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "admin",
"value": "admin"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
0,
192
],
"id": "545bac82-ea34-461d-955e-9ceebe4dc82a",
"name": "get Tageszählerstand PV WP",
"credentials": {
"httpBasicAuth": {
"id": "iJkXePPAceSyENTx",
"name": "CMI CAN Bus local"
}
}
},
{
"parameters": {
"jsCode": "// INPUT: items = [{ json: { data: \"<div id='centerf'>...</div>\" } }]\n// OUTPUT: items = [{ json: { Adresse, Bezeichnung, Wert, Einheit, Waehrung, Raw, Key } }, ...]\n\n// kleine Helfer\nfunction slug(s) {\n return String(s)\n .normalize('NFD').replace(/[\\u0300-\\u036f]/g, '')\n .replace(/ä/g,'ae').replace(/ö/g,'oe').replace(/ü/g,'ue')\n .replace(/Ä/g,'Ae').replace(/Ö/g,'Oe').replace(/Ü/g,'Ue')\n .replace(/ß/g,'ss')\n .replace(/[^A-Za-z0-9]+/g,'_')\n .replace(/^_+|_+$/g,'')\n .toLowerCase();\n}\n\nfunction parseNumUnit(raw) {\n if (!raw) return { value: null, unit: null, currency: null };\n let t = raw.replace(/&nbsp;/g, ' ').trim();\n\n // Währung vorne oder hinten (€, $, £)\n let currency = null;\n const curLead = t.match(/^[€$£]/);\n const curTrail = t.match(/[€$£]$/);\n if (curLead) { currency = curLead[0]; t = t.slice(1).trim(); }\n else if (curTrail) { currency = curTrail[0]; t = t.slice(0, -1).trim(); }\n\n // Zahl (Komma oder Punkt)\n const numMatch = t.match(/-?\\d+(?:[.,]\\d+)?/);\n const value = numMatch ? parseFloat(numMatch[0].replace(',', '.')) : null;\n const unit = numMatch ? (t.replace(numMatch[0], '').trim() || null) : null;\n\n return { value, unit, currency };\n}\n\n// 1) HTML einsammeln (idR nur ein Item)\nconst html = items.map(i => i.json?.data ?? i.json?.body ?? i.json?.html ?? '').join('\\n');\n\n// 2) VALUE-Blöcke parsen\nconst re = /<div class=\"VALUE\">\\s*<div class=\"VALUE_LINK L2\"\\s+adresse=\"([^\"]+)\">([^<]+)<\\/div>\\s*<div class=\"VALUE_B\">([^<]+)<\\/div>\\s*<\\/div>/g;\n\nconst rows = [];\nfor (const m of html.matchAll(re)) {\n const adresse = m[1].trim();\n const label = m[2].trim();\n const raw = m[3].trim();\n\n const { value, unit, currency } = parseNumUnit(raw);\n const key = slug(label);\n\n rows.push({\n Adresse: adresse,\n Bezeichnung: label,\n Wert: (value !== null ? value : raw), // Zahl bevorzugt, sonst Rohstring\n Einheit: unit || '',\n Waehrung: currency || '',\n Raw: raw,\n Key: key\n });\n}\n\n// 3) Als **einzelne Items** zurückgeben (ein Item pro Wert/Zeile)\nreturn rows.map(r => ({ json: r }));\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
224,
192
],
"id": "8f08ee8b-d3eb-4d7e-baa7-6fad114e40b0",
"name": "Code in JavaScript2"
},
{
"parameters": {
"jsCode": "// INPUT: 19 Items vom Parser (Adresse, Bezeichnung, Wert, Einheit, Waehrung, Key)\n// OUTPUT: 1 Item mit vielen Feldern: leistung, tageszaehlerstand, ...\n\nconst out = {};\nconst units = {}; // falls du Einheiten brauchst\n\nfor (const it of items) {\n const j = it.json;\n // Zahl bevorzugt, sonst Rohstring\n out[j.Key] = (j.Wert ?? j.Raw ?? null);\n if (j.Einheit) units[j.Key] = j.Einheit;\n if (j.Waehrung) units[j.Key] = (units[j.Key] ? units[j.Key] + ' ' : '') + j.Waehrung;\n}\n\n// Ein einziges Item zurückgeben\nreturn [{ json: { ...out, _units: units } }];\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
448,
192
],
"id": "d1a4e45e-854b-4359-8da0-3e85f0c41dc4",
"name": "Code in JavaScript3"
},
{
"parameters": {
"operation": "upsert",
"table": {
"__rl": true,
"value": "Waermepumpe",
"mode": "list",
"cachedResultName": "Waermepumpe"
},
"dataMode": "defineBelow",
"columnToMatchOn": "Datum",
"valueToMatchOn": "={{ $now.toFormat('yyyy-MM-dd')}}",
"valuesToSend": {
"values": [
{
"column": "Tagesverbrauch",
"value": "={{ $json.tageszahlerstand }}"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.mySql",
"typeVersion": 2.5,
"position": [
672,
0
],
"id": "797700c3-040b-4a27-b621-68e806b65730",
"name": "update Tagesverbrauch mysql",
"credentials": {
"mySql": {
"id": "GvOnFzLTFlbKtgUM",
"name": "MySQL account - homelab"
}
}
},
{
"parameters": {
"operation": "upsert",
"table": {
"__rl": true,
"value": "Waermepumpe",
"mode": "list",
"cachedResultName": "Waermepumpe"
},
"dataMode": "defineBelow",
"columnToMatchOn": "Datum",
"valueToMatchOn": "={{ $now.toFormat('yyyy-MM-dd')}}",
"valuesToSend": {
"values": [
{
"column": "Anteil PV",
"value": "={{ $json.tageszahlerstand }}"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.mySql",
"typeVersion": 2.5,
"position": [
672,
192
],
"id": "7546d115-42c1-4cf9-ae9c-9030a86f9a7c",
"name": "update Tagesverbrauch PV mysql",
"credentials": {
"mySql": {
"id": "GvOnFzLTFlbKtgUM",
"name": "MySQL account - homelab"
}
}
},
{
"parameters": {
"operation": "select",
"table": {
"__rl": true,
"value": "Waermepumpe",
"mode": "list",
"cachedResultName": "Waermepumpe"
},
"where": {
"values": [
{
"column": "Datum",
"value": "={{ $now.toFormat('yyyy-MM-dd')}}"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.mySql",
"typeVersion": 2.5,
"position": [
1344,
96
],
"id": "6c0fce95-29f5-4c9a-bd06-6ce474207b01",
"name": "Select rows from a table",
"credentials": {
"mySql": {
"id": "GvOnFzLTFlbKtgUM",
"name": "MySQL account - homelab"
}
}
},
{
"parameters": {
"operation": "update",
"table": {
"__rl": true,
"value": "Waermepumpe",
"mode": "list",
"cachedResultName": "Waermepumpe"
},
"dataMode": "defineBelow",
"columnToMatchOn": "Datum",
"valueToMatchOn": "={{ $now.toFormat('yyyy-MM-dd')}}",
"valuesToSend": {
"values": [
{
"column": "Anteil Netzbezug",
"value": "={{ $json['Anteil Netzbezug'] }}"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.mySql",
"typeVersion": 2.5,
"position": [
1792,
96
],
"id": "b0e2962f-490d-4f17-be1c-0f8bbe80d510",
"name": "Update rows in a table",
"credentials": {
"mySql": {
"id": "GvOnFzLTFlbKtgUM",
"name": "MySQL account - homelab"
}
}
},
{
"parameters": {
"mode": "chooseBranch",
"useDataOfInput": 2
},
"type": "n8n-nodes-base.merge",
"typeVersion": 3.2,
"position": [
1120,
96
],
"id": "e3aedba7-b520-40c7-b0ac-80c231bafbd8",
"name": "Merge"
},
{
"parameters": {
"jsCode": "// Verarbeite alle eingehenden Items\nreturn $input.all().map(item => {\n const tagesverbrauch = Number(item.json[\"Tagesverbrauch\"]) || 0;\n const anteilPV = Number(item.json[\"Anteil PV\"]) || 0;\n\n // Berechnung\n const anteilNetzbezug = tagesverbrauch - anteilPV;\n\n // Neues Feld hinzufügen\n item.json[\"Anteil Netzbezug\"] = anteilNetzbezug;\n\n return item;\n});\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1568,
96
],
"id": "b709fffd-4cf1-46ae-839e-fb72a4902b0b",
"name": "calculate Netzbezug"
},
{
"parameters": {
"url": "http://192.168.2.68:8093/v1/state/0_userdata.0.haus.heizung.waermepumpe.heat_pump_daily_consumption_pv",
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "value",
"value": "={{ $('Code in JavaScript3').item.json.tageszahlerstand }}"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
896,
192
],
"id": "a0db8fee-4d48-49b0-a329-80c90783e530",
"name": "update Tagesverbrauch PV"
},
{
"parameters": {
"url": "http://192.168.2.68:8093/v1/state/0_userdata.0.haus.heizung.waermepumpe.heat_pump_daily_consumption_power",
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "value",
"value": "={{ $('calculate Netzbezug').item.json['Anteil Netzbezug'] }}"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
2016,
96
],
"id": "6dcc484c-e75a-46b2-9049-87a4e48d2d1d",
"name": "update Netzbezug"
}
],
"pinData": {},
"connections": {
"Code in JavaScript": {
"main": [
[
{
"node": "Code in JavaScript1",
"type": "main",
"index": 0
}
]
]
},
"Code in JavaScript1": {
"main": [
[
{
"node": "update Tagesverbrauch mysql",
"type": "main",
"index": 0
}
]
]
},
"Schedule Trigger": {
"main": [
[
{
"node": "get Tageszählerstand WP",
"type": "main",
"index": 0
},
{
"node": "get Tageszählerstand PV WP",
"type": "main",
"index": 0
}
]
]
},
"get Tageszählerstand WP": {
"main": [
[
{
"node": "Code in JavaScript",
"type": "main",
"index": 0
}
]
]
},
"get Tageszählerstand PV WP": {
"main": [
[
{
"node": "Code in JavaScript2",
"type": "main",
"index": 0
}
]
]
},
"Code in JavaScript2": {
"main": [
[
{
"node": "Code in JavaScript3",
"type": "main",
"index": 0
}
]
]
},
"Code in JavaScript3": {
"main": [
[
{
"node": "update Tagesverbrauch PV mysql",
"type": "main",
"index": 0
}
]
]
},
"update Tagesverbrauch mysql": {
"main": [
[
{
"node": "update Tageszählerstand iobroker",
"type": "main",
"index": 0
}
]
]
},
"Select rows from a table": {
"main": [
[
{
"node": "calculate Netzbezug",
"type": "main",
"index": 0
}
]
]
},
"Update rows in a table": {
"main": [
[
{
"node": "update Netzbezug",
"type": "main",
"index": 0
}
]
]
},
"update Tageszählerstand iobroker": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 0
}
]
]
},
"update Tagesverbrauch PV mysql": {
"main": [
[
{
"node": "update Tagesverbrauch PV",
"type": "main",
"index": 0
}
]
]
},
"Merge": {
"main": [
[
{
"node": "Select rows from a table",
"type": "main",
"index": 0
}
]
]
},
"calculate Netzbezug": {
"main": [
[
{
"node": "Update rows in a table",
"type": "main",
"index": 0
}
]
]
},
"update Tagesverbrauch PV": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 1
}
]
]
}
},
"active": true,
"settings": {
"executionOrder": "v1"
},
"versionId": "8b467f30-717c-41e3-9051-236e31116740",
"meta": {
"templateCredsSetupCompleted": true,
"instanceId": "3cc6c4256e2bbdec1b3708c49dc3889c7266357f3e239651c157c2de760e6c65"
},
"id": "bKlJd68Ba0ZWBnNS",
"tags": [
{
"updatedAt": "2025-12-20T20:03:34.739Z",
"createdAt": "2025-12-20T20:03:34.739Z",
"id": "i5rtQijMRj2sx5Zj",
"name": "Heizung"
},
{
"updatedAt": "2025-12-20T19:56:59.512Z",
"createdAt": "2025-12-20T19:56:59.512Z",
"id": "9G8OthZJpXo6BPbV",
"name": "Smart Home"
}
]
}