10 Commits
Author SHA1 Message Date
raph666 c5d7f17ee9 Update arkteos_reg3_nodered.json
Les trois fonctions (Discovery, Parse energy-overview, Parse items) utilisent maintenant node.send() au lieu de return []
2026-04-01 20:03:10 +02:00
raph666 299ce57970 Update arkteos_reg3_nodered.json 2026-04-01 13:31:15 +02:00
raph666 2906a39cb1 Update README.md 2026-04-01 11:02:30 +02:00
raph666 2d9d89894b Update README.md 2026-04-01 11:00:05 +02:00
raph666 f06eb4979e Update arkteos_reg3_nodered.json 2026-04-01 10:59:46 +02:00
raph666 11d684cb2e Update arkteos_reg3_nodered.json 2026-04-01 10:32:43 +02:00
raph666 0b8094c20c Update arkteos_reg3_nodered.json 2026-04-01 10:11:34 +02:00
raph666 7cef86160c Update README.md 2026-04-01 10:10:53 +02:00
raph666 b490b145c6 Update README.md 2026-03-31 19:00:28 +02:00
raph666 dd571aaa5f Update arkteos_reg3_nodered.json
add throttling
2026-03-31 18:57:56 +02:00
2 changed files with 588 additions and 172 deletions

No files matched your search

+96 -5
View File
@@ -10,8 +10,8 @@ Permet les connexions simultanées de Node-RED et de l'application mobile Arkteo
2. Ajoute l'URL de ce repo : `https://github.com/raph666/arkteos-proxy-addon`
3. Installe l'addon **Arkteos Proxy**
4. Configure l'IP de ta PAC dans les options
5. Configure le port exposé dans la section **Network** (défaut : 9641)
6. Clique **Save** dans Network puis démarre l'addon
5. Configure le port exposé dans la section **Network** (défaut : 9641) et clique **Save**
6. Démarre l'addon
## Configuration
@@ -32,25 +32,116 @@ Un flow Node-RED prêt à l'emploi est disponible dans ce repo : `nodered/arkteo
3. Double-clique sur le nœud **MQTT publish** → icône crayon → onglet **Security** → renseigne le login et mot de passe Mosquitto
4. Clique **Deploy**
### Fonctionnement
Le flow se connecte en streaming permanent au proxy. Chaque trame reçue est parsée, filtrée puis publiée sur MQTT.
**Filtrage des valeurs aberrantes**
Les valeurs hors plage physiquement plausible sont ignorées et loguées dans la console Node-RED :
| Valeur | Plage valide |
|---|---|
| Température extérieure | -50°C à 150°C |
| Températures circuit primaire | -10°C à 90°C |
| Consigne départ eau primaire | -10°C à 90°C |
| Températures ECS | 0°C à 95°C |
| Consigne ECS | 0°C à 80°C |
| Température intérieure zone 1 | -10°C à 50°C |
| Consigne zone 1 | 5°C à 35°C |
| Puissances | 0 à 50 000 W |
| Pression eau primaire | 0 à 10 bar |
| Fréquence compresseur actuelle | 0 à 200 Hz |
| Fréquence compresseur cible | 0 à 200 Hz |
| Vitesse ventilateur | 0 à 3 000 rpm |
| Voltage DC | 0 à 1 000 V |
| Débit eau primaire | 0 à 10 000 L/h |
| Circulateur primaire | 0% à 100% |
| Statut frigo | 0 à 3 |
| Statut PAC | 0 à 9 |
| Signal RF sonde | -128 à 127 dBm |
| Nombre de dégivrages | 0 à 99 999 |
| Temps fonctionnement compresseur | 0 à 999 999 h |
| Nb cycles compresseur | 0 à 9 999 999 |
| Temps mise sous tension | 0 à 999 999 h |
**Throttling**
Une valeur n'est publiée que si elle a changé au-delà d'un seuil défini, avec un rafraîchissement forcé à intervalle maximum :
| Valeur | Seuil | Intervalle max |
|---|---|---|
| Température extérieure | 0.5°C | 60s |
| Températures circuit primaire | 0.5°C | 60s |
| Consigne départ eau primaire | 0.5°C | 60s |
| Températures ECS | 0.5°C | 60s |
| Consigne ECS | 0.5°C | 60s |
| Température intérieure zone 1 | 0.5°C | 60s |
| Consigne zone 1 | 0.5°C | 60s |
| Puissance produite | 10 W | 30s |
| Puissance consommée | 10 W | 30s |
| Pression eau primaire | 0.1 bar | 60s |
| Fréquence compresseur actuelle | 1 Hz | 30s |
| Fréquence compresseur cible | 1 Hz | 30s |
| Vitesse ventilateur | 10 rpm | 30s |
| Voltage DC | 5 V | 60s |
| Débit eau primaire | 1 L/h | 60s |
| Circulateur primaire | 1% | 60s |
| Modèle PAC | changement | 1h |
| Statut frigo | changement | 60s |
| Statut PAC | changement | 60s |
| Erreurs | changement | 60s |
| Nombre de dégivrages | 1 | 5 min |
| Nb cycles compresseur | 100 | 5 min |
| Temps fonctionnement compresseur | 1 h | 5 min |
| Temps mise sous tension | 1 h | 5 min |
| Signal RF sonde | 1 dBm | 5 min |
**Statut de connexion**
Un `binary_sensor` HA indique si la PAC est connectée. Il passe à `OFF` si aucune trame n'est reçue depuis plus de 30 secondes.
### Entités créées dans HA
Le flow publie automatiquement 12 entités via MQTT Discovery dans un appareil **PAC Arkteos Zuran 4** :
Le flow publie automatiquement via MQTT Discovery dans un appareil **PAC Arkteos Zuran 4** :
**Statut**
- PAC connectée (binary_sensor)
**Groupe frigorifique**
- Température extérieure (°C)
- Nombre de dégivrages
- Temps fonctionnement compresseur (h)
- Nombre de cycles compresseur
- Fréquence compresseur actuelle (Hz)
- Fréquence compresseur cible (Hz)
- Vitesse ventilateur groupe frigo (rpm)
- Voltage DC compresseur (V)
- Statut frigo (0-3)
- Statut frigo texte (Arret / Refroidissement / Chauffage / Degivrage)
- Erreur active frigo
**Régulation**
- Puissance instantanée produite (kW)
- Puissance instantanée consommée (kW)
- Puissance instantanée produite (W)
- Puissance instantanée consommée (W)
- Temps mise sous tension (h)
- Modèle PAC (texte)
- Consigne départ eau primaire (°C)
- Température eau primaire aller (°C)
- Température eau primaire retour (°C)
- Débit eau primaire (L/h)
- Pression eau primaire (bar)
- Circulateur primaire (%)
- Température intérieure zone 1 (°C)
- Consigne température zone 1 (°C)
- Température ballon ECS milieu (°C)
- Température ballon ECS bas (°C)
- Consigne ECS (°C)
- Cycles compresseur (régulation)
- Statut PAC (0-9)
- Statut PAC texte (Arret / Attente / Chaud / Froid / Hors Gel / ECS / Piscine / etc.)
- Erreur active régulation
- Signal RF sonde zone 1 (dBm)
## Protocole
+492 -167
View File
@@ -1,248 +1,573 @@
[
{
"id": "arkteos_flow",
"id": "4587847568fd2353",
"type": "tab",
"label": "Arkteos REG3",
"label": "Solarwatt Local v6 MQTT",
"disabled": false,
"info": "Intégration PAC Arkteos Zuran 4 via addon proxy.\nStreaming permanent sur <IP_HA>:9641.\nPublication MQTT avec discovery automatique HA.\nTrames 163 bytes (frigo), 227 bytes (régulation).\nRemplacer <IP_HA> par l'IP de Home Assistant dans le nœud PAC via proxy."
"info": "Migration v5→v6 : remplacement des noeuds ha-sensor Companion par MQTT Discovery. Les entity_id HA sont identiques à la v5."
},
{
"id": "arkteos_mqtt_broker",
"id": "b2f1d497c83e5a12",
"type": "mqtt-broker",
"name": "Mosquitto local",
"name": "Mosquitto local Solarwatt",
"broker": "core-mosquitto",
"port": "1883",
"clientid": "nodered_arkteos",
"clientid": "nodered_solarwatt",
"autoConnect": true,
"usetls": false,
"compatmode": false,
"protocolVersion": 4,
"keepalive": "60",
"cleansession": true,
"birthTopic": "",
"birthQos": "0",
"birthPayload": "",
"autoUnsubscribe": true,
"birthTopic": "solarwatt/status",
"birthQos": "1",
"birthPayload": "online",
"birthMsg": {},
"closeTopic": "",
"closeQos": "0",
"closePayload": "",
"willTopic": "",
"willQos": "0",
"willPayload": "",
"credentials": {
"user": "<MQTT_USER>",
"password": "<MQTT_PASSWORD>"
}
"closeMsg": {},
"willTopic": "solarwatt/status",
"willQos": "1",
"willPayload": "offline",
"willMsg": {},
"userProps": "",
"sessionExpiry": "",
"willRetain": true,
"birthRetain": true
},
{
"id": "arkteos_tcp_in",
"type": "tcp in",
"z": "arkteos_flow",
"name": "PAC via proxy",
"server": "<IP_HA>",
"port": "9641",
"datamode": "stream",
"datatype": "buffer",
"newline": "",
"id": "770612abc2ebe36d",
"type": "inject",
"z": "4587847568fd2353",
"name": "Login au démarrage",
"props": [],
"repeat": "",
"crontab": "",
"once": true,
"onceDelay": "3",
"topic": "",
"trim": false,
"base64": false,
"tls": "",
"x": 160,
"y": 100,
"wires": [["arkteos_parse_frame"]]
},
{
"id": "arkteos_parse_frame",
"type": "function",
"z": "arkteos_flow",
"name": "Parse frame",
"func": "const buf = msg.payload;\nif (!Buffer.isBuffer(buf) || buf.length === 0) return null;\n\nconst size = buf.length;\n\nfunction read16s(b, i) {\n const v = b[i] + b[i + 1] * 256;\n return v >= 32768 ? v - 65536 : v;\n}\nfunction read16u(b, i) {\n return b[i] + b[i + 1] * 256;\n}\nfunction read8(b, i) {\n return b[i];\n}\n\nif (size === 163) {\n msg.payload = {\n frame_type: 'frigo',\n exterieur_temp: read16s(buf, 24) / 10,\n freq_comp_actuelle: read16u(buf, 52),\n dc_voltage: read16u(buf, 62)\n };\n return msg;\n}\n\nif (size === 227) {\n msg.payload = {\n frame_type: 'regulation',\n puissance_inst_produite: read16u(buf, 16) / 10,\n puissance_inst_consommee: read16u(buf, 18) / 10,\n primaire_temp_eau_aller: read16s(buf, 54) / 10,\n primaire_temp_eau_retour: read16s(buf, 56) / 10,\n primaire_pression: read8(buf, 62) / 10,\n zone1_temp_interieur: read16s(buf, 68) / 10,\n zone1_consigne: read16s(buf, 70) / 10,\n ecs_temp_eau_milieu: read16s(buf, 108) / 10,\n ecs_temp_eau_bas: read16s(buf, 110) / 10\n };\n return msg;\n}\n\nreturn null;\n",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 380,
"y": 100,
"wires": [["arkteos_route"]]
},
{
"id": "arkteos_route",
"type": "switch",
"z": "arkteos_flow",
"name": "Route par type",
"property": "payload.frame_type",
"propertyType": "msg",
"rules": [
{ "t": "eq", "v": "frigo", "vt": "str" },
{ "t": "eq", "v": "regulation", "vt": "str" }
],
"checkall": "false",
"repair": false,
"outputs": 2,
"x": 580,
"y": 100,
"x": 140,
"y": 80,
"wires": [
["arkteos_debug_frigo_parsed", "arkteos_publish_frigo"],
["arkteos_debug_regulation_parsed", "arkteos_publish_regulation"]
[
"8ffb0174365ed9be"
]
]
},
{
"id": "arkteos_debug_frigo_parsed",
"id": "e2eb572a08473c56",
"type": "inject",
"z": "4587847568fd2353",
"name": "Refresh login 30 min",
"props": [],
"repeat": "1800",
"crontab": "",
"once": false,
"onceDelay": "0",
"topic": "",
"x": 140,
"y": 120,
"wires": [
[
"8ffb0174365ed9be"
]
]
},
{
"id": "8ffb0174365ed9be",
"type": "exec",
"z": "4587847568fd2353",
"command": "curl -s -c /tmp/sw.cookie -A \"Mozilla/5.0\" http://192.168.1.110/logon.html -o /dev/null",
"addpay": false,
"append": "",
"useSpawn": "false",
"timer": "",
"winHide": false,
"oldrc": false,
"name": "GET /logon.html",
"x": 390,
"y": 100,
"wires": [
[
"e5e188a8e9086896"
],
[
"c4cc578945748c64"
],
[
"fbe3f52862ebaca5"
]
]
},
{
"id": "e5e188a8e9086896",
"type": "exec",
"z": "4587847568fd2353",
"command": "curl -s -L -b /tmp/sw.cookie -c /tmp/sw.cookie -H \"Content-Type: application/x-www-form-urlencoded\" -H \"Referer: http://192.168.1.110/logon.html\" --data \"username=installer&url=%2F&password=hvpiimld&submit=Login\" http://192.168.1.110/auth/login -o /dev/null",
"addpay": false,
"append": "",
"useSpawn": "false",
"timer": "",
"winHide": false,
"oldrc": false,
"name": "POST /auth/login",
"x": 620,
"y": 100,
"wires": [
[
"3c00344a6efd7589"
],
[
"79a409c5565c37b2"
],
[
"bfb5ac533e8d5120"
]
]
},
{
"id": "3c00344a6efd7589",
"type": "exec",
"z": "4587847568fd2353",
"command": "cat /tmp/sw.cookie",
"addpay": false,
"append": "",
"useSpawn": "false",
"timer": "",
"winHide": false,
"oldrc": false,
"name": "Voir cookie",
"x": 840,
"y": 100,
"wires": [
[
"a2b17344e43630eb"
],
[],
[]
]
},
{
"id": "a2b17344e43630eb",
"type": "debug",
"z": "arkteos_flow",
"name": "Frigo parsé",
"active": true,
"z": "4587847568fd2353",
"name": "DEBUG cookie file",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 810,
"x": 1040,
"y": 100,
"wires": []
},
{
"id": "c4cc578945748c64",
"type": "debug",
"z": "4587847568fd2353",
"name": "GET logon stderr",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"x": 390,
"y": 40,
"wires": []
},
{
"id": "arkteos_debug_regulation_parsed",
"id": "fbe3f52862ebaca5",
"type": "debug",
"z": "arkteos_flow",
"name": "Regulation parsée",
"active": true,
"z": "4587847568fd2353",
"name": "GET logon rc",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 830,
"x": 380,
"y": 160,
"wires": []
},
{
"id": "arkteos_publish_frigo",
"type": "function",
"z": "arkteos_flow",
"name": "Publish frigo",
"func": "const data = msg.payload;\nconst msgs = [];\n\nconst sensors = [\n { topic: 'arkteos/frigo/exterieur_temp', value: data.exterieur_temp },\n { topic: 'arkteos/frigo/freq_comp_actuelle', value: data.freq_comp_actuelle },\n { topic: 'arkteos/frigo/dc_voltage', value: data.dc_voltage }\n];\n\nfor (const s of sensors) {\n msgs.push({ topic: s.topic, payload: String(s.value), qos: 0, retain: true });\n}\n\nreturn [msgs];\n",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 800,
"y": 80,
"wires": [["arkteos_debug_frigo_published", "arkteos_mqtt_out"]]
},
{
"id": "arkteos_publish_regulation",
"type": "function",
"z": "arkteos_flow",
"name": "Publish regulation",
"func": "const data = msg.payload;\nconst msgs = [];\n\nconst sensors = [\n { topic: 'arkteos/regulation/puissance_inst_produite', value: data.puissance_inst_produite },\n { topic: 'arkteos/regulation/puissance_inst_consommee', value: data.puissance_inst_consommee },\n { topic: 'arkteos/regulation/primaire_temp_eau_aller', value: data.primaire_temp_eau_aller },\n { topic: 'arkteos/regulation/primaire_temp_eau_retour', value: data.primaire_temp_eau_retour },\n { topic: 'arkteos/regulation/primaire_pression', value: data.primaire_pression },\n { topic: 'arkteos/regulation/zone1_temp_interieur', value: data.zone1_temp_interieur },\n { topic: 'arkteos/regulation/zone1_consigne', value: data.zone1_consigne },\n { topic: 'arkteos/regulation/ecs_temp_eau_milieu', value: data.ecs_temp_eau_milieu },\n { topic: 'arkteos/regulation/ecs_temp_eau_bas', value: data.ecs_temp_eau_bas }\n];\n\nfor (const s of sensors) {\n msgs.push({ topic: s.topic, payload: String(s.value), qos: 0, retain: true });\n}\n\nreturn [msgs];\n",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 800,
"y": 200,
"wires": [["arkteos_debug_regulation_published", "arkteos_mqtt_out"]]
},
{
"id": "arkteos_debug_frigo_published",
"id": "79a409c5565c37b2",
"type": "debug",
"z": "arkteos_flow",
"name": "Frigo MQTT publié",
"z": "4587847568fd2353",
"name": "POST login stderr",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 1040,
"x": 630,
"y": 40,
"wires": []
},
{
"id": "arkteos_debug_regulation_published",
"id": "bfb5ac533e8d5120",
"type": "debug",
"z": "arkteos_flow",
"name": "Regulation MQTT publiée",
"z": "4587847568fd2353",
"name": "POST login rc",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 1060,
"y": 240,
"x": 620,
"y": 160,
"wires": []
},
{
"id": "arkteos_mqtt_out",
"type": "mqtt out",
"z": "arkteos_flow",
"name": "MQTT publish",
"topic": "",
"qos": "0",
"retain": "true",
"broker": "arkteos_mqtt_broker",
"x": 1060,
"y": 140,
"wires": []
},
{
"id": "arkteos_discovery_inject",
"id": "70ad92976ba19345",
"type": "inject",
"z": "arkteos_flow",
"name": "Send discovery (1x au démarrage)",
"props": [{ "p": "payload" }],
"repeat": "",
"z": "4587847568fd2353",
"name": "Poll energy 15s",
"props": [],
"repeat": "15",
"crontab": "",
"once": true,
"onceDelay": 3,
"onceDelay": "8",
"topic": "",
"payload": "",
"payloadType": "date",
"x": 200,
"y": 360,
"wires": [["arkteos_discovery"]]
"x": 120,
"y": 1100,
"wires": [
[
"236adc97608b73f6"
]
]
},
{
"id": "arkteos_discovery",
"type": "function",
"z": "arkteos_flow",
"name": "MQTT Discovery",
"func": "const device = {\n identifiers: ['arkteos_zuran4'],\n name: 'PAC Arkteos Zuran 4',\n model: 'Zuran 4',\n manufacturer: 'Arkteos'\n};\n\nconst sensors = [\n {\n unique_id: 'arkteos_exterieur_temp',\n name: 'Température extérieure',\n state_topic: 'arkteos/frigo/exterieur_temp',\n unit_of_measurement: '°C',\n device_class: 'temperature',\n state_class: 'measurement'\n },\n {\n unique_id: 'arkteos_freq_comp_actuelle',\n name: 'Fréquence compresseur actuelle',\n state_topic: 'arkteos/frigo/freq_comp_actuelle',\n unit_of_measurement: 'Hz',\n state_class: 'measurement'\n },\n {\n unique_id: 'arkteos_dc_voltage',\n name: 'Voltage DC compresseur',\n state_topic: 'arkteos/frigo/dc_voltage',\n unit_of_measurement: 'V',\n device_class: 'voltage',\n state_class: 'measurement'\n },\n {\n unique_id: 'arkteos_puissance_inst_produite',\n name: 'Puissance instantanée produite',\n state_topic: 'arkteos/regulation/puissance_inst_produite',\n unit_of_measurement: 'kW',\n device_class: 'power',\n state_class: 'measurement'\n },\n {\n unique_id: 'arkteos_puissance_inst_consommee',\n name: 'Puissance instantanée consommée',\n state_topic: 'arkteos/regulation/puissance_inst_consommee',\n unit_of_measurement: 'kW',\n device_class: 'power',\n state_class: 'measurement'\n },\n {\n unique_id: 'arkteos_primaire_temp_eau_aller',\n name: 'Température eau primaire aller',\n state_topic: 'arkteos/regulation/primaire_temp_eau_aller',\n unit_of_measurement: '°C',\n device_class: 'temperature',\n state_class: 'measurement'\n },\n {\n unique_id: 'arkteos_primaire_temp_eau_retour',\n name: 'Température eau primaire retour',\n state_topic: 'arkteos/regulation/primaire_temp_eau_retour',\n unit_of_measurement: '°C',\n device_class: 'temperature',\n state_class: 'measurement'\n },\n {\n unique_id: 'arkteos_primaire_pression',\n name: 'Pression eau primaire',\n state_topic: 'arkteos/regulation/primaire_pression',\n unit_of_measurement: 'bar',\n device_class: 'pressure',\n state_class: 'measurement'\n },\n {\n unique_id: 'arkteos_zone1_temp_interieur',\n name: 'Température intérieure zone 1',\n state_topic: 'arkteos/regulation/zone1_temp_interieur',\n unit_of_measurement: '°C',\n device_class: 'temperature',\n state_class: 'measurement'\n },\n {\n unique_id: 'arkteos_zone1_consigne',\n name: 'Consigne température zone 1',\n state_topic: 'arkteos/regulation/zone1_consigne',\n unit_of_measurement: '°C',\n device_class: 'temperature',\n state_class: 'measurement'\n },\n {\n unique_id: 'arkteos_ecs_temp_eau_milieu',\n name: 'Température ballon ECS milieu',\n state_topic: 'arkteos/regulation/ecs_temp_eau_milieu',\n unit_of_measurement: '°C',\n device_class: 'temperature',\n state_class: 'measurement'\n },\n {\n unique_id: 'arkteos_ecs_temp_eau_bas',\n name: 'Température ballon ECS bas',\n state_topic: 'arkteos/regulation/ecs_temp_eau_bas',\n unit_of_measurement: '°C',\n device_class: 'temperature',\n state_class: 'measurement'\n }\n];\n\nconst msgs = [];\nfor (const s of sensors) {\n const config = Object.assign({}, s, { device });\n msgs.push({\n topic: `homeassistant/sensor/${s.unique_id}/config`,\n payload: JSON.stringify(config),\n qos: 0,\n retain: true\n });\n}\n\nreturn [msgs];\n",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 480,
"y": 360,
"wires": [["arkteos_debug_discovery", "arkteos_mqtt_out"]]
"id": "236adc97608b73f6",
"type": "exec",
"z": "4587847568fd2353",
"command": "curl -s -b /tmp/sw.cookie \"http://192.168.1.110/rest/hems-configurator/energy-overview?kg_session_extend=false\"",
"addpay": false,
"append": "",
"useSpawn": "false",
"timer": "",
"winHide": false,
"oldrc": false,
"name": "GET energy-overview",
"x": 380,
"y": 1100,
"wires": [
[
"f4f9d2dadc100f93",
"56941a627669dee7"
],
[
"9908ece2c4f23f1f"
],
[
"98b0f0acd301348c"
]
]
},
{
"id": "arkteos_debug_discovery",
"id": "56941a627669dee7",
"type": "debug",
"z": "arkteos_flow",
"name": "Discovery MQTT",
"active": true,
"z": "4587847568fd2353",
"name": "DEBUG energy JSON",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 710,
"y": 420,
"x": 610,
"y": 1060,
"wires": []
},
{
"id": "9908ece2c4f23f1f",
"type": "debug",
"z": "4587847568fd2353",
"name": "energy stderr",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"x": 380,
"y": 1160,
"wires": []
},
{
"id": "98b0f0acd301348c",
"type": "debug",
"z": "4587847568fd2353",
"name": "energy rc",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"x": 370,
"y": 1200,
"wires": []
},
{
"id": "f4f9d2dadc100f93",
"type": "function",
"z": "4587847568fd2353",
"name": "Parse energy-overview → MQTT",
"func": "let d;\ntry {\n d = JSON.parse(msg.payload);\n} catch (e) {\n node.error('Réponse non JSON sur energy-overview', msg);\n return null;\n}\n\nconst pv = Number(d.production || 0);\nconst gridExport = Number(d.feedIn || 0);\nconst gridImport = Number(d.feedOut || 0);\nconst home = Number(d.householdConsumption || 0);\nconst battCharge = Number(d.storagePowerIn || 0);\nconst battDischarge = Number(d.storagePowerOut || 0);\nconst battNet = battDischarge - battCharge;\n\nconst vals = {\n pv_power: pv,\n home_power: home,\n grid_import_power: gridImport,\n grid_export_power: gridExport,\n battery_charge_power: battCharge,\n battery_discharge_power: battDischarge,\n battery_power_net: battNet\n};\n\nfor (const [k, v] of Object.entries(vals)) {\n node.send({ topic: 'solarwatt/' + k, payload: String(v), retain: true, qos: 0 });\n}\nreturn null;",
"outputs": 1,
"noerr": 0,
"x": 650,
"y": 1100,
"wires": [
[
"mqtt_energy_out"
]
]
},
{
"id": "232a734730b9a103",
"type": "inject",
"z": "4587847568fd2353",
"name": "Poll items 60s",
"props": [],
"repeat": "60",
"crontab": "",
"once": true,
"onceDelay": "10",
"topic": "",
"x": 120,
"y": 1400,
"wires": [
[
"21dcfb1be4476c47"
]
]
},
{
"id": "21dcfb1be4476c47",
"type": "exec",
"z": "4587847568fd2353",
"command": "curl -s -b /tmp/sw.cookie \"http://192.168.1.110/rest/items?kg_session_extend=false\"",
"addpay": false,
"append": "",
"useSpawn": "false",
"timer": "",
"winHide": false,
"oldrc": false,
"name": "GET items",
"x": 350,
"y": 1400,
"wires": [
[
"684586d0793ae559",
"62bf7d3832073206"
],
[
"1edc91a8e6142d1c"
],
[
"696ce5dfaa450e23"
]
]
},
{
"id": "62bf7d3832073206",
"type": "debug",
"z": "4587847568fd2353",
"name": "DEBUG items JSON",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"x": 600,
"y": 1360,
"wires": []
},
{
"id": "1edc91a8e6142d1c",
"type": "debug",
"z": "4587847568fd2353",
"name": "items stderr",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"x": 350,
"y": 1460,
"wires": []
},
{
"id": "696ce5dfaa450e23",
"type": "debug",
"z": "4587847568fd2353",
"name": "items rc",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"x": 340,
"y": 1500,
"wires": []
},
{
"id": "684586d0793ae559",
"type": "function",
"z": "4587847568fd2353",
"name": "Parse items totals → MQTT",
"func": "function findItem(items, name) {\n return items.find(i => i.name === name);\n}\n\nfunction parseNumber(raw) {\n if (raw === null || raw === undefined) return null;\n let s = String(raw).trim();\n if (!s) return null;\n if (s.includes('|')) s = s.split('|')[1].trim();\n s = s.split(' ')[0].replace(',', '.');\n const n = Number(s);\n return Number.isFinite(n) ? n : null;\n}\n\nlet items;\ntry {\n items = JSON.parse(msg.payload);\n} catch (e) {\n node.error('Réponse non JSON sur items', msg);\n return null;\n}\n\nconst map = {\n battery_soc: 'foxesshybrid_battery_2R8H602058HP023_battery_bms_soc',\n produced_total: 'kiwigrid_location_standard_1823d84e3054_harmonized_work_produced_total',\n grid_import_total: 'kiwigrid_location_standard_1823d84e3054_harmonized_work_consumed_from_grid_total',\n grid_export_total: 'kiwigrid_location_standard_1823d84e3054_harmonized_work_out_total',\n home_consumption_total: 'kiwigrid_location_standard_1823d84e3054_harmonized_work_consumed_total',\n self_consumed_total: 'kiwigrid_location_standard_1823d84e3054_harmonized_work_self_consumed_total',\n battery_charged_from_grid_total: 'kiwigrid_location_standard_1823d84e3054_harmonized_work_buffered_from_grid_total',\n battery_out_total_site: 'kiwigrid_location_standard_1823d84e3054_harmonized_work_out_from_storage_total',\n battery_charge_total_raw: 'foxesshybrid_battery_2R8H602058HP023_battery_work_in_total',\n battery_discharge_total_raw: 'foxesshybrid_battery_2R8H602058HP023_battery_work_out_total',\n battery_temperature: 'foxesshybrid_battery_2R8H602058HP023_battery_bms_1_temperature',\n battery_power_raw: 'foxesshybrid_battery_2R8H602058HP023_battery_battery_calculated_power',\n battery_mode: 'foxesshybrid_battery_2R8H602058HP023_battery_mode'\n};\n\nfor (const [key, itemName] of Object.entries(map)) {\n const item = findItem(items, itemName);\n if (!item) continue;\n\n let value;\n if (key === 'battery_mode') {\n value = String(item.state || '');\n } else {\n value = parseNumber(item.state);\n if (value !== null && String(item.state || '').includes(' Wh')) {\n value = value / 1000;\n }\n if (value === null) continue;\n value = String(value);\n }\n\n node.send({ topic: 'solarwatt/' + key, payload: value, retain: true, qos: 0 });\n}\nreturn null;",
"outputs": 1,
"noerr": 0,
"x": 650,
"y": 1400,
"wires": [
[
"mqtt_items_out"
]
]
},
{
"id": "inj_avail_online",
"type": "inject",
"z": "4587847568fd2353",
"name": "Availability online",
"props": [
{
"p": "payload"
}
],
"repeat": "",
"crontab": "",
"once": true,
"onceDelay": "2",
"topic": "",
"payload": "online",
"payloadType": "str",
"x": 160,
"y": 1060,
"wires": [
[
"mqtt_avail_out"
]
]
},
{
"id": "mqtt_avail_out",
"type": "mqtt out",
"z": "4587847568fd2353",
"name": "MQTT availability",
"topic": "solarwatt/status",
"qos": "1",
"retain": "true",
"respTopic": "",
"contentType": "",
"userProps": "",
"correl": "",
"expiry": "",
"broker": "b2f1d497c83e5a12",
"x": 400,
"y": 1060,
"wires": []
},
{
"id": "inj_discovery",
"type": "inject",
"z": "4587847568fd2353",
"name": "MQTT Discovery au démarrage",
"props": [],
"repeat": "",
"crontab": "",
"once": true,
"onceDelay": "2",
"topic": "",
"x": 160,
"y": 200,
"wires": [
[
"disc_consolidated_fn"
]
]
},
{
"id": "disc_consolidated_fn",
"type": "function",
"z": "4587847568fd2353",
"name": "MQTT Discovery — 20 capteurs",
"func": "node.send({ topic: \"homeassistant/sensor/solarwatt_pv_power/config\", payload: JSON.stringify({\"name\": \"Solarwatt PV Power\", \"unique_id\": \"solarwatt_pv_power\", \"state_topic\": \"solarwatt/pv_power\", \"icon\": \"mdi:solar-power\", \"device\": {\"identifiers\": [\"solarwatt\"], \"name\": \"PV Solarwatt\", \"manufacturer\": \"Solarwatt / KiwiGrid / FoxESS\", \"model\": \"Solarwatt Energy Manager\", \"hw_version\": \"Battery Vision One\", \"sw_version\": \"mqtt-v1\"}, \"unit_of_measurement\": \"W\", \"device_class\": \"power\", \"state_class\": \"measurement\", \"object_id\": \"solarwatt_pv_power\", \"availability_topic\": \"solarwatt/status\", \"payload_available\": \"online\", \"payload_not_available\": \"offline\"}), retain: true, qos: 1 });\nnode.send({ topic: \"homeassistant/sensor/solarwatt_home_power/config\", payload: JSON.stringify({\"name\": \"Solarwatt Home Power\", \"unique_id\": \"solarwatt_home_power\", \"state_topic\": \"solarwatt/home_power\", \"icon\": \"mdi:home-lightning-bolt\", \"device\": {\"identifiers\": [\"solarwatt\"], \"name\": \"PV Solarwatt\", \"manufacturer\": \"Solarwatt / KiwiGrid / FoxESS\", \"model\": \"Solarwatt Energy Manager\", \"hw_version\": \"Battery Vision One\", \"sw_version\": \"mqtt-v1\"}, \"unit_of_measurement\": \"W\", \"device_class\": \"power\", \"state_class\": \"measurement\", \"object_id\": \"solarwatt_home_power\", \"availability_topic\": \"solarwatt/status\", \"payload_available\": \"online\", \"payload_not_available\": \"offline\"}), retain: true, qos: 1 });\nnode.send({ topic: \"homeassistant/sensor/solarwatt_grid_import_power/config\", payload: JSON.stringify({\"name\": \"Solarwatt Grid Import Power\", \"unique_id\": \"solarwatt_grid_import_power\", \"state_topic\": \"solarwatt/grid_import_power\", \"icon\": \"mdi:transmission-tower-import\", \"device\": {\"identifiers\": [\"solarwatt\"], \"name\": \"PV Solarwatt\", \"manufacturer\": \"Solarwatt / KiwiGrid / FoxESS\", \"model\": \"Solarwatt Energy Manager\", \"hw_version\": \"Battery Vision One\", \"sw_version\": \"mqtt-v1\"}, \"unit_of_measurement\": \"W\", \"device_class\": \"power\", \"state_class\": \"measurement\", \"object_id\": \"solarwatt_grid_import_power\", \"availability_topic\": \"solarwatt/status\", \"payload_available\": \"online\", \"payload_not_available\": \"offline\"}), retain: true, qos: 1 });\nnode.send({ topic: \"homeassistant/sensor/solarwatt_grid_export_power/config\", payload: JSON.stringify({\"name\": \"Solarwatt Grid Export Power\", \"unique_id\": \"solarwatt_grid_export_power\", \"state_topic\": \"solarwatt/grid_export_power\", \"icon\": \"mdi:transmission-tower-export\", \"device\": {\"identifiers\": [\"solarwatt\"], \"name\": \"PV Solarwatt\", \"manufacturer\": \"Solarwatt / KiwiGrid / FoxESS\", \"model\": \"Solarwatt Energy Manager\", \"hw_version\": \"Battery Vision One\", \"sw_version\": \"mqtt-v1\"}, \"unit_of_measurement\": \"W\", \"device_class\": \"power\", \"state_class\": \"measurement\", \"object_id\": \"solarwatt_grid_export_power\", \"availability_topic\": \"solarwatt/status\", \"payload_available\": \"online\", \"payload_not_available\": \"offline\"}), retain: true, qos: 1 });\nnode.send({ topic: \"homeassistant/sensor/solarwatt_battery_charge_power/config\", payload: JSON.stringify({\"name\": \"Solarwatt Battery Charge Power\", \"unique_id\": \"solarwatt_battery_charge_power\", \"state_topic\": \"solarwatt/battery_charge_power\", \"icon\": \"mdi:battery-arrow-up\", \"device\": {\"identifiers\": [\"solarwatt\"], \"name\": \"PV Solarwatt\", \"manufacturer\": \"Solarwatt / KiwiGrid / FoxESS\", \"model\": \"Solarwatt Energy Manager\", \"hw_version\": \"Battery Vision One\", \"sw_version\": \"mqtt-v1\"}, \"unit_of_measurement\": \"W\", \"device_class\": \"power\", \"state_class\": \"measurement\", \"object_id\": \"solarwatt_battery_charge_power\", \"availability_topic\": \"solarwatt/status\", \"payload_available\": \"online\", \"payload_not_available\": \"offline\"}), retain: true, qos: 1 });\nnode.send({ topic: \"homeassistant/sensor/solarwatt_battery_discharge_power/config\", payload: JSON.stringify({\"name\": \"Solarwatt Battery Discharge Power\", \"unique_id\": \"solarwatt_battery_discharge_power\", \"state_topic\": \"solarwatt/battery_discharge_power\", \"icon\": \"mdi:battery-arrow-down\", \"device\": {\"identifiers\": [\"solarwatt\"], \"name\": \"PV Solarwatt\", \"manufacturer\": \"Solarwatt / KiwiGrid / FoxESS\", \"model\": \"Solarwatt Energy Manager\", \"hw_version\": \"Battery Vision One\", \"sw_version\": \"mqtt-v1\"}, \"unit_of_measurement\": \"W\", \"device_class\": \"power\", \"state_class\": \"measurement\", \"object_id\": \"solarwatt_battery_discharge_power\", \"availability_topic\": \"solarwatt/status\", \"payload_available\": \"online\", \"payload_not_available\": \"offline\"}), retain: true, qos: 1 });\nnode.send({ topic: \"homeassistant/sensor/solarwatt_battery_power_net/config\", payload: JSON.stringify({\"name\": \"SolarwatLine truncated
"outputs": 1,
"noerr": 0,
"x": 450,
"y": 200,
"wires": [
[
"disc_consolidated_out"
]
]
},
{
"id": "disc_consolidated_out",
"type": "mqtt out",
"z": "4587847568fd2353",
"name": "MQTT Discovery out",
"topic": "",
"qos": "1",
"retain": "true",
"respTopic": "",
"contentType": "",
"userProps": "",
"correl": "",
"expiry": "",
"broker": "b2f1d497c83e5a12",
"x": 730,
"y": 200,
"wires": []
},
{
"id": "mqtt_energy_out",
"type": "mqtt out",
"z": "4587847568fd2353",
"name": "MQTT energy values",
"topic": "",
"qos": "0",
"retain": "true",
"respTopic": "",
"contentType": "",
"userProps": "",
"correl": "",
"expiry": "",
"broker": "b2f1d497c83e5a12",
"x": 950,
"y": 1100,
"wires": []
},
{
"id": "mqtt_items_out",
"type": "mqtt out",
"z": "4587847568fd2353",
"name": "MQTT items values",
"topic": "",
"qos": "0",
"retain": "true",
"respTopic": "",
"contentType": "",
"userProps": "",
"correl": "",
"expiry": "",
"broker": "b2f1d497c83e5a12",
"x": 950,
"y": 1400,
"wires": []
}
]