Trigger: state

The Trigger: state node is very similar to the Events: state node. It will trigger when a given entity state changes, but unlike the Events: state, the Trigger: state node has the opportunity to add conditions using UI fields to construct complex tests. This removes much of the need to manage conditional tests using JSONata.

JSONata can be used in several places within this node. However real practical use is quite limited. Although the Trigger node has extensive conditional testing included as UI setting, it does not have access to the $entity() or $entities() function in the conditions. The example given here is rather simplistic and contrived to demonstrate that JSONata can be used, rather than focusing on a genuinue or realistic use-case.

screenshot

Example: Respond every five minutes during the hour before and hour after sunset.

[{"id":"3a2face9747195a7","type":"group","z":"776c027950fc8c3f","name":"Trigger: state node - respond to state changes","style":{"label":true,"color":"#000000"},"nodes":["320b22abb5a9ea96","da88972398c7ed97","c2d9f44ffe436dcc","2144de490d4257d7","94ceadbce4b45cdf","79b056d9779ec8f1","360fe19e1e203539","247cbab5f584ea63","0509fc220ff7735e","8fc293f5015dd3d5"],"x":34,"y":1359,"w":872,"h":302},{"id":"320b22abb5a9ea96","type":"inject","z":"776c027950fc8c3f","g":"3a2face9747195a7","name":"Testing","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\t   \"entity_id\": \"sensor.time_utc\",\t   \"old_state\": {\"state\": \"16:49\"},\t   \"new_state\": {\"state\": \"16:50\"}\t}","payloadType":"jsonata","x":130,"y":1400,"wires":[["da88972398c7ed97"]]},{"id":"da88972398c7ed97","type":"trigger-state","z":"776c027950fc8c3f","g":"3a2face9747195a7","name":"Actions around sunset","server":"","version":4,"inputs":1,"outputs":3,"exposeAsEntityConfig":"","entityId":"sensor.time_utc","entityIdType":"exact","debugEnabled":false,"constraints":[{"targetType":"this_entity","targetValue":"","propertyType":"current_state","propertyValue":"new_state.state","comparatorType":"includes","comparatorValueDatatype":"jsonata","comparatorValue":"(\t/* UTC sunrise & sunset to 5 mins, month average (London UK) */\t/* Jan-Dec, Feb-Nov, Mar-Oct, Apr-Sep, May-Aug, Jun-Jly      */\t    $times:=[[\"07:55\", \"16:20\"],\t             [\"07:15\", \"16:45\"],\t             [\"06:00\", \"17:35\"],\t             [\"05:15\", \"18:30\"],\t             [\"04:30\", \"19:30\"],\t             [\"04:05\", \"20:05\"]];\t\t/* get this month (Jan=0), lookup sun-times table, return sun rise-set for this month */\t    $mtable:=[0..11].$min([$, $abs($-11)]);\t    $mindex:=$now('[M]').$number()-1;\t\t/* get sunset and build array of condition test-times around this */\t    $sunset:=($times[$mtable[$mindex]])[1];\t    $h:=$number($substringBefore($sunset, \":\"));\t    $m:=$number($substringAfter($sunset, \":\"));\t\t/* for span period mins, generate array of times and select every nth */\t    $span:=60;\t    $nth:=5;\t\t    $t:=$h*60+$m-$span;\t    ([$t..$t+$span*2])#$i.(\t        $h:=$floor($/60);\t        $m:=$%60;\t        $i%$nth=0 ? $formatInteger($h,'99') & \":\" & $formatInteger($m, '99');\t    )\t)"}],"customOutputs":[{"messageType":"custom","messageValue":"(\t    $sunset:=$entities('sensor.sun_next_setting').state;\t    $togo:= ($toMillis($sunset)-$millis())/60000~>$floor();\t    $toset:= $togo < 180 ? $togo : $togo-1440;\t    $x:= $toset-25;\t    $pc:= $x<0 and $x>-55 ? $min([$abs($x)*2, 100]) : null;\t\t    {\"topic\": \"Minutes to Sunset\",\t     \"payload\": $toset,\t     \"time\": $entities('sensor.time').state,\t     \"sunset\": $sunset,\t     \"percent\": $pc\t    }\t)","messageValueType":"jsonata","comparatorPropertyType":"always","comparatorPropertyValue":"","comparatorType":"is","comparatorValue":"","comparatorValueDataType":"jsonata"}],"outputInitially":false,"stateType":"str","enableInput":true,"x":300,"y":1440,"wires":[["c2d9f44ffe436dcc"],[],["2144de490d4257d7","247cbab5f584ea63"]]},{"id":"c2d9f44ffe436dcc","type":"debug","z":"776c027950fc8c3f","g":"3a2face9747195a7","name":"Every 5 mins around sunset","active":false,"tosidebar":true,"console":false,"tostatus":true,"complete":"true","targetType":"full","statusVal":"payload","statusType":"auto","x":580,"y":1400,"wires":[]},{"id":"2144de490d4257d7","type":"switch","z":"776c027950fc8c3f","g":"3a2face9747195a7","name":"Sunset Actions","property":"payload","propertyType":"msg","rules":[{"t":"btwn","v":"0","vt":"num","v2":"4","v2t":"num"},{"t":"btwn","v":"10","vt":"num","v2":"14","v2t":"num"},{"t":"btwn","v":"0","vt":"num","v2":"-30","v2t":"num"},{"t":"else"}],"checkall":"false","repair":false,"outputs":4,"x":540,"y":1480,"wires":[["94ceadbce4b45cdf"],["79b056d9779ec8f1"],["360fe19e1e203539"],[]]},{"id":"94ceadbce4b45cdf","type":"debug","z":"776c027950fc8c3f","g":"3a2face9747195a7","name":"At Sunset","active":false,"tosidebar":false,"console":false,"tostatus":true,"complete":"true","targetType":"full","statusVal":"payload","statusType":"auto","x":780,"y":1440,"wires":[]},{"id":"79b056d9779ec8f1","type":"debug","z":"776c027950fc8c3f","g":"3a2face9747195a7","name":"Just Before","active":false,"tosidebar":false,"console":false,"tostatus":true,"complete":"true","targetType":"full","statusVal":"payload","statusType":"auto","x":790,"y":1500,"wires":[]},{"id":"360fe19e1e203539","type":"debug","z":"776c027950fc8c3f","g":"3a2face9747195a7","name":"Just After","active":false,"tosidebar":false,"console":false,"tostatus":true,"complete":"true","targetType":"full","statusVal":"payload","statusType":"auto","x":780,"y":1560,"wires":[]},{"id":"247cbab5f584ea63","type":"change","z":"776c027950fc8c3f","g":"3a2face9747195a7","name":"Brightness 0-100% over 50 min","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\t    \"domain\": \"homeassistant\",\t    \"service\": \"turn_on\",\t    \"target\": {\t        \"entity_id\": [\"light.front_door\", \"switch.hall_light\"]\t        },\t    \"data\": {\t        \"brightness_pct\": percent\t    }\t}","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":310,"y":1620,"wires":[["8fc293f5015dd3d5"]]},{"id":"0509fc220ff7735e","type":"api-call-service","z":"776c027950fc8c3f","g":"3a2face9747195a7","name":"","server":"","version":5,"debugenabled":false,"domain":"homeassistant","service":"turn_on","areaId":[],"deviceId":[],"entityId":[],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":760,"y":1620,"wires":[[]]},{"id":"8fc293f5015dd3d5","type":"switch","z":"776c027950fc8c3f","g":"3a2face9747195a7","name":"Not null","property":"payload.data.brightness_pct","propertyType":"msg","rules":[{"t":"nnull"}],"checkall":"true","repair":false,"outputs":1,"x":540,"y":1620,"wires":[["0509fc220ff7735e"]]}]

Sunrise and sunset times are often critical to running automations, and Home Assistant has inbuilt entities that provide the next dawn, sunrise, dusk, and sunset times. In order to be able to respond to multiple events during the period both before and after sunset, it is necessary to trigger at a more regular interval, not just on the sun-events, and then to test for required times or periods before and after sunset.

The Trigger: state node can be set to use the sensor.time_utc entity as the subject of the node. This sensor holds UTC time as "hh:mm", and will change every minute, thus triggering a state change at regular 60 second intervals. We can add conditions to test the time against an expected period around sunset, and to generate a customised output that includes given periods or times before or after sunset. Combined with a Switch node this permits multiple tests and multiple automation events to be actioned.

Using JSONata to set a condition value

Here JSONata is used to generate an array of times of interest. When the trigger entity state, UTC time "hh:mm", is included in the calculated array, the condition is met and a message will be output.

Since JSONata does not have access to the $entities() function in this particular UI field, the code here is used to generate a period of interest based on hard-coded sunrise and sunset times according to the current month. This is not ideal, but does demonstrate how JSONata can be used to easily generate and manipulate various JSON structures.

(
/* UTC sunrise & sunset to 5 mins, month average (London UK) */
/* Jan-Dec, Feb-Nov, Mar-Oct, Apr-Sep, May-Aug, Jun-Jly      */
    $times:=[["07:55", "16:20"],
             ["07:15", "16:45"],
             ["06:00", "17:35"],
             ["05:15", "18:30"],
             ["04:30", "19:30"],
             ["04:05", "20:05"]];

/* get this month (Jan=0), lookup sun-times table, return sun rise-set for this month */
    $mtable:=[0..11].$min([$, $abs($-11)]);
    $mindex:=$now('[M]').$number()-1;

/* get sunset and build array of condition test-times around this */
    $sunset:=($times[$mtable[$mindex]])[1];
    $h:=$number($substringBefore($sunset, ":"));
    $m:=$number($substringAfter($sunset, ":"));

/* for span period mins, generate array of times and select every nth */
    $span:=60;
    $nth:=5;

    $t:=$h*60+$m-$span;
    ([$t..$t+$span*2])#$i.(
        $h:=$floor($/60);
        $m:=$%60;
        $i%$nth=0 ? $formatInteger($h,'99') & ":" & $formatInteger($m, '99');
    )
)

This code uses the current month to lookup a given sunset time, and then to generate an array of times either side of expected sunset, using a given span in minutes, and then picking out every five minutes only.

The figures provided are examples, based on UTC times (DST ignored) for location London, UK and estimate an average sunrise and sunset time for each calendar month. Since sun events can move by up to one hour from start to end of a month, a span of at least 30 minutes is required to successfully include events across the entire month.

Using JSONata to create a custom message

The output properties on successful trigger include a default message and a default payload, which includes the basic event details for the trigger entity. It is possible to set the output as a customised payload, which replaces msg.payload with specific output, or as a customised message, which replaces the entire message with a generated object.

(
    $sunset:=$entities('sensor.sun_next_setting').state;
    $togo:= ($toMillis($sunset)-$millis())/60000~>$floor();
    $toset:= $togo < 180 ? $togo : $togo-1440;
    $x:= $toset-25;
    $pc:= $x<0 and $x>-55 ? $min([$abs($x)*2, 100]) : null;

    {"topic": "Minutes to Sunset",
     "payload": $toset,
     "time": $entities('sensor.time').state,
     "sunset": $sunset,
     "percent": $pc
    }
)

The $entities() function is available in JSONata in the output properties UI field, which means that both current time and sunset time can be obtained and compared. This allows some simple JSONata code to generate a complete message object, including msg.topic and msg.payload with the exact minutes to the next sunset correctly calculated.

For automation, all that is required is a following Switch node set to route on a message property, for example msg.payload as minutes to next sunset between 0 and 4 (based on a five minute trigger interval) so as to be able to run an automation just as sunset is about to take place (with a five minute discrimination).

As a further example, the JSONata output calculates a percentage value going from 0 to 100 over the period 25 minutes before to 25 minutes after sunset, with the percentage value set to 'null' outside this time. The Change node following also uses JSONata to create the entire Service Call node input settings, to turn on a group of lights in an timed-incremental fashion.

Also see: