smart light Node-RED+zwave+dz

Décrivez votre installation de Domotique : ce que vous gérez, quels sont les points forts de votre config.
Illustrez par des photos, partagez et faites nous envie !
Répondre
hestia
Messages : 240
Enregistré le : 12 sept. 2018, 22:36

smart light Node-RED+zwave+dz

Message par hestia »

après l'évocation d'une solution basée sur Node-RED + dz Question refonte domotique, je suis allée plus loin pour mon éclairage extérieur.
zwavejs2mqtt <-> Node-RED pour l'allumage (rapide) / dz pour l’extinction qui a des règles plus complexes.

* L'architecture
LumExtArchi.png
LumExtArchi.png (5.03 Kio) Vu 134 fois
* Le fonctionnement
La partie Node-RED (NR) est déclenchée par les détecteurs de mouvement et allume les lumières.
La partie Domoticz (dz) gère l'extinction des lumières.

NR est plus réactif que dz et permet d'allumer les lumières plus rapidement que la gestion des événements de dz (qui pourrait être ralentie parfois avec la sauvegarde ou tout script long).
Peu de fonctions dans NR pour le garder léger et réactif.

La partie la plus délicate était les "ids" des "devices" (qui ne sont pas les mêmes dans zwavejs2mqtt et dz) que je voulais éviter de les dupliquer.
La translation est faite dans dz entre le sujet (mqtt de NR) et l'ID (dz Hardware).

Avec mes devices zwave, c'est le même devices pour l'interrupteur et la lumière (à adapter pour les lumières avec des interrupteurs séparés).

Un autre script calcule la valeur du rayonnement solaire (lux) à donner à NR : 0 = nuit ; 2 = jour ; 1 = jour pour les capteurs, nuit pour les lumières.

* Les règles
Plusieurs capteurs et plusieurs lumières, les lumières peuvent être allumées par leur interrupteur ou par les capteurs de mouvement assignés.
Après un délai, elles s'éteignent ; il y a un délai pour les détecteurs de mouvement (court) et un autre pour les interrupteurs (plus long).
Tout mouvement réinitialise le décompte du délai de l'extinction.

Un détecteur de mouvement peut allumer plusieurs lumières

Les lumières peuvent être allumées si c'est la nuit, le niveau n'est pas le même pour les détecteurs et les interrupteurs.
Une personne pourrait décider d'allumer la lumière même s'il n'y a pas assez de lumière pour permettre à un détecteur de mouvement de s'allumer.
Il faut plus de lumière pour lire que pour marcher ;-)

Lorsque les lumières sont allumées par des capteurs, elles peuvent être éteintes par n'importe quel interrupteur avec la lumière allumée.

Il y a aussi un double contrôle pour être sûr que les lumières sont vraiment éteintes, en cas de perte de déclenchement ou autre problème (mais pas en cas de problèmes avec le réseau zwave).

Une autre partie délicate est que zwave donne plusieurs messages quand une lumière est allumée et éteinte,
une fonction spécifique a été faite pour filtrer les doublons (à accorder avec le réseau).

Node-RED
LumExtNodeRED.png
LumExtNodeRED.png (166.87 Kio) Vu 134 fois

Code : Tout sélectionner

