Lighting controller ideas

As a basic framework, I was intending to inject (possibly by the ui) different scenarios as dynamic schedules, which would then stop/start an additional cron+ node that emits on or off every 30 seconds, which would then be passed to the garden lights using MQTT.
The big flaw, is that if I change a dynamic schedule, there is no initial output, as the timed event may have occurred at an earlier time, and therefore the existing '30 second' output continues regardless.

[{"id":"0e0d405c4867eb2e","type":"cronplus","z":"f8cf7ea5344d6287","name":"schedule","outputField":"payload","timeZone":"","persistDynamic":true,"commandResponseMsgOutput":"output1","outputs":1,"options":[{"name":"schedule1","topic":"schedule1","payloadType":"default","payload":"","expressionType":"solar","expression":"0 * * * * * *","location":"53.50993548027587 -1.0997486114501953","offset":"106","solarType":"selected","solarEvents":"morningGoldenHourEnd"}],"x":295,"y":2680,"wires":[["51ace23ab19da439","eba261c67c41cf78"]]},{"id":"51ace23ab19da439","type":"cronplus","z":"f8cf7ea5344d6287","name":"overide","outputField":"payload","timeZone":"","persistDynamic":true,"commandResponseMsgOutput":"output1","outputs":1,"options":[{"name":"overide","topic":"relayboard/relay2","payloadType":"str","payload":"on","expressionType":"cron","expression":"0/30 * * * * * *","location":"","offset":"0","solarType":"all","solarEvents":"sunrise,sunset"}],"x":495,"y":2660,"wires":[["12149f56fdf9a1db"]]},{"id":"eba261c67c41cf78","type":"debug","z":"f8cf7ea5344d6287","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":480,"y":2625,"wires":[]},{"id":"6ee32238224b239a","type":"inject","z":"f8cf7ea5344d6287","name":"autoOn","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[{\"command\":\"remove-all-dynamic\"},{\"command\":\"add\",\"topic\":\"start\",\"name\":\"autoOn\",\"payloadType\":\"str\",\"payload\":\"overide\",\"limit\":null,\"expressionType\":\"solar\",\"solarType\":\"selected\",\"solarEvents\":\"civilDusk\",\"location\":\"53.47300061858879 -1.0548551380634308\",\"offset\":\"0\"},{\"command\":\"add\",\"topic\":\"stop\",\"name\":\"autoOff\",\"payloadType\":\"str\",\"payload\":\"overide\",\"limit\":null,\"expressionType\":\"solar\",\"solarType\":\"selected\",\"solarEvents\":\"civilDusk\",\"location\":\"53.47300061858879 -1.0548551380634308\",\"offset\":\"60\"}]","payloadType":"json","x":125,"y":2665,"wires":[["0e0d405c4867eb2e"]]},{"id":"da9eac8284c3a9f0","type":"inject","z":"f8cf7ea5344d6287","name":"dusk","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[{\"command\":\"remove-all-dynamic\"},{\"command\":\"add\",\"topic\":\"start\",\"name\":\"duskStart\",\"payloadType\":\"str\",\"payload\":\"overide\",\"limit\":null,\"expressionType\":\"solar\",\"solarType\":\"selected\",\"solarEvents\":\"civilDusk\",\"location\":\"53.47300061858879 -1.0548551380634308\",\"offset\":\"0\"},{\"command\":\"add\",\"topic\":\"stop\",\"name\":\"duskEnd\",\"payloadType\":\"str\",\"payload\":\"overide\",\"limit\":null,\"expressionType\":\"solar\",\"solarType\":\"selected\",\"solarEvents\":\"civilDawn\",\"location\":\"53.47300061858879 -1.0548551380634308\",\"offset\":\"0\"}]","payloadType":"json","x":125,"y":2700,"wires":[["0e0d405c4867eb2e"]]},{"id":"12149f56fdf9a1db","type":"debug","z":"f8cf7ea5344d6287","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":650,"y":2660,"wires":[]}]

Thanks Dave, I had not considered a FSM, and will have a good look this evening.
(I've been told that I've got to paint a room today :cold_sweat:).

FSMs are a great way to deal with sequential events.
Here's a pictorial view of the very simple state machine for my Home Lighting system.
FSM_house_lighting
S0 - system in 'automatic' mode
S1 - system in 'manual' mode and all lights are OFF
S2 - system in 'manual' mode and all lights are ON

