diff --git a/arkteos_reg3_nodered.json b/arkteos_reg3_nodered.json index f905afb..db8d306 100644 --- a/arkteos_reg3_nodered.json +++ b/arkteos_reg3_nodered.json @@ -4,7 +4,7 @@ "type": "tab", "label": "Arkteos REG3", "disabled": false, - "info": "Intégration PAC Arkteos Zuran 4 via addon proxy.\nStreaming permanent sur :9641.\nPublication MQTT avec discovery automatique HA et throttling.\nRemplacer par l'IP de Home Assistant dans le nœud PAC via proxy." + "info": "Intégration PAC Arkteos Zuran 4 via addon proxy.\nStreaming permanent sur :9641.\nPublication MQTT avec discovery automatique HA, throttling et filtrage valeurs aberrantes.\nRemplacer par l'IP de Home Assistant dans le nœud PAC via proxy." }, { "id": "arkteos_mqtt_broker", @@ -47,14 +47,61 @@ "tls": "", "x": 160, "y": 100, - "wires": [["arkteos_parse_frame"]] + "wires": [["arkteos_watchdog", "arkteos_parse_frame"]] + }, + { + "id": "arkteos_watchdog", + "type": "function", + "z": "arkteos_flow", + "name": "Watchdog connexion", + "func": "// Met à jour le timestamp de dernière réception\n// Un inject séparé vérifie si on a dépassé 30s sans données\nflow.set('arkteos_last_frame', Date.now());\nflow.set('arkteos_connected', true);\nreturn null;\n", + "outputs": 1, + "noerr": 0, + "initialize": "flow.set('arkteos_last_frame', 0);\nflow.set('arkteos_connected', false);\n", + "finalize": "", + "libs": [], + "x": 400, + "y": 40, + "wires": [[]] + }, + { + "id": "arkteos_watchdog_check_inject", + "type": "inject", + "z": "arkteos_flow", + "name": "Check connexion toutes les 15s", + "props": [{ "p": "payload" }], + "repeat": "15", + "crontab": "", + "once": true, + "onceDelay": 10, + "topic": "", + "payload": "", + "payloadType": "date", + "x": 180, + "y": 40, + "wires": [["arkteos_watchdog_check"]] + }, + { + "id": "arkteos_watchdog_check", + "type": "function", + "z": "arkteos_flow", + "name": "Check timeout", + "func": "const last = flow.get('arkteos_last_frame') || 0;\nconst connected = flow.get('arkteos_connected') || false;\nconst now = Date.now();\nconst elapsed = now - last;\n\n// Déconnecté si aucune trame depuis 30s\nconst isConnected = last > 0 && elapsed < 30000;\n\nif (isConnected !== connected) {\n flow.set('arkteos_connected', isConnected);\n msg.payload = isConnected ? 'ON' : 'OFF';\n msg.topic = 'arkteos/status/connected';\n return msg;\n}\n\n// Publie quand même toutes les 60s pour garder HA à jour\nif (elapsed % 60000 < 15000) {\n msg.payload = isConnected ? 'ON' : 'OFF';\n msg.topic = 'arkteos/status/connected';\n return msg;\n}\n\nreturn null;\n", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 430, + "y": 40, + "wires": [["arkteos_mqtt_out"]] }, { "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", + "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 freq_comp_cible: read16u(buf, 54),\n fan_speed_evaporator_1: read16u(buf, 56),\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) + read16u(buf, 17) * 256) / 0.1,\n puissance_inst_consommee: (read16u(buf, 18) + read16u(buf, 19) * 256) / 0.1,\n temps_mise_sous_tension: read16u(buf, 20),\n primaire_temp_eau_aller_consigne: read16s(buf, 52) / 10,\n primaire_temp_eau_aller: read16s(buf, 54) / 10,\n primaire_temp_eau_retour: read16s(buf, 56) / 10,\n primaire_debit_eau: read16u(buf, 60),\n primaire_pression: read8(buf, 62) / 10,\n primaire_pompe_pct: read8(buf, 64),\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 ecs_consigne: read16s(buf, 122) / 10,\n nb_cycles_compresseur: read16u(buf, 148)\n };\n return msg;\n}\n\nreturn null;\n", "outputs": 1, "noerr": 0, "initialize": "", @@ -62,6 +109,21 @@ "libs": [], "x": 380, "y": 100, + "wires": [["arkteos_filter_aberrant"]] + }, + { + "id": "arkteos_filter_aberrant", + "type": "function", + "z": "arkteos_flow", + "name": "Filtre valeurs aberrantes", + "func": "const data = msg.payload;\n\n// Plages valides par valeur\nconst RANGES = {\n // Frigo\n exterieur_temp: { min: -50, max: 150 },\n freq_comp_actuelle: { min: 0, max: 200 },\n freq_comp_cible: { min: 0, max: 200 },\n fan_speed_evaporator_1: { min: 0, max: 3000 },\n dc_voltage: { min: 0, max: 1000 },\n // Regulation\n puissance_inst_produite: { min: 0, max: 50000 },\n puissance_inst_consommee: { min: 0, max: 50000 },\n temps_mise_sous_tension: { min: 0, max: 999999 },\n primaire_temp_eau_aller_consigne: { min: -10, max: 90 },\n primaire_temp_eau_aller: { min: -10, max: 90 },\n primaire_temp_eau_retour: { min: -10, max: 90 },\n primaire_debit_eau: { min: 0, max: 10000 },\n primaire_pression: { min: 0, max: 10 },\n primaire_pompe_pct: { min: 0, max: 100 },\n zone1_temp_interieur: { min: -10, max: 50 },\n zone1_consigne: { min: 5, max: 35 },\n ecs_temp_eau_milieu: { min: 0, max: 95 },\n ecs_temp_eau_bas: { min: 0, max: 95 },\n ecs_consigne: { min: 0, max: 80 },\n nb_cycles_compresseur: { min: 0, max: 999999 }\n};\n\nconst filtered = { frame_type: data.frame_type };\nconst rejected = {};\n\nfor (const [key, val] of Object.entries(data)) {\n if (key === 'frame_type') continue;\n const range = RANGES[key];\n if (!range) {\n filtered[key] = val;\n continue;\n }\n if (val >= range.min && val <= range.max) {\n filtered[key] = val;\n } else {\n rejected[key] = val;\n }\n}\n\nif (Object.keys(rejected).length > 0) {\n node.warn('Valeurs aberrantes filtrées : ' + JSON.stringify(rejected));\n}\n\nmsg.payload = filtered;\nreturn msg;\n", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 590, + "y": 100, "wires": [["arkteos_route"]] }, { @@ -78,7 +140,7 @@ "checkall": "false", "repair": false, "outputs": 2, - "x": 580, + "x": 800, "y": 100, "wires": [ ["arkteos_throttle_frigo"], @@ -90,13 +152,13 @@ "type": "function", "z": "arkteos_flow", "name": "Throttle frigo", - "func": "// Seuils de changement minimum pour publier\nconst THRESHOLDS = {\n exterieur_temp: 0.5,\n freq_comp_actuelle: 1,\n dc_voltage: 5\n};\n// Intervalle max de publication en ms même sans changement\nconst MAX_INTERVALS = {\n exterieur_temp: 60000,\n freq_comp_actuelle: 30000,\n dc_voltage: 60000\n};\n\nconst now = Date.now();\nconst prev = context.get('prev') || {};\nconst lastPublished = context.get('lastPublished') || {};\n\nconst data = msg.payload;\nconst toPublish = {};\n\nfor (const key of Object.keys(THRESHOLDS)) {\n const val = data[key];\n const prevVal = prev[key];\n const lastTs = lastPublished[key] || 0;\n const delta = prevVal !== undefined ? Math.abs(val - prevVal) : Infinity;\n const elapsed = now - lastTs;\n\n if (delta >= THRESHOLDS[key] || elapsed >= MAX_INTERVALS[key]) {\n toPublish[key] = val;\n lastPublished[key] = now;\n prev[key] = val;\n }\n}\n\ncontext.set('prev', prev);\ncontext.set('lastPublished', lastPublished);\n\nif (Object.keys(toPublish).length === 0) return null;\n\nmsg.payload = Object.assign({ frame_type: 'frigo' }, toPublish);\nreturn msg;\n", + "func": "const THRESHOLDS = {\n exterieur_temp: 0.5,\n freq_comp_actuelle: 1,\n freq_comp_cible: 1,\n fan_speed_evaporator_1: 10,\n dc_voltage: 5\n};\nconst MAX_INTERVALS = {\n exterieur_temp: 60000,\n freq_comp_actuelle: 30000,\n freq_comp_cible: 30000,\n fan_speed_evaporator_1: 30000,\n dc_voltage: 60000\n};\n\nconst now = Date.now();\nconst prev = context.get('prev') || {};\nconst lastPublished = context.get('lastPublished') || {};\nconst data = msg.payload;\nconst toPublish = {};\n\nfor (const key of Object.keys(THRESHOLDS)) {\n const val = data[key];\n if (val === undefined) continue;\n const prevVal = prev[key];\n const lastTs = lastPublished[key] || 0;\n const delta = prevVal !== undefined ? Math.abs(val - prevVal) : Infinity;\n const elapsed = now - lastTs;\n if (delta >= THRESHOLDS[key] || elapsed >= MAX_INTERVALS[key]) {\n toPublish[key] = val;\n lastPublished[key] = now;\n prev[key] = val;\n }\n}\n\ncontext.set('prev', prev);\ncontext.set('lastPublished', lastPublished);\n\nif (Object.keys(toPublish).length === 0) return null;\n\nmsg.payload = Object.assign({ frame_type: 'frigo' }, toPublish);\nreturn msg;\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], - "x": 790, + "x": 1010, "y": 60, "wires": [["arkteos_debug_frigo_parsed", "arkteos_publish_frigo"]] }, @@ -105,13 +167,13 @@ "type": "function", "z": "arkteos_flow", "name": "Throttle regulation", - "func": "// Seuils de changement minimum pour publier\nconst THRESHOLDS = {\n puissance_inst_produite: 0.1,\n puissance_inst_consommee: 0.1,\n primaire_temp_eau_aller: 0.5,\n primaire_temp_eau_retour: 0.5,\n primaire_pression: 0.1,\n zone1_temp_interieur: 0.5,\n zone1_consigne: 0.5,\n ecs_temp_eau_milieu: 0.5,\n ecs_temp_eau_bas: 0.5\n};\n// Intervalle max de publication en ms même sans changement\nconst MAX_INTERVALS = {\n puissance_inst_produite: 30000,\n puissance_inst_consommee: 30000,\n primaire_temp_eau_aller: 60000,\n primaire_temp_eau_retour: 60000,\n primaire_pression: 60000,\n zone1_temp_interieur: 60000,\n zone1_consigne: 60000,\n ecs_temp_eau_milieu: 60000,\n ecs_temp_eau_bas: 60000\n};\n\nconst now = Date.now();\nconst prev = context.get('prev') || {};\nconst lastPublished = context.get('lastPublished') || {};\n\nconst data = msg.payload;\nconst toPublish = {};\n\nfor (const key of Object.keys(THRESHOLDS)) {\n const val = data[key];\n const prevVal = prev[key];\n const lastTs = lastPublished[key] || 0;\n const delta = prevVal !== undefined ? Math.abs(val - prevVal) : Infinity;\n const elapsed = now - lastTs;\n\n if (delta >= THRESHOLDS[key] || elapsed >= MAX_INTERVALS[key]) {\n toPublish[key] = val;\n lastPublished[key] = now;\n prev[key] = val;\n }\n}\n\ncontext.set('prev', prev);\ncontext.set('lastPublished', lastPublished);\n\nif (Object.keys(toPublish).length === 0) return null;\n\nmsg.payload = Object.assign({ frame_type: 'regulation' }, toPublish);\nreturn msg;\n", + "func": "const THRESHOLDS = {\n puissance_inst_produite: 10,\n puissance_inst_consommee: 10,\n temps_mise_sous_tension: 1,\n primaire_temp_eau_aller_consigne: 0.5,\n primaire_temp_eau_aller: 0.5,\n primaire_temp_eau_retour: 0.5,\n primaire_debit_eau: 10,\n primaire_pression: 0.1,\n primaire_pompe_pct: 1,\n zone1_temp_interieur: 0.5,\n zone1_consigne: 0.5,\n ecs_temp_eau_milieu: 0.5,\n ecs_temp_eau_bas: 0.5,\n ecs_consigne: 0.5,\n nb_cycles_compresseur: 1\n};\nconst MAX_INTERVALS = {\n puissance_inst_produite: 30000,\n puissance_inst_consommee: 30000,\n temps_mise_sous_tension: 300000,\n primaire_temp_eau_aller_consigne: 60000,\n primaire_temp_eau_aller: 60000,\n primaire_temp_eau_retour: 60000,\n primaire_debit_eau: 60000,\n primaire_pression: 60000,\n primaire_pompe_pct: 60000,\n zone1_temp_interieur: 60000,\n zone1_consigne: 60000,\n ecs_temp_eau_milieu: 60000,\n ecs_temp_eau_bas: 60000,\n ecs_consigne: 60000,\n nb_cycles_compresseur: 300000\n};\n\nconst now = Date.now();\nconst prev = context.get('prev') || {};\nconst lastPublished = context.get('lastPublished') || {};\nconst data = msg.payload;\nconst toPublish = {};\n\nfor (const key of Object.keys(THRESHOLDS)) {\n const val = data[key];\n if (val === undefined) continue;\n const prevVal = prev[key];\n const lastTs = lastPublished[key] || 0;\n const delta = prevVal !== undefined ? Math.abs(val - prevVal) : Infinity;\n const elapsed = now - lastTs;\n if (delta >= THRESHOLDS[key] || elapsed >= MAX_INTERVALS[key]) {\n toPublish[key] = val;\n lastPublished[key] = now;\n prev[key] = val;\n }\n}\n\ncontext.set('prev', prev);\ncontext.set('lastPublished', lastPublished);\n\nif (Object.keys(toPublish).length === 0) return null;\n\nmsg.payload = Object.assign({ frame_type: 'regulation' }, toPublish);\nreturn msg;\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], - "x": 790, + "x": 1010, "y": 140, "wires": [["arkteos_debug_regulation_parsed", "arkteos_publish_regulation"]] }, @@ -128,7 +190,7 @@ "targetType": "msg", "statusVal": "", "statusType": "auto", - "x": 1010, + "x": 1230, "y": 40, "wires": [] }, @@ -145,7 +207,7 @@ "targetType": "msg", "statusVal": "", "statusType": "auto", - "x": 1030, + "x": 1250, "y": 160, "wires": [] }, @@ -154,13 +216,13 @@ "type": "function", "z": "arkteos_flow", "name": "Publish frigo", - "func": "const data = msg.payload;\nconst msgs = [];\n\nconst topics = {\n exterieur_temp: 'arkteos/frigo/exterieur_temp',\n freq_comp_actuelle: 'arkteos/frigo/freq_comp_actuelle',\n dc_voltage: 'arkteos/frigo/dc_voltage'\n};\n\nfor (const [key, topic] of Object.entries(topics)) {\n if (data[key] !== undefined) {\n msgs.push({ topic, payload: String(data[key]), qos: 0, retain: true });\n }\n}\n\nif (msgs.length === 0) return null;\nreturn [msgs];\n", + "func": "const data = msg.payload;\nconst topics = {\n exterieur_temp: 'arkteos/frigo/exterieur_temp',\n freq_comp_actuelle: 'arkteos/frigo/freq_comp_actuelle',\n freq_comp_cible: 'arkteos/frigo/freq_comp_cible',\n fan_speed_evaporator_1: 'arkteos/frigo/fan_speed_evaporator_1',\n dc_voltage: 'arkteos/frigo/dc_voltage'\n};\n\nconst msgs = [];\nfor (const [key, topic] of Object.entries(topics)) {\n if (data[key] !== undefined) {\n msgs.push({ topic, payload: String(data[key]), qos: 0, retain: true });\n }\n}\n\nif (msgs.length === 0) return null;\nreturn [msgs];\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], - "x": 1000, + "x": 1230, "y": 80, "wires": [["arkteos_debug_frigo_published", "arkteos_mqtt_out"]] }, @@ -169,13 +231,13 @@ "type": "function", "z": "arkteos_flow", "name": "Publish regulation", - "func": "const data = msg.payload;\nconst msgs = [];\n\nconst topics = {\n puissance_inst_produite: 'arkteos/regulation/puissance_inst_produite',\n puissance_inst_consommee: 'arkteos/regulation/puissance_inst_consommee',\n primaire_temp_eau_aller: 'arkteos/regulation/primaire_temp_eau_aller',\n primaire_temp_eau_retour: 'arkteos/regulation/primaire_temp_eau_retour',\n primaire_pression: 'arkteos/regulation/primaire_pression',\n zone1_temp_interieur: 'arkteos/regulation/zone1_temp_interieur',\n zone1_consigne: 'arkteos/regulation/zone1_consigne',\n ecs_temp_eau_milieu: 'arkteos/regulation/ecs_temp_eau_milieu',\n ecs_temp_eau_bas: 'arkteos/regulation/ecs_temp_eau_bas'\n};\n\nfor (const [key, topic] of Object.entries(topics)) {\n if (data[key] !== undefined) {\n msgs.push({ topic, payload: String(data[key]), qos: 0, retain: true });\n }\n}\n\nif (msgs.length === 0) return null;\nreturn [msgs];\n", + "func": "const data = msg.payload;\nconst topics = {\n puissance_inst_produite: 'arkteos/regulation/puissance_inst_produite',\n puissance_inst_consommee: 'arkteos/regulation/puissance_inst_consommee',\n temps_mise_sous_tension: 'arkteos/regulation/temps_mise_sous_tension',\n primaire_temp_eau_aller_consigne: 'arkteos/regulation/primaire_temp_eau_aller_consigne',\n primaire_temp_eau_aller: 'arkteos/regulation/primaire_temp_eau_aller',\n primaire_temp_eau_retour: 'arkteos/regulation/primaire_temp_eau_retour',\n primaire_debit_eau: 'arkteos/regulation/primaire_debit_eau',\n primaire_pression: 'arkteos/regulation/primaire_pression',\n primaire_pompe_pct: 'arkteos/regulation/primaire_pompe_pct',\n zone1_temp_interieur: 'arkteos/regulation/zone1_temp_interieur',\n zone1_consigne: 'arkteos/regulation/zone1_consigne',\n ecs_temp_eau_milieu: 'arkteos/regulation/ecs_temp_eau_milieu',\n ecs_temp_eau_bas: 'arkteos/regulation/ecs_temp_eau_bas',\n ecs_consigne: 'arkteos/regulation/ecs_consigne',\n nb_cycles_compresseur: 'arkteos/regulation/nb_cycles_compresseur'\n};\n\nconst msgs = [];\nfor (const [key, topic] of Object.entries(topics)) {\n if (data[key] !== undefined) {\n msgs.push({ topic, payload: String(data[key]), qos: 0, retain: true });\n }\n}\n\nif (msgs.length === 0) return null;\nreturn [msgs];\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], - "x": 1000, + "x": 1230, "y": 200, "wires": [["arkteos_debug_regulation_published", "arkteos_mqtt_out"]] }, @@ -192,7 +254,7 @@ "targetType": "msg", "statusVal": "", "statusType": "auto", - "x": 1230, + "x": 1460, "y": 40, "wires": [] }, @@ -209,7 +271,7 @@ "targetType": "msg", "statusVal": "", "statusType": "auto", - "x": 1250, + "x": 1480, "y": 240, "wires": [] }, @@ -222,7 +284,7 @@ "qos": "0", "retain": "true", "broker": "arkteos_mqtt_broker", - "x": 1240, + "x": 1460, "y": 140, "wires": [] }, @@ -240,7 +302,7 @@ "payload": "", "payloadType": "date", "x": 200, - "y": 360, + "y": 460, "wires": [["arkteos_discovery"]] }, { @@ -248,14 +310,14 @@ "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", + "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 // Binary sensor statut connexion\n {\n unique_id: 'arkteos_connected',\n name: 'PAC connectée',\n state_topic: 'arkteos/status/connected',\n device_class: 'connectivity',\n payload_on: 'ON',\n payload_off: 'OFF',\n _type: 'binary_sensor'\n },\n // Frigo\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_freq_comp_cible',\n name: 'Fréquence compresseur cible',\n state_topic: 'arkteos/frigo/freq_comp_cible',\n unit_of_measurement: 'Hz',\n state_class: 'measurement'\n },\n {\n unique_id: 'arkteos_fan_speed_evaporator_1',\n name: 'Vitesse ventilateur groupe frigo',\n state_topic: 'arkteos/frigo/fan_speed_evaporator_1',\n unit_of_measurement: 'rpm',\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 // Regulation\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: 'W',\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: 'W',\n device_class: 'power',\n state_class: 'measurement'\n },\n {\n unique_id: 'arkteos_temps_mise_sous_tension',\n name: 'Temps mise sous tension',\n state_topic: 'arkteos/regulation/temps_mise_sous_tension',\n unit_of_measurement: 'h',\n device_class: 'duration',\n state_class: 'total_increasing'\n },\n {\n unique_id: 'arkteos_primaire_temp_eau_aller_consigne',\n name: 'Consigne température départ eau primaire',\n state_topic: 'arkteos/regulation/primaire_temp_eau_aller_consigne',\n unit_of_measurement: '°C',\n device_class: 'temperature',\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_debit_eau',\n name: 'Débit eau primaire',\n state_topic: 'arkteos/regulation/primaire_debit_eau',\n unit_of_measurement: 'L/h',\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_primaire_pompe_pct',\n name: 'Circulateur primaire',\n state_topic: 'arkteos/regulation/primaire_pompe_pct',\n unit_of_measurement: '%',\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 unique_id: 'arkteos_ecs_consigne',\n name: 'Consigne ECS',\n state_topic: 'arkteos/regulation/ecs_consigne',\n unit_of_measurement: '°C',\n device_class: 'temperature',\n state_class: 'measurement'\n },\n {\n unique_id: 'arkteos_nb_cycles_compresseur',\n name: 'Nombre de cycles compresseur',\n state_topic: 'arkteos/regulation/nb_cycles_compresseur',\n state_class: 'total_increasing'\n }\n];\n\nconst msgs = [];\nfor (const s of sensors) {\n const type = s._type || 'sensor';\n const config = Object.assign({}, s, { device });\n delete config._type;\n msgs.push({\n topic: `homeassistant/${type}/${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, + "y": 460, "wires": [["arkteos_debug_discovery", "arkteos_mqtt_out"]] }, { @@ -272,7 +334,7 @@ "statusVal": "", "statusType": "auto", "x": 710, - "y": 420, + "y": 520, "wires": [] } ]