[
    {
        "id": "6262a545dc54b0c6",
        "type": "tab",
        "label": "Eclairage Extérieur",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "4d06b0e190e41b0f",
        "type": "switch",
        "z": "6262a545dc54b0c6",
        "name": "Motions",
        "property": "topic",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "zwave/99/113/0/Home_Security/Motion_sensor_status",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "zwave/121/113/0/Home_Security/Motion_sensor_status",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "zwave/80/113/0/Home_Security/Motion_sensor_status",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "zwave/126/48/0/Motion",
                "vt": "str"
            }
        ],
        "checkall": "false",
        "repair": false,
        "outputs": 4,
        "x": 160,
        "y": 160,
        "wires": [
            [
                "9fd52e88907223f2"
            ],
            [
                "70ae0b14d4602f36"
            ],
            [
                "25c1793fe834d50e"
            ],
            [
                "051a58a323a529a8"
            ]
        ]
    },
    {
        "id": "f774526cf796f4cf",
        "type": "mqtt in",
        "z": "6262a545dc54b0c6",
        "name": "from zwave",
        "topic": "zwave/#",
        "qos": "2",
        "datatype": "auto",
        "broker": "35f3ae39d3073cb5",
        "nl": false,
        "rap": true,
        "rh": 0,
        "inputs": 0,
        "x": 90,
        "y": 360,
        "wires": [
            [
                "9a94f9dc77ae1354",
                "ea5b1323d1266075",
                "279a24a53e1b9f3f"
            ]
        ]
    },
    {
        "id": "b715c325798fbfd3",
        "type": "mqtt out",
        "z": "6262a545dc54b0c6",
        "name": "to dz",
        "topic": "domoticz/in",
        "qos": "2",
        "retain": "",
        "respTopic": "",
        "contentType": "",
        "userProps": "",
        "correl": "",
        "expiry": "",
        "broker": "35f3ae39d3073cb5",
        "x": 1010,
        "y": 440,
        "wires": []
    },
    {
        "id": "f9359ae301fe4a01",
        "type": "mqtt out",
        "z": "6262a545dc54b0c6",
        "name": "to zwave",
        "topic": "",
        "qos": "2",
        "retain": "",
        "respTopic": "",
        "contentType": "",
        "userProps": "",
        "correl": "",
        "expiry": "",
        "broker": "35f3ae39d3073cb5",
        "x": 1020,
        "y": 200,
        "wires": []
    },
    {
        "id": "411708a0e722d9a9",
        "type": "mqtt in",
        "z": "6262a545dc54b0c6",
        "name": "dz Day or Night",
        "topic": "domoticz/out/daynight",
        "qos": "2",
        "datatype": "utf8",
        "broker": "35f3ae39d3073cb5",
        "nl": false,
        "rap": true,
        "rh": 0,
        "inputs": 0,
        "x": 160,
        "y": 680,
        "wires": [
            [
                "5ee19e982756d0eb",
                "e761a3000063ebd5"
            ]
        ]
    },
    {
        "id": "172b53b872529718",
        "type": "debug",
        "z": "6262a545dc54b0c6",
        "d": true,
        "name": "No motion",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 830,
        "y": 40,
        "wires": []
    },
    {
        "id": "9a94f9dc77ae1354",
        "type": "switch",
        "z": "6262a545dc54b0c6",
        "name": "Lights",
        "property": "topic",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "zwave/102/38/0/currentValue",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "zwave/150/38/0/currentValue",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "zwave/11/38/0/currentValue",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "zwave/156/38/0/currentValue",
                "vt": "str"
            }
        ],
        "checkall": "false",
        "repair": false,
        "outputs": 4,
        "x": 150,
        "y": 500,
        "wires": [
            [
                "46b3cbd2ab75bd69"
            ],
            [
                "46b3cbd2ab75bd69"
            ],
            [
                "46b3cbd2ab75bd69"
            ],
            [
                "46b3cbd2ab75bd69"
            ]
        ]
    },
    {
        "id": "446b6a41513f9c50",
        "type": "function",
        "z": "6262a545dc54b0c6",
        "name": "Light w Motion",
        "func": "// build the vars to get the state on the light and of the motion\nvar light_topic = msg.topic.split(\"/\")[0] +\n\"/\" + msg.topic.split(\"/\")[1] +\n\"/\" + msg.topic.split(\"/\")[2] + \n\"/\" + msg.topic.split(\"/\")[3] + \n\"/\";\n\n// state of the light\nvar var_lighttopic = light_topic + \"_on\";\n\n// state of the motion\n// 0 = no motion (not here)\n// 1 = motion to switch On the light (if Off = 0 ; <> 1) => to zwave\n// 2 = motion to keep the light On (if On = 1) => to dz only\nvar var_motion = light_topic + \"_motion\";\nvar motion_state = flow.get(var_motion);\nif (motion_state === 'undefined') {\n    motion_state = 9\n}\n// motion topic to send to dz in order to find the motion idx\nvar var_motion_topic = light_topic + \"_motion_topic\";\nvar motion_topic = flow.get(var_motion_topic);\nif (motion_topic === 'undefined') {\n    motion_topic = 0\n}\n\n// \nvar light_state;\nif (msg.payload.value === 0 || msg.payload.value === false) {\n    light_state = 0;\n    motion_state = 0;\n}\nelse {\n    light_state = 1;\n}\n\nflow.set(var_lighttopic, light_state);\nflow.set(var_motion, 0);\n\nmsg.payload.light_state = light_state;\nmsg.payload.motion_state = motion_state;\nmsg.payload.light_topic = light_topic;\nmsg.payload.motion_topic = motion_topic;\n\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 580,
        "y": 500,
        "wires": [
            [
                "758a3a9697543029",
                "da7c683e1802fdfa"
            ]
        ]
    },
    {
        "id": "66796159fb3745f2",
        "type": "function",
        "z": "6262a545dc54b0c6",
        "name": "Light to zwave",
        "func": "// build the topic to zwavejs2mqtt\nmsg.topic = msg.payload.light_topic + \"targetValue/set\";\n\nmsg.payload = msg.payload.light_value\n\nreturn msg\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 820,
        "y": 180,
        "wires": [
            [
                "f9359ae301fe4a01",
                "1e1fcae53b65176e"
            ]
        ]
    },
    {
        "id": "758a3a9697543029",
        "type": "debug",
        "z": "6262a545dc54b0c6",
        "d": true,
        "name": "Light On / Off",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 830,
        "y": 540,
        "wires": []
    },
    {
        "id": "9425bab3e2ae8d98",
        "type": "debug",
        "z": "6262a545dc54b0c6",
        "d": true,
        "name": "Motion w Light",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 820,
        "y": 240,
        "wires": []
    },
    {
        "id": "25c1793fe834d50e",
        "type": "json",
        "z": "6262a545dc54b0c6",
        "name": "Motion Portail",
        "property": "payload",
        "action": "obj",
        "pretty": false,
        "x": 360,
        "y": 200,
        "wires": [
            [
                "2a3ad179e42046b9",
                "9362535c2cbdbc34"
            ]
        ]
    },
    {
        "id": "9362535c2cbdbc34",
        "type": "change",
        "z": "6262a545dc54b0c6",
        "name": "Light Terrasse",
        "rules": [
            {
                "t": "set",
                "p": "payload.light_topic",
                "pt": "msg",
                "to": "zwave/156/38/0/",
                "tot": "str"
            },
            {
                "t": "set",
                "p": "payload.light_value",
                "pt": "msg",
                "to": "99",
                "tot": "num"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 580,
        "y": 240,
        "wires": [
            [
                "9425bab3e2ae8d98",
                "b93cb8c4baf8cfba"
            ]
        ]
    },
    {
        "id": "1e1fcae53b65176e",
        "type": "debug",
        "z": "6262a545dc54b0c6",
        "name": "to zwave",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 1020,
        "y": 140,
        "wires": []
    },
    {
        "id": "0252414ff9c0e132",
        "type": "debug",
        "z": "6262a545dc54b0c6",
        "name": "to dz",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 1010,
        "y": 500,
        "wires": []
    },
    {
        "id": "0828a86f1ce9ce0d",
        "type": "change",
        "z": "6262a545dc54b0c6",
        "name": "Light Allée",
        "rules": [
            {
                "t": "set",
                "p": "payload.light_topic",
                "pt": "msg",
                "to": "zwave/150/38/0/",
                "tot": "str"
            },
            {
                "t": "set",
                "p": "payload.light_value",
                "pt": "msg",
                "to": "99",
                "tot": "num"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 570,
        "y": 160,
        "wires": [
            [
                "9425bab3e2ae8d98",
                "b93cb8c4baf8cfba"
            ]
        ]
    },
    {
        "id": "2a3ad179e42046b9",
        "type": "change",
        "z": "6262a545dc54b0c6",
        "name": "Light Portail",
        "rules": [
            {
                "t": "set",
                "p": "payload.light_topic",
                "pt": "msg",
                "to": "zwave/11/38/0/",
                "tot": "str"
            },
            {
                "t": "set",
                "p": "payload.light_value",
                "pt": "msg",
                "to": "99",
                "tot": "num"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 570,
        "y": 200,
        "wires": [
            [
                "9425bab3e2ae8d98",
                "b93cb8c4baf8cfba"
            ]
        ]
    },
    {
        "id": "249c24b97f469711",
        "type": "change",
        "z": "6262a545dc54b0c6",
        "name": "Light Portillon",
        "rules": [
            {
                "t": "set",
                "p": "payload.light_topic",
                "pt": "msg",
                "to": "zwave/102/38/0/",
                "tot": "str"
            },
            {
                "t": "set",
                "p": "payload.light_value",
                "pt": "msg",
                "to": "60",
                "tot": "num"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 580,
        "y": 120,
        "wires": [
            [
                "9425bab3e2ae8d98",
                "b93cb8c4baf8cfba"
            ]
        ]
    },
    {
        "id": "9fd52e88907223f2",
        "type": "json",
        "z": "6262a545dc54b0c6",
        "name": "Motion Portillon",
        "property": "payload",
        "action": "obj",
        "pretty": false,
        "x": 360,
        "y": 120,
        "wires": [
            [
                "249c24b97f469711",
                "0828a86f1ce9ce0d"
            ]
        ]
    },
    {
        "id": "70ae0b14d4602f36",
        "type": "json",
        "z": "6262a545dc54b0c6",
        "name": "Motion Allée",
        "property": "payload",
        "action": "obj",
        "pretty": false,
        "x": 350,
        "y": 160,
        "wires": [
            [
                "0828a86f1ce9ce0d",
                "249c24b97f469711"
            ]
        ]
    },
    {
        "id": "051a58a323a529a8",
        "type": "json",
        "z": "6262a545dc54b0c6",
        "name": "Motion Terrasse",
        "property": "payload",
        "action": "obj",
        "pretty": false,
        "x": 360,
        "y": 240,
        "wires": [
            [
                "9362535c2cbdbc34"
            ]
        ]
    },
    {
        "id": "1428cc47125190b4",
        "type": "debug",
        "z": "6262a545dc54b0c6",
        "d": true,
        "name": "Day for Motion",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 360,
        "y": 320,
        "wires": []
    },
    {
        "id": "ea5b1323d1266075",
        "type": "switch",
        "z": "6262a545dc54b0c6",
        "name": "Night?",
        "property": "dayNight",
        "propertyType": "flow",
        "rules": [
            {
                "t": "eq",
                "v": "0",
                "vt": "num"
            },
            {
                "t": "else"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 90,
        "y": 300,
        "wires": [
            [
                "4d06b0e190e41b0f"
            ],
            [
                "1428cc47125190b4"
            ]
        ]
    },
    {
        "id": "b93cb8c4baf8cfba",
        "type": "switch",
        "z": "6262a545dc54b0c6",
        "name": "Motion?",
        "property": "payload.value",
        "propertyType": "msg",
        "rules": [
            {
                "t": "false"
            },
            {
                "t": "eq",
                "v": "0",
                "vt": "num"
            },
            {
                "t": "else"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 3,
        "x": 640,
        "y": 60,
        "wires": [
            [
                "172b53b872529718"
            ],
            [
                "172b53b872529718"
            ],
            [
                "abd5e88fe39240d3"
            ]
        ]
    },
    {
        "id": "f94c8c2c1883bcf7",
        "type": "debug",
        "z": "6262a545dc54b0c6",
        "d": true,
        "name": "Light On",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 1020,
        "y": 60,
        "wires": []
    },
    {
        "id": "c055eaae780d191b",
        "type": "debug",
        "z": "6262a545dc54b0c6",
        "d": true,
        "name": "Light Off",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 1020,
        "y": 100,
        "wires": []
    },
    {
        "id": "46b3cbd2ab75bd69",
        "type": "json",
        "z": "6262a545dc54b0c6",
        "name": "Lights On/Off",
        "property": "payload",
        "action": "obj",
        "pretty": false,
        "x": 350,
        "y": 500,
        "wires": [
            [
                "446b6a41513f9c50"
            ]
        ]
    },
    {
        "id": "e27cb3182cb34645",
        "type": "inject",
        "z": "6262a545dc54b0c6",
        "d": true,
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 200,
        "y": 780,
        "wires": [
            [
                "e984b358ca970466"
            ]
        ]
    },
    {
        "id": "976d56f936e63feb",
        "type": "debug",
        "z": "6262a545dc54b0c6",
        "name": "Day",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 810,
        "y": 840,
        "wires": []
    },
    {
        "id": "1fc532434ca04cfc",
        "type": "debug",
        "z": "6262a545dc54b0c6",
        "name": "Night",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 810,
        "y": 760,
        "wires": []
    },
    {
        "id": "e984b358ca970466",
        "type": "switch",
        "z": "6262a545dc54b0c6",
        "d": true,
        "name": "Night?",
        "property": "dayNight",
        "propertyType": "flow",
        "rules": [
            {
                "t": "eq",
                "v": "0",
                "vt": "num"
            },
            {
                "t": "else"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 485.9999885559082,
        "y": 776.9999885559082,
        "wires": [
            [
                "0570fe421e974355"
            ],
            [
                "073cc37e8c52fa95"
            ]
        ]
    },
    {
        "id": "5ee19e982756d0eb",
        "type": "debug",
        "z": "6262a545dc54b0c6",
        "d": true,
        "name": "Day or Night",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 390,
        "y": 700,
        "wires": []
    },
    {
        "id": "e761a3000063ebd5",
        "type": "change",
        "z": "6262a545dc54b0c6",
        "name": "to flow Day or Night",
        "rules": [
            {
                "t": "move",
                "p": "payload",
                "pt": "msg",
                "to": "dayNight",
                "tot": "flow"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 410,
        "y": 660,
        "wires": [
            [
                "e984b358ca970466"
            ]
        ]
    },
    {
        "id": "da7c683e1802fdfa",
        "type": "function",
        "z": "6262a545dc54b0c6",
        "name": "Light to dz",
        "func": "// prepare payload to dz\nvar w_data =\nmsg.payload.light_topic\n+ '|'\n+ msg.payload.light_state\n+ '|' \n+ msg.payload.motion_topic\n+ '|'\n+ msg.payload.motion_state\n+ '|'\n+ msg.payload.time;\n\nvar w_payload = { \n    command: 'customevent',\n    event: 'LumNR',\n    data: w_data\n    };\n\nmsg.payload = w_payload;\n\nreturn msg",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 830,
        "y": 460,
        "wires": [
            [
                "b715c325798fbfd3",
                "0252414ff9c0e132"
            ]
        ]
    },
    {
        "id": "abd5e88fe39240d3",
        "type": "function",
        "z": "6262a545dc54b0c6",
        "name": "Light Off?",
        "func": "// flow var of the light state \nvar var_light_topic = msg.payload.light_topic + \"_on\";\n\n// get the state of the light (0 = Off ; 1 = On ; 9 = ?)\nvar light_state = flow.get(var_light_topic);\nif (light_state === 'undefined') {\n    light_state = 9\n}\n\n// save of the light state to the payload\nmsg.payload.light_state = light_state;\n\nvar var_motion_topic = msg.payload.light_topic + \"_motion_topic\";\n\nvar motion_topic = msg.topic\n//var motion_topic = msg.topic.split(\"/\")[0] +\n\"/\" + msg.topic.split(\"/\")[1] +\n\"/\" + msg.topic.split(\"/\")[2] + \n\"/\" + msg.topic.split(\"/\")[3] + \n\"/\";\nflow.set(var_motion_topic, motion_topic);\nmsg.payload.motion_topic = motion_topic;\n\n// state of the motion\n// 0 = no motion (not here)\n// 1 = motion to switch On the light (if Off = 0 ; <> 1) => to zwave\n// 2 = motion to keep the light On (if On = 1) => to dz only\nvar var_motion = msg.payload.light_topic + \"_motion\";\n\nif (light_state == 1) {\n    flow.set(var_motion, 2);\n    msg.payload.motion_state = 2;\n    return [msg, null];\n} else {\n    flow.set(var_motion, 1);\n    msg.payload.motion_state = 1;\n    return [null, msg];\n}\n\n",
        "outputs": 2,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 800,
        "y": 100,
        "wires": [
            [
                "f94c8c2c1883bcf7",
                "da7c683e1802fdfa"
            ],
            [
                "c055eaae780d191b",
                "66796159fb3745f2"
            ]
        ]
    },
    {
        "id": "0570fe421e974355",
        "type": "change",
        "z": "6262a545dc54b0c6",
        "name": "Night",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "Night",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 645.9999885559082,
        "y": 756.9999885559082,
        "wires": [
            [
                "1fc532434ca04cfc"
            ]
        ]
    },
    {
        "id": "073cc37e8c52fa95",
        "type": "change",
        "z": "6262a545dc54b0c6",
        "name": "Day",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "Day",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 645.9999885559082,
        "y": 816.9999885559082,
        "wires": [
            [
                "976d56f936e63feb"
            ]
        ]
    },
    {
        "id": "f90f2b75ae58f99b",
        "type": "debug",
        "z": "6262a545dc54b0c6",
        "name": "Motion Portillon",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 620,
        "y": 380,
        "wires": []
    },
    {
        "id": "279a24a53e1b9f3f",
        "type": "switch",
        "z": "6262a545dc54b0c6",
        "d": true,
        "name": "Détecteur Portillon",
        "property": "topic",
        "propertyType": "msg",
        "rules": [
            {
                "t": "cont",
                "v": "zwave/99",
                "vt": "str"
            }
        ],
        "checkall": "false",
        "repair": false,
        "outputs": 1,
        "x": 390,
        "y": 380,
        "wires": [
            [
                "f90f2b75ae58f99b"
            ]
        ]
    },
    {
        "id": "35f3ae39d3073cb5",
        "type": "mqtt-broker",
        "name": "local",
        "broker": "host.docker.internal",
        "port": "1883",
        "clientid": "",
        "autoConnect": true,
        "usetls": false,
        "protocolVersion": "4",
        "keepalive": "60",
        "cleansession": true,
        "birthTopic": "",
        "birthQos": "0",
        "birthPayload": "",
        "birthMsg": {},
        "closeTopic": "",
        "closeQos": "0",
        "closePayload": "",
        "closeMsg": {},
        "willTopic": "",
        "willQos": "0",
        "willPayload": "",
        "willMsg": {},
        "sessionExpiry": ""
    }
]
DzVents
à suivre....

hestia
Messages : 240
Enregistré le : 12 sept. 2018, 22:36

Re: smart light Node-RED+zwave+dz

Message par hestia »

Le script dzVents

Code : Tout sélectionner

--[[
A dzVents script as a part off a lighting management system, based on motion sensors, switches and lights

* The architecture

motion sensors-------|                                |-----Domoticz
                     |                                |
                     |-----zwavejs2mqtt------mqtt-----| 
                     |                                |
lights-switches------|                                |-----Node-RED

The Node-RED part (NR) is triggered by the motion sensors and switches on the lights
The Domoticz (dz) part manages the switch off of the lights

NR is more reactive than dz and allows to switch on the lights faster than the dz event management (that could be slowed sometimes with backup or any long script)
Few functions in NR to keep it lite and reactive

The tricky part was the "ids" of the "devices" (not the same in zwavejs2mqtt and dz) to avoid to duplicate
The translation is done in dz between the topic (mqtt from NR) and the ID (dz Hardware)

With my zwave devices, it's the same device for the switch and the light (to be adapted for lights with separate switches)

Another script computes the solar radiation (lux) value to give to NR: 0 = night ; 2 = day ; 1 = day for sensors, night for lights

* The functions
Several sensors and several lights, the lights could be switched on by their switch or the assigned motion sensors
After a delay they are switched off ; there is a delay for motion sensors (short) and another for the switches (longer)
Any movement restart count down of the delay to the switch off

A motion sensors could switch on several lights

The lights could be on if it's night, the level is not the same for the sensors and the switches
A person could decided to switch on even if there is not enouhg light to allow a motion sensor to switch on
More light is needed to read than to walk ;-)

When lights were switched on by sensors they could be switched off by any switch with the light on

There is also a double-check to be sure the lights are really off, in case of loss of trigger or other issue (but not in case of issues with the zwave network)

Another tricky part is that zwave gives several messages when a light is switched on and off,
a specific function was done to filter the duplicates (to be tune regarding the network)

The inital system was started with dz only in 2019 and was splitted in 2022 between dz and NR

* Changes
14/02/2022:  use Node-RED to accelarate the detection of motions and switches, send info to dz with customEvents
18/03/2022:  fix bug: attempt to concatenate a nil value (local 'f_deviceMSX')
02/04/2022:  switch off the light if no longer a motion sensor
08/04/2022:  comments + switch off the light if timeOff in the past when the motion sensor is stuck on 'On' since too much time
09/04/2022:  add dimto 0 to switch off dimmer, to avoid a dimmer to stay ON with low % (issue encountered), but not solve => comment on 11/04/2022
25/04/2022:  add a control for duplicate after issue incountered
27/04/2022:  change DUPLICATE_TIMEOUT_L_SEC from 15 to 35 after issue #12 happened with 29s

Pershaps some errors at the begining when the lua tables are not initiated (difficult to test all the cases, to switch on and off and move to eliminate the errors)
--]]

-- triggers
local MQTTEVENT_NR = 'LumNR' -- from Node-RED
local DEOL_DEV = 2487 -- dummy switch to pilot the on time (Dummy End Of Light)
local TIME_INTERVAL = 'every 5 minutes' -- period to double-check if there is any light to switch off, in case of something went wrong

-- duration in seconds
local MAX_MOTION_SEC = 100 --  seconds ; time to keep lights On when switched on by MOTION
local MAX_SWITCH_SEC = 1800 -- seconds ; time to keep lights On when switched on by switch / bigger than MAX_MOTION_SEC!
local MOTION_TIMEOUT_SEC = 300 -- seconds ; time to consider the motion sensor is stuck on 'On', to avoid light to stay On w no end
local MOTION_MARGING_SEC = 5 -- seconds ; time to consider the motion sensor 'On' if just switched 'Off'
local DUPLICATE_TIMEOUT_S_SEC = 6 -- short delay to wait to avoid "duplicated" messages fron Node-RED (several messages with zwaves) -- 15/04/2022
local DUPLICATE_TIMEOUT_L_SEC = 35 -- long delay to wait to avoid "duplicated" messages fron Node-RED (several messages with zwaves)

local DAY_NIGHT_DEV = 2454  -- dummy device to give if it's day or night, to be updated with another script that send the info to Node-RED
                            -- 0: night ; 1: day for motion sensor, night for manual switch ; 2: day 

-- log options
local LOG_DEBUG = 0         -- 0 =>ERROR / 1 => FORCE / 2 => DEBUG
local LOG_LEVEL
local LOGGING
if LOG_DEBUG == 2 then
    LOGGING = domoticz.LOG_DEBUG
    LOG_LEVEL = domoticz.LOG_DEBUG
elseif LOG_DEBUG == 1 then
    LOGGING = domoticz.LOG_FORCE
    LOG_LEVEL = domoticz.LOG_FORCE
else
    LOGGING = domoticz.LOG_ERROR
    LOG_LEVEL = domoticz.LOG_INFO
end


return {
    logging =   {
                level =   LOGGING
                },
            
    on =        {
                customEvents = {MQTTEVENT_NR},
                devices = {DEOL_DEV},
                timer = {TIME_INTERVAL}
  	            },
  	        
    data =      {
        		lightStateNR = {initial={}},    -- last state of the lights with NR id
        		timeNR = {initial={}},          -- times previous topic with NR id
        		lightState = {initial={}},      -- last state of the lights with dz id
        		dupTimeOut = {initial={}},      -- true if a previous issue w NR data
        		motionId = {initial={}},        -- last motion sensor that trigger the lights
        		deviceMSX = {initial={}},       -- 'M' On from a Motion, 'S' from a switch ; 'X' Off
          		timeOn = {initial={}}           -- last On time or last state change if Off
        	    },
        
    execute = function(dz, item, triggerInfo)
    _G.logMarker =  dz.moduleLabel -- set logmarker to scriptname   
	local _u = dz.utils
	
    local l_forSecOff = 99999
    local l_nextTimeOff
    
    -- tables, no objects
    local l_lightStateNR = dz.data.lightStateNR
    local l_timeNR = dz.data.timeNR
    local l_lightState = dz.data.lightState
    local l_motionId = dz.data.motionId
	local l_deviceMSX = dz.data.deviceMSX
	local l_timeOn = dz.data.timeOn

	local l_lightId

--- FUNCTIONS BEG --------------------------------------------------------------

local function logWrite(str, level)
    if level == nil then
        level = LOG_LEVEL
    end
    dz.log(tostring(str), level)
end


local function switchTimeOn (p_deviceId, p_motionState)
    --switch on 1 light
    --logWrite('p_deviceId: ' .. p_deviceId)
    local f_onDuration
    
    if not l_deviceMSX[p_deviceId] then
        l_deviceMSX[p_deviceId] = '?'
    end
    
    if p_motionState == 2 then -- motion w light already On
    elseif p_motionState == 1 then -- the light is On due to motion
        l_deviceMSX[p_deviceId]= 'M'
    elseif p_motionState == 0 then -- mo motion, the light is On by switch
        l_deviceMSX[p_deviceId]= 'S'
    else
        logWrite('p_motionState: ' .. p_motionState .. ' unknown', dz.LOG_FORCE)
        l_deviceMSX[p_deviceId]= 'M'
    end 

    --logWrite(p_motionState .. ' ' .. f_onDuration .. ' s')
    l_timeOn[p_deviceId] = dz.time -- last 'on' time
    --logWrite(p_deviceId .. ' ' .. dz.devices(p_deviceId).state .. ' ' .. l_deviceMSX[p_deviceId] .. ' last On ' .. l_timeOn[p_deviceId].rawTime .. ' next off ' .. f_timeOff.rawTime .. ' ' .. dz.devices(p_deviceId).name .. ' motion: ' .. l_motionId[p_deviceId] .. ' ' .. p_motionState, dz.LOG_FORCE)
    logWrite(p_deviceId .. ' ' .. l_deviceMSX[p_deviceId] .. ' last On ' .. l_timeOn[p_deviceId].rawTime .. ' ' .. p_motionState, dz.LOG_FORCE)

end


local function switchTimeOff (p_deviceId)
    l_deviceMSX[p_deviceId]= 'X'
    l_motionId[p_deviceId]= 0 -- remove the motion sensor 
    l_timeOn[p_deviceId] = dz.time
end


local function switchOffLight (p_deviceId)
    
    if dz.devices(p_deviceId).switchType == 'Dimmer' then -- 09/04/2022 -- to avoid a dimmer to stay ON (issue encountered)
        logWrite('dimmer', dz.LOG_DEBUG)
        --dz.devices(p_deviceId).dimTo(0).silent() -- 11/04/2022
    end
    dz.devices(p_deviceId).switchOff().silent()
    switchTimeOff(p_deviceId)
    logWrite('switchOffLight ' .. p_deviceId, dz.LOG_FORCE)
end

local function switchOffAllMotionLight (p_deviceId)
    for f_deviceIdx, _ in pairs(l_deviceMSX) do
        if l_deviceMSX[f_deviceIdx] == 'M' and f_deviceIdx ~= p_deviceId then -- On due to motion, but the the light just Offed
            logWrite('switchOffAllMotionLight ' .. f_deviceIdx)
            switchOffLight(f_deviceIdx)
        end
    end 
end

local function calcTimeOff (p_deviceId, p_deviceMSX, p_timeStart)
-- calculate the time to switch off the light
    --logWrite('calcTimeOff ' .. p_deviceId .. ' ' .. p_deviceMSX .. ' ' .. p_timeStart.raw)
    local f_timeOff
    if p_deviceMSX == 'M' then
        f_timeOff = p_timeStart.addSeconds(MAX_MOTION_SEC)
    elseif p_deviceMSX == 'S' then
        f_timeOff = p_timeStart.addSeconds(MAX_SWITCH_SEC)
    elseif p_deviceMSX == 'X' then
       f_timeOff = dz.time.addDays(1)
    else
        logWrite(p_deviceId .. 'p_deviceMSX unknown: ' .. p_deviceMSX, dz.LOG_ERROR)
    end
    return f_timeOff
end


local function nextTimeOff (p_deviceIdx, p_deviceMSX, p_motionId, p_timeOn, p_timerTrigger)
    --logWrite('nextTimeOff')
    local f_forSec, f_timeOff, f_sec_delta
     -- calculate the time before triggering this script to switch off the next light
     
    f_timeOff = calcTimeOff (p_deviceIdx, p_deviceMSX, dz.time.makeTime(p_timeOn))
    --logWrite(p_deviceIdx .. ' next off / light : ' .. f_timeOff.raw)
    --logWrite('nextTimeOff 0')    
    if dz.time.compare(f_timeOff).compare == -1 then -- in the past
        logWrite(p_deviceIdx .. ' ' .. f_timeOff.raw .. ' past')
        if p_motionId ~= 0 then -- a motion for this light
            local f_motion_lastupdate = dz.devices(p_motionId).lastUpdate.secondsAgo
            logWrite ('p_motionId last On ' .. p_motionId .. ' ' .. f_motion_lastupdate .. ' sec ')
            if dz.devices(p_motionId).active then -- still 'On'
                if f_motion_lastupdate > MOTION_TIMEOUT_SEC then
                    logWrite(dz.devices(p_motionId).name .. ' ' .. p_motionId .. ' active since ' .. f_motion_lastupdate .. ' sec', dz.LOG_ERROR)
                    switchOffLight(p_deviceIdx) -- 08/04/2022                    
                else
                    --logWrite('nextTimeOff 1.1')
                    f_timeOff = calcTimeOff (p_motionId, p_deviceMSX, dz.time)
                end
            elseif f_motion_lastupdate < MOTION_MARGING_SEC then -- just went 'Off'
                --logWrite('nextTimeOff 1.2')
                f_timeOff = calcTimeOff (p_motionId, p_deviceMSX, dz.devices(p_motionId).lastUpdate)
                
            else
                --logWrite('nextTimeOff 2')
                switchOffLight(p_deviceIdx)
                if p_timerTrigger then -- triggered by a timer, if stil On, then the EOL trigger was lost
                    logWrite('nextTimeOff ' .. p_deviceIdx .. ' switched off by timer', dz.LOG_ERROR)
                else
                    logWrite(p_deviceIdx .. ' switched off')
                end
                f_timeOff = dz.time.addDays(1)
            end
        else
            switchOffLight(p_deviceIdx) -- 02/04/2022
        end
    end

    f_sec_delta = dz.time.compare(f_timeOff).secs
    logWrite(p_deviceIdx .. ' next off ' .. f_timeOff.raw .. ' in ' .. f_sec_delta .. ' sec')
    return f_timeOff, f_sec_delta
end


local function nextEndOfLight (p_timerTrigger)
    --return next time and duration to check in order to switch off a light
    --logWrite('nextEndOfLight')
    local f_deviceMSX, f_motionId, f_timeOn, f_timeOff, f_forSec, f_nextTimeOff, f_forSecOff
    f_forSecOff = 99999

    for f_deviceIdx, _ in pairs(l_deviceMSX) do
        logWrite('f_deviceIdx: ' .. f_deviceIdx, dz.LOG_DEBUG)
        f_deviceMSX = l_deviceMSX[f_deviceIdx]  -- 'M' On from a Motion, 'S' from a switch ; 'X' Off
        f_motionId = l_motionId[f_deviceIdx]    -- last motion sensor that stays this device On ; 0 if none
		f_timeOn = l_timeOn[f_deviceIdx]        -- last On time or last state change if Off
		
		if f_timeOn == nil then f_timeOn = dz.time.addDays(1) end
		logWrite('nextEndOfLight ' .. f_deviceIdx, dz.LOG_DEBUG)
		if f_motionId == nil then
		    logWrite('nextEndOfLight ' .. f_deviceIdx .. ' motionId nil !!!!!', dz.LOG_FORCE)
		    motionId = 0
		else
		    logWrite(f_deviceIdx .. ' MSX: ' .. f_deviceMSX .. ' motionId: ' .. f_motionId .. ' timeOn: ' .. f_timeOn.rawTime, dz.LOG_DEBUG)
		end
	    f_timeOff, f_forSec = nextTimeOff (f_deviceIdx, f_deviceMSX, f_motionId, f_timeOn, p_timerTrigger)
	    if f_forSec < f_forSecOff then -- this time is before the earlier ones
	        f_forSecOff = f_forSec
	        f_nextTimeOff = f_timeOff
	    end
    end

    --logWrite('nextEndOfLight ' .. f_nextTimeOff.raw .. ' in ' .. f_forSecOff .. ' sec')
    return f_nextTimeOff, f_forSecOff
end


local function checkStateConsistance ()
    for f_deviceIdx, _ in pairs(l_deviceMSX) do
        if dz.devices(f_deviceIdx) ~= nil then
            if dz.devices(f_deviceIdx).active then
                if l_deviceMSX[f_deviceIdx] == 'X' then
                    switchTimeOn(f_deviceIdx, 1)
                    logWrite(f_deviceIdx .. ' state issue ' .. dz.devices(f_deviceIdx).state .. ' MSX ' .. l_deviceMSX[f_deviceIdx], dz.LOG_ERROR)
                end
            else
                logWrite('checkStateConsistance f_deviceIdx: ' .. f_deviceIdx .. ' OK')        
            end
        else
            logWrite('device ' .. f_deviceIdx .. ' no longer exists', dz.LOG_FORCE) 
            l_deviceMSX[f_deviceIdx] = nil
        end
    end
end


local function selectTableDeviceStatus (p_id)
    local _,rc = _u.osCommand('which sqlite3')

    if rc == 0 then
        local f_cmd, f_result
            f_cmd = 'sudo sqlite3  -list -noheader domoticz.db "SELECT ID FROM DeviceStatus WHERE HardwareID = 31 and Used = 1 and Type = 244 and DeviceID LIKE \'' .. p_id .. '%\' ;" '
            f_result = _u.osCommand(f_cmd)
             
            --logWrite(f_cmd)
            --logWrite(f_result)

            f_result = tonumber(f_result)
            if f_result == nil then
                logWrite('selectTableDeviceStatus ' .. p_id .. ' no DeviceID', dz.LOG_ERROR)
                return -1
            else
                logWrite('selectTableDeviceStatus ' .. p_id .. ' ' .. f_result, dz.LOG_DEBUG)
                return f_result
            end
    else
        logWrite(p_table .. ' sqlite3 not available. Please make sure it is installed (sudo apt install sqlite3) and accessible', dz.LOG_ERROR)
        return -1
    end
end


local function getDeviceIdx (p_deviceTopic)
    logWrite('getDeviceIdx p_deviceTopic: ' .. p_deviceTopic)
    local f_deviceIDlike
    f_deviceIDlike = p_deviceTopic:gsub('zwave/','zwavejs2mqtt_0xe236465e_')
    f_deviceIDlike = f_deviceIDlike:gsub('/','-')

    local f_deviceIdx = selectTableDeviceStatus(f_deviceIDlike)
    
    return f_deviceIdx
end


local function getDzIDs (p_dataTable)
    logWrite('getDzIDs')
    -- p_dataTable => zwave/17/38/0/|0|zwave/126/48/0/Motion|0|
    local f_lightId, f_lightState, f_motionId, f_motionState
    
    f_lightId = getDeviceIdx(p_dataTable[1])
    f_lightState = tonumber(p_dataTable[2])
    if p_dataTable[3] == 'undefined' then -- done in Node-RED?
        f_motionId = 0
    else
        f_motionId = getDeviceIdx(p_dataTable[3])
    end
    f_motionState = tonumber(p_dataTable[4])
    if f_lightState == nil then
        logWrite('light state nil', dz.LOG_ERROR)
    end
    if f_motionState == nil then
        logWrite('motion state nil', dz.LOG_ERROR)
    end
    
    --logWrite('light Id: ' .. f_lightId .. ' state ' ..  f_lightState .. ' motion Id: ' .. f_motionId .. ' motion ' .. f_motionState, dz.LOG_FORCE)
    l_lightState[f_lightId] = f_lightState
    l_motionId[f_lightId] = f_motionId
    --l_motionState[f_lightId] = f_motionState

    return f_lightId, f_lightState, f_motionId, f_motionState, f_time

end

local function checkNoDuplicate (p_dataTable)
    -- p_dataTable ex: zwave/17/38/0/|0|zwave/126/48/0/Motion|0|1645215895499
    logWrite('checkNoDuplicate', dz.LOG_DEBUG)

--[[
#   prévious	current		current		action		comment
    light		light		motion
    state		state		state
1   0	->  	0   		1   		err	    	the light is off, no risk to stop
2   0	->  	0   		2   		err	    	the light is off, no risk to stop
3   1	->  	0   		1   		err	    	the light is off, no risk to stop
4   1	->  	0   		2   		err	    	the light is off, no risk to stop
5   0	->  	1   		2   		err + go    the light is on, better to continue if on for real
6   1	->  	1   		1   		err + go	the light is on, better to continue if on for real
7   1	->  	0   		0   		ok	    	the light was switched off (sbdy or dz) ; check to off for sure
8   0	->  	1   		0   		ok	    	the light was switched on (sbdy or dz) ; duplicate if short time --15/04/2022
9   0	->  	1   		1   		ok	    	a motion switched on the light
10  1	->  	1   		2   		ok	    	a motion on a light already on
11  0	->  	0   		0	    	err         should not happen, add a timeout
12  1	->  	1   		0	    	stop/go     duplicate if short time, lost msg or context if long time?

state of the motion
0 = no motion (not here)
1 = motion to switch On the light (if Off = 0 ; <> 1) => to zwave
2 = motion to keep the light On (if On = 1) => to dz only
9 = undefined

state of the light
0 = off
1 = on


]]--
    
    local f_lightNR, f_lightState, f_motionState, f_timeNR
    local f_lightStatePrev, f_timeNRprev
    local f_go, f_dupTimeOut
    f_go = false
    
    if dz.data.dupTimeOut[f_lightNR] then -- 25/04/2022
        f_dupTimeOut = true
    else
        f_dupTimeOut = false
    end
    
    f_lightNR = p_dataTable[1] -- the id is the light topic
    
    f_lightState = tonumber(p_dataTable[2])
    f_motionState = tonumber(p_dataTable[4]) or 0
    if f_lightState == nil or f_motionState == nil then
        logWrite('data issue' .. item.data, dz.LOG_ERROR)
    end
    
    f_timeNR = p_dataTable[5]
    if f_timeNR == nil then
        logWrite('f_timeNR = nil ' .. item.data, dz.LOG_ERROR)
    else
        logWrite('currentNR: ' .. f_lightNR .. ' state ' .. f_lightState .. ' motion state ' .. f_motionState .. ' time ' .. f_timeNR, dz.LOG_DEBUG)
    end
    f_lightStatePrev = l_lightStateNR[f_lightNR]
    f_timeNRprev = l_timeNR[f_lightNR]
    --logWrite('previousNR ' .. l_lightStateNR .. ' time ' .. f_timeNRprev)

    if f_timeNRprev ~= nil then
        f_timeGap = _u.round((f_timeNR - f_timeNRprev) / 1000)
        
        if f_timeGap < 0 then
            logWrite('NR Time ERROR: curent ' .. f_timeNR .. ' prev ' .. f_timeNRprev .. ' gap ' ..f_timeGap, dz.LOG_ERROR)
            f_go = true
        elseif f_dupTimeOut and f_timeGap < DUPLICATE_TIMEOUT_S_SEC then -- an error just previously
            f_go = false
            logWrite('dupTimeOut=' .. f_dupTimeOut .. ' timeGap=' .. f_timeGap .. ' stop', dz.LOG_FORCE)       
        else
            f_dupTimeOut = false
            logWrite(f_lightNR .. ' prev state ' .. f_lightStatePrev .. ' current ' .. f_lightState .. ' motion ' .. f_motionState .. ' time ' .. f_timeNR, dz.LOG_DEBUG)
            
            if f_lightState == 0 and f_motionState ~= 0 then    -- # 1, 2, 3, 4
                logWrite('1 2 3 4 ' .. f_lightNR .. ' ' .. f_lightState .. ' motion ' .. f_motionState, dz.LOG_ERROR)
                
            elseif (f_lightStatePrev == 0 and f_lightState == 1 and f_motionState == 2) or  -- # 5
                    (f_lightStatePrev == 1 and f_lightState == 1 and f_motionState == 1) then   -- # 6
                logWrite('5 ' .. f_lightNR .. ' prev ' .. f_lightStatePrev .. ' current ' .. f_lightState .. ' motion ' .. f_motionState, dz.LOG_ERROR)
                f_go = true

            elseif f_lightStatePrev == 0 and f_lightState == 1 and f_motionState == 0 then -- # 8 (15/04/2022)
                --if f_timeGap < DUPLICATE_TIMEOUT_S_SEC then    -- # 8 stop
                    --logWrite('8 ' .. f_lightNR .. ' prev ' .. f_lightStatePrev .. ' current ' .. f_lightState .. ' motion ' .. f_motionState .. '  ' .. f_timeGap .. 's', dz.LOG_FORCE)
                --else    -- # 8 go
                    logWrite('go 8 ' .. f_lightNR .. ' prev ' .. f_lightStatePrev .. ' current ' .. f_lightState .. ' motion ' .. f_motionState .. '  ' .. f_timeGap .. 's', dz.LOG_FORCE)
                    f_go = true
                --end
    
            elseif f_lightStatePrev == 0 and f_lightState == 0 and f_motionState == 0 then  -- # 11
                logWrite('11 ' .. f_lightNR .. ' prev ' .. f_lightStatePrev .. ' current ' .. f_lightState .. ' motion ' .. f_motionState, dz.LOG_FORCE)
                f_dupTimeOut = true
                
            elseif f_lightStatePrev == 1 and f_lightState == 1 and f_motionState == 0 then
                if f_timeGap < DUPLICATE_TIMEOUT_L_SEC then    -- # 12 stop
                    logWrite('12 ' .. f_lightNR .. ' prev ' .. f_lightStatePrev .. ' current ' .. f_lightState .. ' motion ' .. f_motionState .. '  ' .. f_timeGap .. 's', dz.LOG_FORCE)
                else    -- # 12 go
                    logWrite('go 12 ' .. f_lightNR .. ' prev ' .. f_lightStatePrev .. ' current ' .. f_lightState .. ' motion ' .. f_motionState .. '  ' .. f_timeGap .. 's', dz.LOG_FORCE)
                    f_go = true
                end
            else    -- # 7, 9, 10
                logWrite('7 9 10', dz.LOG_DEBUG)
                f_go = true
            end
        end
    else       
        f_go = true
        logWrite('No previous data for ' .. f_lightNR, dz.LOG_FORCE)
    end
    logWrite('checkNoDuplicate: ' .. tostring(f_go))
    if not f_go then
        logWrite(f_lightNR .. ' prev ' .. f_lightStatePrev .. ' current ' .. f_lightState .. ' motion ' .. f_motionState .. ' [duplicate]', dz.LOG_FORCE)
    end
    -- save for the next run, also if duplicate topic with the light topic as id (ex: zwave/17/38/0)
    dz.data.lightStateNR[f_lightNR] = f_lightState
    dz.data.timeNR[f_lightNR] = f_timeNR
    dz.data.dupTimeOut[f_lightNR] = f_dupTimeOut

    return f_go
end

--- FUNCTIONS END ----------------------------------------------------------------------------------------------------------------------------------------------

---     MAIN      ----------------------------------------------------------------------------------------------------------------------------------------------

    local w_lightId, w_lightState, w_motionId, w_motionState
    local stopScript = false

    if item.isCustomEvent then
        logWrite('=> triggered by ' .. item.trigger .. ' ' .. item.data, dz.LOG_FORCE)
        local w_dataTable = _u.stringSplit(item.data,'|')
        if checkNoDuplicate(w_dataTable) == false then
            stopScript = true
            --logWrite('stopScript1 = true')
        end
        if not stopScript then        
            w_lightId, w_lightState, w_motionId, w_motionState = getDzIDs (w_dataTable)
            
            if w_lightId == -1 or w_motionId == -1 then
                stopScript = true
                --logWrite('stopScript2 = true')
            end
        end
        if not stopScript then
 
             -- MOTION
            if w_motionState ~= 0 then
                logWrite('MOTION light: ' .. w_lightId .. ' ' .. dz.devices(w_lightId).name .. ' state ' ..  w_lightState .. ' motion ' .. w_motionId .. ' ' .. dz.devices(w_motionId).name .. ' state ' .. w_motionState, dz.LOG_FORCE)
     
                switchTimeOn(w_lightId, w_motionState)
    
            -- SWITCH --    
            else -- switch with no motion
                if w_motionId ~= 0 and w_motionId ~= nil then
                    logWrite('LIGHT light: ' .. w_lightId .. ' ' .. dz.devices(w_lightId).name .. ' state ' ..  w_lightState .. ' motion ' .. w_motionId .. ' ' .. dz.devices(w_motionId).name .. ' state ' .. w_motionState, dz.LOG_FORCE)
                else
                    logWrite('LIGHT light: ' .. w_lightId .. ' ' .. dz.devices(w_lightId).name .. ' state ' ..  w_lightState, dz.LOG_FORCE)
                end
                if w_lightState ~= 0 then -- On
                    
                    if dz.devices(DAY_NIGHT_DEV).sensorValue < 2 then -- night for switch
                        logWrite('night for light')
                        switchTimeOn (w_lightId, w_motionState)
                    else -- day => to switch off the light
                        logWrite('day for light')
                        switchOffLight(w_lightId)                    
                    end
            
                else
                    if l_deviceMSX[w_lightId] == 'M' then -- was On by a motion sensor
                        switchOffAllMotionLight(w_lightId) -- switch Off all the light On by a sensor
                    end
                    switchTimeOff(w_lightId)
                end

            end -- SWITCH

        end -- not stopScript
    
    elseif item.isTimer then
        logWrite('=> triggered by ' .. triggerInfo.type, dz.LOG_FORCE)
        checkStateConsistance()

        if dz.devices(DEOL_DEV).active and dz.devices(DEOL_DEV).lastUpdate.secondsAgo < MAX_SWITCH_SEC then
            logWrite('timer w DEOL on')
            stopScript=true -- not necessary to check if the lights are off if it will be done by the DEOL
        end
    elseif item.isDevice then
        logWrite('=> triggered by ' .. triggerInfo.type .. ' ' .. item.state, dz.LOG_FORCE)
    else
        logWrite('=> triggered by ' .. triggerInfo.type .. ' undefined', dz.LOG_ERROR)    
    end

    if not stopScript then

        -- calculate the next time to turn off the next light and turn off thoses which should be
        l_nextTimeOff, l_forSecOff = nextEndOfLight(item.isTimer)
        if l_forSecOff ~= 99999 then -- not one of the first time for the script
            logWrite('l_nextTimeOff ' .. l_nextTimeOff.rawTime .. ' l_forSecOff ' .. l_forSecOff, dz.LOG_DEBUG)
    
            -- activate a dummy device to trigger the script to switch off the next light
            if l_forSecOff ~= 86400 then
                    logWrite('Next check at ' .. l_nextTimeOff.raw .. ' in ' .. l_forSecOff .. ' sec', dz.LOG_FORCE)
                    if dz.devices(DEOL_DEV).active then
                        dz.devices(DEOL_DEV).cancelQueuedCommands()
                    else
                        dz.devices(DEOL_DEV).switchOn().silent()
                    end
                    dz.devices(DEOL_DEV).switchOff().afterSec(l_forSecOff)
    
            else
                logWrite('All is switched off',  LOG_LEVEL)
                if dz.devices(DEOL_DEV).active then
                    dz.devices(DEOL_DEV).cancelQueuedCommands()
                    dz.devices(DEOL_DEV).switchOff().silent()
                end
            end
        else
            logWrite('No next check for the script, new run, no values yet', dz.LOG_FORCE)
        end
        
    -- save for the next run with the light idx as id (ex: 1981)
        dz.data.deviceMSX = l_deviceMSX
        dz.data.lightState = l_lightState
        dz.data.motionId = l_motionId
        dz.data.timeOn = l_timeOn

    end
    
    logWrite('FIN')
    
end
}
Cette solution est basée sur zwavejs2mqtt, et pourrait être adaptée à d'autres protocoles...

Répondre