Pressing the various buttons forces a transition according to the diagram.

You dont need the 30s pulse Paul.

This might put you on the right track...

[{"id":"6ee32238224b239a","type":"inject","z":"62a0e5a5.fc59bc","name":"create Dusk-Dusk+1h","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[{\"command\":\"remove-all-dynamic\"},{\"command\":\"add\",\"topic\":\"start\",\"name\":\"autoOn\",\"payloadType\":\"str\",\"payload\":\"on\",\"limit\":null,\"expressionType\":\"solar\",\"solarType\":\"selected\",\"solarEvents\":\"civilDusk\",\"location\":\"53.47300061858879 -1.0548551380634308\",\"offset\":\"0\"},{\"command\":\"add\",\"topic\":\"stop\",\"name\":\"autoOff\",\"payloadType\":\"str\",\"payload\":\"off\",\"limit\":null,\"expressionType\":\"solar\",\"solarType\":\"selected\",\"solarEvents\":\"civilDusk\",\"location\":\"53.47300061858879 -1.0548551380634308\",\"offset\":\"60\"}]","payloadType":"json","x":760,"y":1220,"wires":[["c52c9f72081d0bca"]]},{"id":"a04c11f75d25b278","type":"inject","z":"62a0e5a5.fc59bc","name":"Manual On","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"on","payloadType":"str","x":720,"y":1260,"wires":[["9423851524e2d7c1"]]},{"id":"7605bdb19280141d","type":"inject","z":"62a0e5a5.fc59bc","name":"Manual Off","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"off","payloadType":"str","x":720,"y":1300,"wires":[["9423851524e2d7c1"]]},{"id":"9423851524e2d7c1","type":"debug","z":"62a0e5a5.fc59bc","name":"To MQTT","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":1540,"y":1200,"wires":[]},{"id":"c52c9f72081d0bca","type":"cronplus","z":"62a0e5a5.fc59bc","name":"schedule","outputField":"payload","timeZone":"","persistDynamic":true,"commandResponseMsgOutput":"output2","outputs":2,"options":[],"x":1060,"y":1200,"wires":[["9423851524e2d7c1"],[]]},{"id":"666e44e6646803c6","type":"inject","z":"62a0e5a5.fc59bc","name":"PIR signal","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"true","payloadType":"bool","x":720,"y":1080,"wires":[["1309559d26b658c0"]]},{"id":"ae3d321225f6ab1b","type":"debug","z":"62a0e5a5.fc59bc","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1555,"y":1060,"wires":[],"l":false},{"id":"7b98c8c3c4510dd4","type":"group","z":"62a0e5a5.fc59bc","name":"Test for Day / Night time","style":{"fill":"#bfdbef","fill-opacity":"0.78","label":true,"color":"#000000"},"nodes":["1309559d26b658c0","4137ccddc21bb441","e8a5d153a305ec81","06f7ff3dc13a233f","7bf0a9108416ec08"],"env":[],"x":794,"y":1019,"w":672,"h":122},{"id":"1309559d26b658c0","type":"change","z":"62a0e5a5.fc59bc","g":"7b98c8c3c4510dd4","name":"Get Solar Status","rules":[{"t":"set","p":"payload","pt":"msg","to":"[     {         \"command\": \"describe\",         \"expressionType\": \"solar\",         \"solarType\": \"selected\",         \"solarEvents\": \"civilDusk,civilDawn\",         \"location\": \"53.47300061858879 -1.0548551380634308\"     } ]","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":900,"y":1080,"wires":[["4137ccddc21bb441"]]},{"id":"4137ccddc21bb441","type":"cronplus","z":"62a0e5a5.fc59bc","g":"7b98c8c3c4510dd4","name":"describe","outputField":"payload","timeZone":"","persistDynamic":true,"commandResponseMsgOutput":"output2","outputs":2,"options":[],"x":1060,"y":1080,"wires":[[],["e8a5d153a305ec81"]]},{"id":"e8a5d153a305ec81","type":"switch","z":"62a0e5a5.fc59bc","g":"7b98c8c3c4510dd4","name":"","property":"payload.result.nextEvent","propertyType":"msg","rules":[{"t":"eq","v":"civilDusk","vt":"str"},{"t":"eq","v":"civilDawn","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":1210,"y":1080,"wires":[["06f7ff3dc13a233f"],["7bf0a9108416ec08"]]},{"id":"06f7ff3dc13a233f","type":"change","z":"62a0e5a5.fc59bc","g":"7b98c8c3c4510dd4","name":"is Day time","rules":[{"t":"set","p":"payload","pt":"msg","to":"off","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1370,"y":1060,"wires":[["ae3d321225f6ab1b"]]},{"id":"7bf0a9108416ec08","type":"change","z":"62a0e5a5.fc59bc","g":"7b98c8c3c4510dd4","name":"is Night time","rules":[{"t":"set","p":"payload","pt":"msg","to":"on","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1370,"y":1100,"wires":[["9423851524e2d7c1"]]}]

Hi
I have been beta testing MSR you can get release 1.0 here it runs on any system that runs nodejs and you can link multiple HA controllers. so far Vera,ezlo,hubitat,home assistant and others.
It is a logic engine for control of smart devices. and is very powerful.
Take a look
docs
there is support here

1 Like

Any chance you could share the FSM code Dave? My logic is similar to yours in that I separately track whether it is daylight or not (and I also track whether it is "sleep time" or not). However, I'll admit that some of the logic has gotten a bit arcane and could do with some simplification.

The flow I posted above does that part if you are interested.

Thanks Steve, I'm looking at that. But it was Dave's FSM that I was interested in because I'd like to learn more about them. :slight_smile:


I keep track of the sun-rise/set using inject-enhanced. And keep track of the sun and moon positions using the sun-position nodes. These are written to MQTT every 60s

image

Sleep and daylight switches are set using CRON+

Everything is saved to globals as well as MQTT.

Here you go...


I wrote the code years ago, so could do with being updated (when I have some spare time).

Basically I have three flow variables called 'state_register', 'mode' and 'all'.
The 'state_register' can be S0, S1 or S2.
'mode' can be "automatic" or "manual" and 'all' can be "on" or "off".

Here's the NR flow.

[{"id":"3e9f6604.fcbc32","type":"function","z":"48b4f0d6.1b2b4","g":"9f19bc62.be1678","name":"FSM - State Transition Table","func":"if (msg.topic == \"reset\")\n{\n    node.send( {payload: \"turn_lights_off\"} );\n}\n\n\nvar fsm_state = flow.get(\"state_register\") || 0;\n\nvar mode  = flow.get(\"mode\")  || \"automatic\";\nvar all   = flow.get(\"all\")   || \"off\";\nvar timer = flow.get(\"timer\") || \"off\";\n\nswitch (fsm_state)\n   {\n        case \"S0\":\n        if (mode == \"manual\")\n        {\n            fsm_state = \"S1\";\n            flow.set(\"all\", \"off\");\n            node.send( {payload: \"all_on_button\"} );\n        }\n        else\n        {\n            if (timer == \"on\")\n            {\n             flow.set(\"light_command\",\"lights_on\");   \n            }\n            else \n            {\n              flow.set(\"light_command\",\"lights_off\");  \n            }\n            fsm_state = \"S0\";\n        }\n        break;\n       \n        case \"S1\":\n        if (mode == \"automatic\")\n        {\n            fsm_state = \"S0\";\n            flow.set(\"mode\", \"automatic\");\n            node.send( {payload: \"grey_out_buttons\"} );\n        }\n        else if (all == \"on\")\n        {\n            fsm_state = \"S2\";\n            flow.set(\"light_command\",\"lights_on\");\n            node.send( {payload: \"activate_lights\"} );\n        }\n        else\n        {\n            fsm_state = \"S1\";\n        }\n        break;\n       \n        case \"S2\":\n        if (mode == \"automatic\")\n        {   \n            fsm_state = \"S0\";\n            if (timer == \"on\")\n           {\n            flow.set(\"light_command\",\"lights_on\");\n            node.send( {payload: \"activate_lights\"} );\n            node.send( {payload: \"grey_out_buttons\"} );\n           }\n           else\n           {\n            flow.set(\"light_command\",\"lights_off\");\n            node.send( {payload: \"deactivate_lights\"} );\n            node.send( {payload: \"grey_out_buttons\"} );\n           }\n        }\n        else if (all == \"off\")\n        {\n            fsm_state = \"S1\";\n            flow.set(\"light_command\",\"lights_off\");\n            node.send( {payload: \"deactivate_lights\"} );\n           \n        }\n        else\n        {\n            fsm_state = \"S2\";\n        }\n        break;\n       \n   }\n\nflow.set(\"state_register\", fsm_state);\nnode.status({text:\"State register = \" + fsm_state});\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":500,"y":140,"wires":[["aeaf20f9.affd18"]]},{"id":"277b503b.2381a","type":"inject","z":"48b4f0d6.1b2b4","g":"9f19bc62.be1678","name":"Trigger every 3 seconds","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"3","crontab":"","once":true,"onceDelay":"1","topic":"","payload":"1","payloadType":"num","x":190,"y":140,"wires":[["3e9f6604.fcbc32","b8d885b6.09eb98"]]},{"id":"f4a8e0bf.1e14a","type":"inject","z":"48b4f0d6.1b2b4","g":"9f19bc62.be1678","name":"Reset","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":"3","topic":"reset","payload":"true","payloadType":"bool","x":130,"y":260,"wires":[["ed31b0ee.37273","a2214b89.cff708","3e9f6604.fcbc32"]]},{"id":"ed31b0ee.37273","type":"change","z":"48b4f0d6.1b2b4","g":"9f19bc62.be1678","name":"","rules":[{"t":"set","p":"state_register","pt":"flow","to":"S0","tot":"str"},{"t":"set","p":"mode","pt":"flow","to":"automatic","tot":"str"},{"t":"set","p":"all","pt":"flow","to":"off","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":460,"y":260,"wires":[[]]},{"id":"a2214b89.cff708","type":"link out","z":"48b4f0d6.1b2b4","g":"9f19bc62.be1678","name":"initial_reset","links":["32502511.321e02","54ea055.4bf1bfc"],"x":355,"y":220,"wires":[]},{"id":"aeaf20f9.affd18","type":"link out","z":"48b4f0d6.1b2b4","g":"9f19bc62.be1678","name":"button_control","links":["2d42e5b7.3c3cf2","3957f9ee.e0bf0e","5cdb7089.2899d8"],"x":675,"y":140,"wires":[]},{"id":"b8d885b6.09eb98","type":"link out","z":"48b4f0d6.1b2b4","g":"9f19bc62.be1678","name":"System_Clock","links":[],"x":355,"y":180,"wires":[]},{"id":"952dad2d.44e8a8","type":"comment","z":"48b4f0d6.1b2b4","g":"9f19bc62.be1678","name":"Finite State Machine - the clever bit","info":"","x":200,"y":80,"wires":[]},{"id":"dcce51f5.98cd6","type":"comment","z":"48b4f0d6.1b2b4","g":"9f19bc62.be1678","name":"System Clock","info":"","x":150,"y":180,"wires":[]}]

Hope you find it useful.

2 Likes

Thanks Dave, always good to look at other people's code :slight_smile:

Part of the fun of doing this stuff is to learn new or improve old techniques.

1 Like

And so prompted, a new flow for the light by the front door:

image

Output is to MQTT and it updates the Ikea Zigbee plug via Zigbee2MQTT

/** Control a plug based on daylight & sleep settings
 * If either HOME/daylight or HOME/sleep MQTT msgs received,
 * this decides whether the status of the plug should change.
 */

const state = context.get('state', 'file') || ''
let newState = state 

// These are set elsewhere, just read-only here
const homeinfo = global.get('homeInfo', 'file')
const sleep = homeinfo.sleep
const daylight = homeinfo.daylight

// Allow on/off or mqtt device inputs to just update the status
if (msg.topic !== 'zigbee2mqtt/Ikea_Plug_01/set' && msg.topic !== 'zigbee2mqtt/Ikea_Plug_01/state' ) {
    
    // Always off during daylight
    if ( daylight === true ) {
        newState = 'OFF'
    } else { // daylight === false
        // During nighttime, turn off during sleep
        if ( sleep === 'On' ) newState = 'OFF'
        else newState = 'ON'
    }
}

let colour, shape
if ( newState === 'ON' ) {
    colour = 'yellow'
    shape = 'dot'
} else {
    colour = 'grey'
    shape = 'ring'
}
node.status({ fill: colour, shape: shape, text: newState })

// Only send the MQTT update if status changes
if ( state !== newState ) {
    context.set('state', newState, 'file')
    msg.topic = 'zigbee2mqtt/Ikea_Plug_01/set'
    msg.payload = { "state": newState }
    return msg
}

The added complexity is simple to enable the fn node to also act as a quick display of whether the light is on or off. I would generally use a proper dashboard of course.

The HOME topics simply act as an event trigger - I REALLY wish that Node-RED's context/flow/global variables had events attached so that you could have a simple listener node.

1 Like

… of course … :wink::innocent:

1 Like

I made my own if you are interested?

piJZP4h6xK

2 Likes

2 Likes

Yes, I am interested :slight_smile:

Here you go...

[{"id":"3984433f.b0374c","type":"function","z":"553814a2.1248ec","name":"Make Observable","func":"\n/**\n* make properties of `object` Observable\n* @param {string} name  key name to store `object` in `global` or `flow` context\n* @param {object} object  the object to make observable\n*/\nfunction makeObservable(name, object) {\n    let store = null;\n    let nameParts = name.split(\".\");\n    if(nameParts.length < 2) {\n        node.error(\"name must be global.xxx or flow.xxx\", msg);\n        return null;\n    }\n\n    switch (nameParts[0]) {\n        case \"flow\":\n            store = flow;\n            break;\n        case \"global\":\n            store = global;\n            break;\n        default:\n            node.error(\"name must be global.xxx or flow.xxx\", msg);\n            return null;\n    }\n\n    nameParts.shift();//drop the first item\n    name = nameParts.join(\".\");\n\n    if (!object) {\n        object = store.get(name);\n    }\n    if (!object || typeof object != \"object\") {\n        node.error(`msg.payload or '${name}' is not an object`, msg);\n        return null;\n    }\n\n\n    observe(object);\n    store.set(name, object);\n\n    return object;\n\n    /**\n    * @param {string} name\n    * @param {any} value\n    */\n    function notify(name, value) {\n        var m = {\n            topic: name,\n            payload: value\n        }\n        node.send(m);\n    }\n\n    /**\n    * @param {object} obj  the object to make reactive\n    * @param {string} key  the item in the data object\n    */\n    function makeReactive(obj, key) {\n        let val = obj[key];\n        Object.defineProperty(obj, key, {\n            get() {\n                return val;\n            },\n            set(newVal) {\n                val = newVal;\n                notify(name + \".\" + key, val);\n            }\n        })\n    }\n\n    /**\n    * @param {object} obj  the object to observe\n    */\n    function observe(obj) {\n        for (let key in obj) {\n            if (obj.hasOwnProperty(key)) {\n                makeReactive(obj, key);\n            }\n        }\n    }\n}\n\nmsg.payload = makeObservable(msg.topic, msg.payload);\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":2610,"y":480,"wires":[["d3ad2877.7bdec8"]]},{"id":"83d19ad4.6d3768","type":"inject","z":"553814a2.1248ec","name":"Observe global.sensor.values","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"global.sensor.values","payload":"{\"sensor1\":0,\"sensor2\":0,\"sensor3\":0,\"sensor4\":0}","payloadType":"json","x":2260,"y":360,"wires":[["3984433f.b0374c"]]},{"id":"e0229828.fbc678","type":"inject","z":"553814a2.1248ec","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"false","payloadType":"bool","x":2670,"y":620,"wires":[["97b6542.bc93aa8"]]},{"id":"49baae3b.93f0f","type":"inject","z":"553814a2.1248ec","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"true","payloadType":"bool","x":2670,"y":580,"wires":[["97b6542.bc93aa8"]]},{"id":"97b6542.bc93aa8","type":"change","z":"553814a2.1248ec","name":"","rules":[{"t":"set","p":"sensor.values.sensor2","pt":"global","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":2890,"y":600,"wires":[[]]},{"id":"6cff8090.6601f","type":"inject","z":"553814a2.1248ec","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"high","payloadType":"str","x":2190,"y":620,"wires":[["e30a0606.89df38"]]},{"id":"8cc38216.10979","type":"inject","z":"553814a2.1248ec","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"low","payloadType":"str","x":2190,"y":580,"wires":[["e30a0606.89df38"]]},{"id":"e30a0606.89df38","type":"change","z":"553814a2.1248ec","name":"","rules":[{"t":"set","p":"sensor.values.sensor1","pt":"global","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":2430,"y":600,"wires":[[]]},{"id":"a0fba143.0a78a","type":"inject","z":"553814a2.1248ec","name":"Observe flow.plc_values","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"flow.plc_values","payload":"{\"DM0001\":0,\"DM0002\":0,\"DM0003\":0,\"DM0004\":0}","payloadType":"json","x":2250,"y":460,"wires":[["3984433f.b0374c"]]},{"id":"c3d1d0db.df3a8","type":"change","z":"553814a2.1248ec","name":"","rules":[{"t":"set","p":"plc_values.DM0001","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":2420,"y":800,"wires":[[]]},{"id":"d7a4a609.804718","type":"inject","z":"553814a2.1248ec","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"2222","payloadType":"num","x":2190,"y":820,"wires":[["c3d1d0db.df3a8"]]},{"id":"4b832cc0.a11d94","type":"inject","z":"553814a2.1248ec","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"1111","payloadType":"num","x":2190,"y":780,"wires":[["c3d1d0db.df3a8"]]},{"id":"8173e9b.5b56e18","type":"change","z":"553814a2.1248ec","name":"","rules":[{"t":"set","p":"plc_values.DM0005","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":2880,"y":800,"wires":[[]]},{"id":"eba61d84.683d9","type":"inject","z":"553814a2.1248ec","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"66","payloadType":"num","x":2670,"y":820,"wires":[["8173e9b.5b56e18"]]},{"id":"ac24a89f.7fd018","type":"inject","z":"553814a2.1248ec","name":"","props":[{"p":"payload"},{"p":"topic","v":"55","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"55","payloadType":"num","x":2670,"y":780,"wires":[["8173e9b.5b56e18"]]},{"id":"9b185180.5b2b3","type":"inject","z":"553814a2.1248ec","name":"Refresh flow.plc_values","props":[{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"flow.plc_values","x":2240,"y":500,"wires":[["3984433f.b0374c"]]},{"id":"45313bd2.09c144","type":"inject","z":"553814a2.1248ec","name":"Refresh global.sensor.values","props":[{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"global.sensor.values","x":2260,"y":400,"wires":[["3984433f.b0374c"]]},{"id":"d6f4bd71.a2c8e","type":"change","z":"553814a2.1248ec","name":"","rules":[{"t":"set","p":"sensor.values.sensor5","pt":"global","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":2430,"y":680,"wires":[[]]},{"id":"f641e704.8a0338","type":"inject","z":"553814a2.1248ec","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"123.456","payloadType":"num","x":2200,"y":700,"wires":[["d6f4bd71.a2c8e"]]},{"id":"21bf4e02.f248d2","type":"inject","z":"553814a2.1248ec","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"543.210","payloadType":"num","x":2200,"y":660,"wires":[["d6f4bd71.a2c8e"]]},{"id":"698c39c4dd359765","type":"group","z":"553814a2.1248ec","name":"","style":{"stroke":"#ff7f7f","stroke-opacity":"0.86","label":true,"fill":"#ff7f7f","fill-opacity":"0.28"},"nodes":["9df73953f604e906","880a13bd644bad88","eac6e314211ea3f0","d3ad2877.7bdec8","b0703dcf4fb665d8","1d96f8b68f16c3b3","7e2c7bd2158d0f13","7749ee1d409c6e16","103d8b20eec86085","ffcfd386d852e8f3","e2d004abed594f59","82c2e22730c8deb1","1ea1e6eac0f30b6a"],"env":[],"x":2794,"y":279,"w":212,"h":242},{"id":"9df73953f604e906","type":"comment","z":"553814a2.1248ec","g":"698c39c4dd359765","name":"WATCH THIS","info":"","x":2895,"y":440,"wires":[],"l":false},{"id":"880a13bd644bad88","type":"comment","z":"553814a2.1248ec","g":"698c39c4dd359765","name":"WATCH THIS","info":"","x":2895,"y":420,"wires":[],"l":false},{"id":"eac6e314211ea3f0","type":"comment","z":"553814a2.1248ec","g":"698c39c4dd359765","name":"WATCH THIS","info":"","x":2895,"y":420,"wires":[],"l":false},{"id":"d3ad2877.7bdec8","type":"debug","z":"553814a2.1248ec","g":"698c39c4dd359765","name":"","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"topic & \": \" & payload","statusType":"jsonata","x":2890,"y":480,"wires":[]},{"id":"b0703dcf4fb665d8","type":"comment","z":"553814a2.1248ec","g":"698c39c4dd359765","name":"WATCH THIS","info":"","x":2875,"y":420,"wires":[],"l":false},{"id":"1d96f8b68f16c3b3","type":"comment","z":"553814a2.1248ec","g":"698c39c4dd359765","name":"WATCH THIS","info":"","x":2915,"y":420,"wires":[],"l":false},{"id":"7e2c7bd2158d0f13","type":"comment","z":"553814a2.1248ec","g":"698c39c4dd359765","name":"WATCH THIS","info":"","x":2855,"y":400,"wires":[],"l":false},{"id":"7749ee1d409c6e16","type":"comment","z":"553814a2.1248ec","g":"698c39c4dd359765","name":"WATCH THIS","info":"","x":2935,"y":400,"wires":[],"l":false},{"id":"103d8b20eec86085","type":"comment","z":"553814a2.1248ec","g":"698c39c4dd359765","name":"WATCH THIS","info":"","x":2895,"y":400,"wires":[],"l":false},{"id":"ffcfd386d852e8f3","type":"comment","z":"553814a2.1248ec","g":"698c39c4dd359765","name":"WATCH THIS","info":"","x":2895,"y":380,"wires":[],"l":false},{"id":"e2d004abed594f59","type":"comment","z":"553814a2.1248ec","g":"698c39c4dd359765","name":"WATCH THIS","info":"","x":2895,"y":360,"wires":[],"l":false},{"id":"82c2e22730c8deb1","type":"comment","z":"553814a2.1248ec","g":"698c39c4dd359765","name":"WATCH THIS","info":"","x":2895,"y":340,"wires":[],"l":false},{"id":"1ea1e6eac0f30b6a","type":"comment","z":"553814a2.1248ec","g":"698c39c4dd359765","name":"WATCH THIS","info":"","x":2890,"y":320,"wires":[]}]
2 Likes

Thanks everyone for the examples, you've given me lots to think about.
The way forward for me seems to be taking parts from each of the suggestions and build it in a function node, with timing support from Cron+.
It's a lot clearer now :grinning:

Using uibuilder of course :grinning: :sunglasses:

Finally made some progress;

const relayTopic = "relayboard/relay2"
var data = global.get('lighting')    ||   { daylight: true, pir: "0", mode: "auto", dusk: false, button: "0" };

if (msg.topic && msg.topic == 'shellies/gjd-controller/input/0')      { data.pir = msg.payload }
if (msg.topic && msg.topic == 'dusk')                                 { data.dusk = msg.payload }
if (msg.topic && msg.topic == 'mode')                                 { data.mode = msg.payload }
if (msg.topic && msg.topic == 'button')                               { data.button = msg.payload }
if (msg.topic && msg.topic == 'daylight')                             { data.daylight = msg.payload }

// run program only if dark enough for lights
if (data.daylight == false) {
switch (data.mode) {
    case "manual":
        if (data.button == "1") {
            node.send({ payload: "on", topic: relayTopic })
        } else if (data.button == "0" ) {
            node.send({ payload: "off", topic: relayTopic })
        }
    case "auto":
        if (data.dusk == true) {
            node.send({ payload: "on", topic: relayTopic })
        } else if (data.dusk == false && data.pir == "0") {
            node.send({ payload: "off", topic: relayTopic })
        } else if (data.dusk == false && data.pir == "1") {
            node.send({ payload: "on", topic: relayTopic })
        }
    }
}

// save back data to globals
global.set(["lighting.pir", "lighting.dusk", "lighting.mode", "lighting.button", "lighting.daylight"], [data.pir, data.dusk, data.mode, data.button, data.daylight]);

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.