Lutron Caseta Pico Remotes and Node-Red

I have recently employed the Lutron Pro Hub and have several Pico remotes controlling non-Lutron lights. I have a few of the two switch models, that send a 0 when idle and a 1 and 4 when the top (on) and bottom (off) buttons are pushed respectively. I’m trying to make them dim the lights when held. I’ve figured out how to recognize a “short” and “long” push, but it doesn’t recognize it until the button is released and goes back to zero. That obviously won’t work very well for dimming. Does anyone have any suggestions or examples of a similar flow?

1 Like

Here’s my flow, but it’s not very elegant and I’m sure there’s a much better better way to do it. It’s dims a preset amount each time it’s pressed.

Here is a subflow that I created for my Pico remotes, which handles various tap/hold combinations for each button, including adjustments while holding the dimmer buttons.

[{"id":"26fa9897.8dc708","type":"subflow","name":"Pico Remote - Lights","info":"","category":"","in":[{"x":30,"y":135,"wires":[{"id":"26e24595.347092"}]}],"out":[],"env":[]},{"id":"26e24595.347092","type":"switch","z":"26fa9897.8dc708","name":"Pico Sensor Switch","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"0","vt":"num"},{"t":"eq","v":"8","vt":"num"},{"t":"eq","v":"16","vt":"num"},{"t":"eq","v":"1","vt":"num"},{"t":"eq","v":"4","vt":"num"},{"t":"eq","v":"2","vt":"num"}],"checkall":"true","repair":false,"outputs":6,"x":195,"y":135,"wires":[["ed76edfe.b85ca","3cb9667a.6bd052"],["94a01109.d92dd"],["94a01109.d92dd"],[],[],[]],"outputLabels":["Idle","Up","Down","On","Off","Action"]},{"id":"94a01109.d92dd","type":"looptimer","z":"26fa9897.8dc708","duration":"1.5","units":"Second","maxloops":"6","maxtimeout":"1","maxtimeoutunits":"Minute","name":"","x":510,"y":120,"wires":[["73b940af.34f05"],["80b174c2.fc8938"]]},{"id":"ed76edfe.b85ca","type":"change","z":"26fa9897.8dc708","name":"Stop Loop","rules":[{"t":"set","p":"payload","pt":"msg","to":"STOP","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":490,"y":210,"wires":[["94a01109.d92dd"]]},{"id":"73b940af.34f05","type":"function","z":"26fa9897.8dc708","name":"Hold Loop Counter","func":"// Get or create a context for this message\nvar msg_context = context.get(msg._msgid) || {}\n\n// Initialize the hold count if not message context\nif (msg_context.hold_count === undefined) {\n    msg_context.hold_count = 0;\n}\n\n// Update the hold count\nmsg_context.hold_count += 1;\nmsg.hold_count = msg_context.hold_count;\n\n// Save and return hold count\ncontext.set(msg._msgid, msg_context)\nreturn msg;\n","outputs":1,"noerr":0,"x":510,"y":75,"wires":[["41015b9d.8e10c4"]]},{"id":"41015b9d.8e10c4","type":"function","z":"26fa9897.8dc708","name":"service_dimmer_up_down","func":"let dim_step = 50;\nlet initial_brightness = msg.source_light.attributes.brightness || 0;\nvar dim_direction = 1;  // 1=dim_up, -1=dim_down\n\n// Only create payload for dimmer buttons\n// and pass it through the first output\nif (msg.payload != 16 && msg.payload != 8) {\n    return [null, msg];\n}\n\n\n// Payload is 8 for dim up, 16 for dim down\nif (msg.payload == 16) {\n    dim_direction = -1;\n}\n\nvar brightness = initial_brightness + (dim_direction * dim_step * msg.hold_count);\nif (brightness < 1) {\n    brightness = 1;\n}\n\nlet dimmer_service_data = {\n    \"data\" : {\n        \"entity_id\" : msg.target_light,\n        \"brightness\": brightness,\n        \"transition\": 1.00\n    }\n}\nmsg.dimmer_service_data = dimmer_service_data;\n\nmsg.payload = dimmer_service_data;\n\nreturn [msg, null];\n","outputs":2,"noerr":0,"x":825,"y":75,"wires":[["519a3f5a.7684e"],[]]},{"id":"519a3f5a.7684e","type":"api-call-service","z":"26fa9897.8dc708","name":"Dim Light","server":"af006389.170db","version":1,"service_domain":"light","service":"turn_on","entityId":"","data":"","dataType":"json","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":1530,"y":75,"wires":[[]]},{"id":"975e6050.5821e","type":"function","z":"26fa9897.8dc708","name":"Get Button Press Type","func":"// Map of sensor states to button names\nlet buttonMap = {\n    \"0\": \"idle\",\n    \"1\": \"on\",\n    \"2\": \"action\",\n    \"4\": \"off\",\n    \"8\": \"up\",\n    \"16\": \"down\"\n}\n\n//var pico = msg.data.pico || {};\nvar pico = {};\n\n// Identify the pressed button from previous state (before released)\npico.state = msg.data.old_state.state;\npico.button = buttonMap[pico.state.toString()];\n\n// Store number of rapid button taps from timed-counter\npico.tap_count = msg.count;\ndelete msg.count;\n\n// Calculate the hold duration of the last button press\nvar holdStart = new Date(msg.data.old_state.last_changed).getTime();\nvar holdEnd = new Date(msg.data.new_state.last_changed).getTime();\npico.hold_duration = holdEnd - holdStart;\n\n// Save the button press data in the message's pico object\nmsg.pico = pico;\n\n// Move the original message data\nmsg.sensor_data = msg.data\n\n// Replace message data with the target light data to support service calls\nmsg.payload.data  = {\n    \"entity_id\" : msg.target_light\n}\n\n// Output 3: Long Press\nif (pico.hold_duration >= 1000)\n    return [null, null, msg];\n    \n// Output 2: Double Tap\nelse if (pico.tap_count > 1)\n    return [null, msg, null];\n    \n// Output 1: Single Tap\nelse\n    return [msg, null, null];","outputs":3,"noerr":0,"x":520,"y":315,"wires":[["1b4304c0.755883"],["4eecc065.39adc"],["9f9b4370.50841"]],"inputLabels":["Pico Sensor Event"],"outputLabels":["Single Tap","Double Tap","Long Press"]},{"id":"3cb9667a.6bd052","type":"timed-counter","z":"26fa9897.8dc708","name":"Count Taps","timelimit":"350","timeunit":1,"withhold":true,"fixedtimeout":false,"pertopic":false,"x":490,"y":255,"wires":[["975e6050.5821e"]]},{"id":"1b4304c0.755883","type":"switch","z":"26fa9897.8dc708","name":"Button Tapped","property":"pico.button","propertyType":"msg","rules":[{"t":"eq","v":"on","vt":"str"},{"t":"eq","v":"off","vt":"str"},{"t":"eq","v":"up","vt":"str"},{"t":"eq","v":"down","vt":"str"},{"t":"eq","v":"action","vt":"str"}],"checkall":"true","repair":false,"outputs":5,"x":785,"y":240,"wires":[["5e468631.5b51c8"],["7073abb6.0d6b84"],["43ca083b.760358"],["43ca083b.760358"],["e7de0b16.3fc9d8"]],"outputLabels":["On","Off","Up","Down","Action"]},{"id":"4eecc065.39adc","type":"switch","z":"26fa9897.8dc708","name":"Button Double Tapped","property":"pico.button","propertyType":"msg","rules":[{"t":"eq","v":"on","vt":"str"},{"t":"eq","v":"off","vt":"str"},{"t":"eq","v":"up","vt":"str"},{"t":"eq","v":"down","vt":"str"},{"t":"eq","v":"action","vt":"str"}],"checkall":"true","repair":false,"outputs":5,"x":805,"y":390,"wires":[["3cc9a5ab.875c9a","43ca083b.760358"],["937f45c2.1e08f","43ca083b.760358"],[],[],["135a5b3d.df8b35"]],"outputLabels":["On","Off","Up","Down","Action"]},{"id":"9f9b4370.50841","type":"switch","z":"26fa9897.8dc708","name":"Button Long Pressed","property":"pico.button","propertyType":"msg","rules":[{"t":"eq","v":"on","vt":"str"},{"t":"eq","v":"off","vt":"str"},{"t":"eq","v":"up","vt":"str"},{"t":"eq","v":"down","vt":"str"},{"t":"eq","v":"action","vt":"str"}],"checkall":"true","repair":false,"outputs":5,"x":805,"y":540,"wires":[["31073fb7.41b068"],["9bdab28c.fbf96"],[],[],["9b98da6.c7494a8"]],"outputLabels":["On","Off","Up","Down","Action"]},{"id":"80b174c2.fc8938","type":"function","z":"26fa9897.8dc708","name":"Clear Msg Context","func":"// Get or create a context for this message\nvar msg_context = context.get(msg._msgid) || {}\n\n// TODO: Clear this message from the context to prevent memory leak\n\n\nreturn msg;\n","outputs":1,"noerr":0,"x":510,"y":165,"wires":[[]]},{"id":"e7de0b16.3fc9d8","type":"function","z":"26fa9897.8dc708","name":"Action Iterator - Single Tap","func":"// Get or create a context for the remote's scene index\nlet remote_name = msg.topic.split(/\\.(.+)/)[1];      // remove 'sensor' \nlet action_index_context = \"pico_remotes.\" + remote_name + \".action_index_single\";\n\n// Set action index to -1 if context not yet initialized\nvar action_index = context.get(action_index_context);\nif (typeof action_index === \"undefined\") {\n    action_index = -1\n}\n\n// A list of actions/service calls should be part of the msg\nvar actions = msg.service_action_tap_single || [];\n\n// Loop back to the first action if we've advanced too far\nif (action_index >= actions.length - 1) {\n    action_index = 0;\n} else {\n    action_index++;\n}\n\nvar service_payload = actions[action_index];\n\n// Update message details\nmsg[\"action_index\"] = action_index;\nmsg[\"service_payload\"] = service_payload;\n\n// Update context with the remote's action index\ncontext.set(action_index_context, action_index);\n\nmsg[\"sensor_payload\"] = msg.payload;\nmsg.payload = service_payload;\n\nreturn msg;\n","outputs":1,"noerr":0,"x":1110,"y":150,"wires":[["c2f4e603.1b9d18","d7581470.a427d"]]},{"id":"c2f4e603.1b9d18","type":"change","z":"26fa9897.8dc708","name":"Turn Off Both Circadian Switches","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\t   \"domain\": \"switch\",\t   \"service\": \"turn_off\",\t   \"data\": {\t       \"entity_id\": [msg.circadian_switch, msg.circadian_switch & \"_nobrightadj\"]\t   }\t}","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":1400,"y":150,"wires":[["d7581470.a427d"]]},{"id":"5e468631.5b51c8","type":"change","z":"26fa9897.8dc708","name":"service_on_tap_single","rules":[{"t":"move","p":"payload","pt":"msg","to":"sensor_payload","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"service_on_tap_single","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1090,"y":180,"wires":[["d7581470.a427d"]]},{"id":"7073abb6.0d6b84","type":"change","z":"26fa9897.8dc708","name":"service_off_tap_single","rules":[{"t":"move","p":"payload","pt":"msg","to":"sensor_payload","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"service_off_tap_single","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1090,"y":210,"wires":[["d7581470.a427d"]]},{"id":"6cd20bba.9d8514","type":"change","z":"26fa9897.8dc708","name":"service_up_tap_single","rules":[{"t":"move","p":"payload","pt":"msg","to":"sensor_payload","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"service_up_tap_single","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1090,"y":240,"wires":[["d7581470.a427d"]]},{"id":"d69fd6e4.dbbbf","type":"change","z":"26fa9897.8dc708","name":"service_down_tap_single","rules":[{"t":"move","p":"payload","pt":"msg","to":"sensor_payload","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"service_down_tap_single","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1100,"y":270,"wires":[["d7581470.a427d"]]},{"id":"8484ddc2.6bd38","type":"change","z":"26fa9897.8dc708","name":"service_action_tap_single","rules":[{"t":"move","p":"payload","pt":"msg","to":"sensor_payload","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"service_action_tap_single","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1100,"y":300,"wires":[["d7581470.a427d"]]},{"id":"3cc9a5ab.875c9a","type":"change","z":"26fa9897.8dc708","name":"service_on_tap_double","rules":[{"t":"move","p":"payload","pt":"msg","to":"sensor_payload","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"service_on_tap_double","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1100,"y":330,"wires":[["d7581470.a427d"]]},{"id":"937f45c2.1e08f","type":"change","z":"26fa9897.8dc708","name":"service_off_tap_double","rules":[{"t":"move","p":"payload","pt":"msg","to":"sensor_payload","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"service_off_tap_double","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1100,"y":360,"wires":[["d7581470.a427d"]]},{"id":"377e4fc.ed3bf3","type":"change","z":"26fa9897.8dc708","name":"service_up_tap_double","rules":[{"t":"move","p":"payload","pt":"msg","to":"sensor_payload","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"service_up_tap_double","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1100,"y":390,"wires":[["d7581470.a427d"]]},{"id":"60ed4e1.afa313","type":"change","z":"26fa9897.8dc708","name":"service_down_tap_double","rules":[{"t":"move","p":"payload","pt":"msg","to":"sensor_payload","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"service_down_tap_double","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1100,"y":420,"wires":[["d7581470.a427d"]]},{"id":"31073fb7.41b068","type":"change","z":"26fa9897.8dc708","name":"service_on_tap_hold","rules":[{"t":"move","p":"payload","pt":"msg","to":"sensor_payload","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"service_on_tap_hold","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1090,"y":480,"wires":[["d7581470.a427d"]]},{"id":"9bdab28c.fbf96","type":"change","z":"26fa9897.8dc708","name":"service_off_tap_hold","rules":[{"t":"move","p":"payload","pt":"msg","to":"sensor_payload","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"service_off_tap_hold","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1090,"y":510,"wires":[["d7581470.a427d"]]},{"id":"8f1e4d50.600a7","type":"change","z":"26fa9897.8dc708","name":"service_up_tap_hold","rules":[{"t":"move","p":"payload","pt":"msg","to":"sensor_payload","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"service_up_tap_hold","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1090,"y":540,"wires":[["d7581470.a427d"]]},{"id":"43071de1.ce27ac","type":"change","z":"26fa9897.8dc708","name":"service_down_tap_hold","rules":[{"t":"move","p":"payload","pt":"msg","to":"sensor_payload","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"service_down_tap_hold","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1100,"y":570,"wires":[["d7581470.a427d"]]},{"id":"9b98da6.c7494a8","type":"change","z":"26fa9897.8dc708","name":"service_action_tap_hold","rules":[{"t":"move","p":"payload","pt":"msg","to":"sensor_payload","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"service_action_tap_hold","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1100,"y":600,"wires":[["d7581470.a427d"]]},{"id":"d7581470.a427d","type":"api-call-service","z":"26fa9897.8dc708","name":"Call Service","server":"af006389.170db","version":1,"service_domain":"","service":"","entityId":"","data":"","dataType":"json","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":1525,"y":390,"wires":[[]]},{"id":"135a5b3d.df8b35","type":"change","z":"26fa9897.8dc708","name":"service_action_tap_double","rules":[{"t":"move","p":"payload","pt":"msg","to":"sensor_payload","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"service_action_tap_double","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1110,"y":450,"wires":[["d7581470.a427d"]]},{"id":"71af814a.898a3","type":"change","z":"26fa9897.8dc708","name":"Turn On \"No Brightness\" Circadian Switch","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\t   \"domain\": \"switch\",\t   \"service\": \"turn_on\",\t   \"data\": {\t       \"entity_id\": msg.circadian_switch & \"_nobrightadj\"\t   }\t}","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":1670,"y":705,"wires":[["d7581470.a427d"]]},{"id":"2f394085.7128c","type":"api-current-state","z":"26fa9897.8dc708","name":"Get State","server":"af006389.170db","version":1,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","override_topic":false,"entity_id":"","state_type":"str","state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","blockInputOverrides":false,"x":1305,"y":705,"wires":[["b48eddb1.46b7e"]]},{"id":"43ca083b.760358","type":"change","z":"26fa9897.8dc708","name":"'Standard' Circiadian Switch","rules":[{"t":"delete","p":"payload","pt":"msg"},{"t":"set","p":"payload.entity_id","pt":"msg","to":"circadian_switch","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1110,"y":705,"wires":[["2f394085.7128c"]]},{"id":"b48eddb1.46b7e","type":"switch","z":"26fa9897.8dc708","name":"Is \"on\"","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"on","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":1430,"y":705,"wires":[["71af814a.898a3"]]},{"id":"5fde6378.56544c","type":"comment","z":"26fa9897.8dc708","name":"If a brightness was adjusted while circadian is enabled, disable circadian brightess adjustments","info":"","x":1320,"y":675,"wires":[]},{"id":"af006389.170db","type":"server","z":"","name":"Home Assistant"}]

Dimming loop logic is at the top-center of diagram.

If you want to decipher all of the logic, the higher level flow is as follows. When there is a state change on the remote, there is a change node that is contains all of the configuration - mapping different button press types to service calls - before running that common subflow logic that all Picos use.

[{"id":"26fa9897.8dc708","type":"subflow","name":"Pico Remote - Lights","info":"","category":"","in":[{"x":30,"y":135,"wires":[{"id":"26e24595.347092"}]}],"out":[],"env":[]},{"id":"26e24595.347092","type":"switch","z":"26fa9897.8dc708","name":"Pico Sensor Switch","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"0","vt":"num"},{"t":"eq","v":"8","vt":"num"},{"t":"eq","v":"16","vt":"num"},{"t":"eq","v":"1","vt":"num"},{"t":"eq","v":"4","vt":"num"},{"t":"eq","v":"2","vt":"num"}],"checkall":"true","repair":false,"outputs":6,"x":195,"y":135,"wires":[["ed76edfe.b85ca","3cb9667a.6bd052"],["94a01109.d92dd"],["94a01109.d92dd"],[],[],[]],"outputLabels":["Idle","Up","Down","On","Off","Action"]},{"id":"94a01109.d92dd","type":"looptimer","z":"26fa9897.8dc708","duration":"1.5","units":"Second","maxloops":"6","maxtimeout":"1","maxtimeoutunits":"Minute","name":"","x":510,"y":120,"wires":[["73b940af.34f05"],["80b174c2.fc8938"]]},{"id":"ed76edfe.b85ca","type":"change","z":"26fa9897.8dc708","name":"Stop Loop","rules":[{"t":"set","p":"payload","pt":"msg","to":"STOP","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":490,"y":210,"wires":[["94a01109.d92dd"]]},{"id":"73b940af.34f05","type":"function","z":"26fa9897.8dc708","name":"Hold Loop Counter","func":"// Get or create a context for this message\nvar msg_context = context.get(msg._msgid) || {}\n\n// Initialize the hold count if not message context\nif (msg_context.hold_count === undefined) {\n    msg_context.hold_count = 0;\n}\n\n// Update the hold count\nmsg_context.hold_count += 1;\nmsg.hold_count = msg_context.hold_count;\n\n// Save and return hold count\ncontext.set(msg._msgid, msg_context)\nreturn msg;\n","outputs":1,"noerr":0,"x":510,"y":75,"wires":[["41015b9d.8e10c4"]]},{"id":"41015b9d.8e10c4","type":"function","z":"26fa9897.8dc708","name":"service_dimmer_up_down","func":"let dim_step = 50;\nlet initial_brightness = msg.source_light.attributes.brightness || 0;\nvar dim_direction = 1;  // 1=dim_up, -1=dim_down\n\n// Only create payload for dimmer buttons\n// and pass it through the first output\nif (msg.payload != 16 && msg.payload != 8) {\n    return [null, msg];\n}\n\n\n// Payload is 8 for dim up, 16 for dim down\nif (msg.payload == 16) {\n    dim_direction = -1;\n}\n\nvar brightness = initial_brightness + (dim_direction * dim_step * msg.hold_count);\nif (brightness < 1) {\n    brightness = 1;\n}\n\nlet dimmer_service_data = {\n    \"data\" : {\n        \"entity_id\" : msg.target_light,\n        \"brightness\": brightness,\n        \"transition\": 1.00\n    }\n}\nmsg.dimmer_service_data = dimmer_service_data;\n\nmsg.payload = dimmer_service_data;\n\nreturn [msg, null];\n","outputs":2,"noerr":0,"x":825,"y":75,"wires":[["519a3f5a.7684e"],[]]},{"id":"519a3f5a.7684e","type":"api-call-service","z":"26fa9897.8dc708","name":"Dim Light","server":"af006389.170db","version":1,"service_domain":"light","service":"turn_on","entityId":"","data":"","dataType":"json","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":1530,"y":75,"wires":[[]]},{"id":"975e6050.5821e","type":"function","z":"26fa9897.8dc708","name":"Get Button Press Type","func":"// Map of sensor states to button names\nlet buttonMap = {\n    \"0\": \"idle\",\n    \"1\": \"on\",\n    \"2\": \"action\",\n    \"4\": \"off\",\n    \"8\": \"up\",\n    \"16\": \"down\"\n}\n\n//var pico = msg.data.pico || {};\nvar pico = {};\n\n// Identify the pressed button from previous state (before released)\npico.state = msg.data.old_state.state;\npico.button = buttonMap[pico.state.toString()];\n\n// Store number of rapid button taps from timed-counter\npico.tap_count = msg.count;\ndelete msg.count;\n\n// Calculate the hold duration of the last button press\nvar holdStart = new Date(msg.data.old_state.last_changed).getTime();\nvar holdEnd = new Date(msg.data.new_state.last_changed).getTime();\npico.hold_duration = holdEnd - holdStart;\n\n// Save the button press data in the message's pico object\nmsg.pico = pico;\n\n// Move the original message data\nmsg.sensor_data = msg.data\n\n// Replace message data with the target light data to support service calls\nmsg.payload.data  = {\n    \"entity_id\" : msg.target_light\n}\n\n// Output 3: Long Press\nif (pico.hold_duration >= 1000)\n    return [null, null, msg];\n    \n// Output 2: Double Tap\nelse if (pico.tap_count > 1)\n    return [null, msg, null];\n    \n// Output 1: Single Tap\nelse\n    return [msg, null, null];","outputs":3,"noerr":0,"x":520,"y":315,"wires":[["1b4304c0.755883"],["4eecc065.39adc"],["9f9b4370.50841"]],"inputLabels":["Pico Sensor Event"],"outputLabels":["Single Tap","Double Tap","Long Press"]},{"id":"3cb9667a.6bd052","type":"timed-counter","z":"26fa9897.8dc708","name":"Count Taps","timelimit":"350","timeunit":1,"withhold":true,"fixedtimeout":false,"pertopic":false,"x":490,"y":255,"wires":[["975e6050.5821e"]]},{"id":"1b4304c0.755883","type":"switch","z":"26fa9897.8dc708","name":"Button Tapped","property":"pico.button","propertyType":"msg","rules":[{"t":"eq","v":"on","vt":"str"},{"t":"eq","v":"off","vt":"str"},{"t":"eq","v":"up","vt":"str"},{"t":"eq","v":"down","vt":"str"},{"t":"eq","v":"action","vt":"str"}],"checkall":"true","repair":false,"outputs":5,"x":785,"y":240,"wires":[["5e468631.5b51c8"],["7073abb6.0d6b84"],["43ca083b.760358"],["43ca083b.760358"],["e7de0b16.3fc9d8"]],"outputLabels":["On","Off","Up","Down","Action"]},{"id":"4eecc065.39adc","type":"switch","z":"26fa9897.8dc708","name":"Button Double Tapped","property":"pico.button","propertyType":"msg","rules":[{"t":"eq","v":"on","vt":"str"},{"t":"eq","v":"off","vt":"str"},{"t":"eq","v":"up","vt":"str"},{"t":"eq","v":"down","vt":"str"},{"t":"eq","v":"action","vt":"str"}],"checkall":"true","repair":false,"outputs":5,"x":805,"y":390,"wires":[["3cc9a5ab.875c9a","43ca083b.760358"],["937f45c2.1e08f","43ca083b.760358"],[],[],["135a5b3d.df8b35"]],"outputLabels":["On","Off","Up","Down","Action"]},{"id":"9f9b4370.50841","type":"switch","z":"26fa9897.8dc708","name":"Button Long Pressed","property":"pico.button","propertyType":"msg","rules":[{"t":"eq","v":"on","vt":"str"},{"t":"eq","v":"off","vt":"str"},{"t":"eq","v":"up","vt":"str"},{"t":"eq","v":"down","vt":"str"},{"t":"eq","v":"action","vt":"str"}],"checkall":"true","repair":false,"outputs":5,"x":805,"y":540,"wires":[["31073fb7.41b068"],["9bdab28c.fbf96"],[],[],["9b98da6.c7494a8"]],"outputLabels":["On","Off","Up","Down","Action"]},{"id":"80b174c2.fc8938","type":"function","z":"26fa9897.8dc708","name":"Clear Msg Context","func":"// Get or create a context for this message\nvar msg_context = context.get(msg._msgid) || {}\n\n// TODO: Clear this message from the context to prevent memory leak\n\n\nreturn msg;\n","outputs":1,"noerr":0,"x":510,"y":165,"wires":[[]]},{"id":"e7de0b16.3fc9d8","type":"function","z":"26fa9897.8dc708","name":"Action Iterator - Single Tap","func":"// Get or create a context for the remote's scene index\nlet remote_name = msg.topic.split(/\\.(.+)/)[1];      // remove 'sensor' \nlet action_index_context = \"pico_remotes.\" + remote_name + \".action_index_single\";\n\n// Set action index to -1 if context not yet initialized\nvar action_index = context.get(action_index_context);\nif (typeof action_index === \"undefined\") {\n    action_index = -1\n}\n\n// A list of actions/service calls should be part of the msg\nvar actions = msg.service_action_tap_single || [];\n\n// Loop back to the first action if we've advanced too far\nif (action_index >= actions.length - 1) {\n    action_index = 0;\n} else {\n    action_index++;\n}\n\nvar service_payload = actions[action_index];\n\n// Update message details\nmsg[\"action_index\"] = action_index;\nmsg[\"service_payload\"] = service_payload;\n\n// Update context with the remote's action index\ncontext.set(action_index_context, action_index);\n\nmsg[\"sensor_payload\"] = msg.payload;\nmsg.payload = service_payload;\n\nreturn msg;\n","outputs":1,"noerr":0,"x":1110,"y":150,"wires":[["c2f4e603.1b9d18","d7581470.a427d"]]},{"id":"c2f4e603.1b9d18","type":"change","z":"26fa9897.8dc708","name":"Turn Off Both Circadian Switches","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\t   \"domain\": \"switch\",\t   \"service\": \"turn_off\",\t   \"data\": {\t       \"entity_id\": [msg.circadian_switch, msg.circadian_switch & \"_nobrightadj\"]\t   }\t}","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":1400,"y":150,"wires":[["d7581470.a427d"]]},{"id":"5e468631.5b51c8","type":"change","z":"26fa9897.8dc708","name":"service_on_tap_single","rules":[{"t":"move","p":"payload","pt":"msg","to":"sensor_payload","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"service_on_tap_single","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1090,"y":180,"wires":[["d7581470.a427d"]]},{"id":"7073abb6.0d6b84","type":"change","z":"26fa9897.8dc708","name":"service_off_tap_single","rules":[{"t":"move","p":"payload","pt":"msg","to":"sensor_payload","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"service_off_tap_single","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1090,"y":210,"wires":[["d7581470.a427d"]]},{"id":"6cd20bba.9d8514","type":"change","z":"26fa9897.8dc708","name":"service_up_tap_single","rules":[{"t":"move","p":"payload","pt":"msg","to":"sensor_payload","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"service_up_tap_single","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1090,"y":240,"wires":[["d7581470.a427d"]]},{"id":"d69fd6e4.dbbbf","type":"change","z":"26fa9897.8dc708","name":"service_down_tap_single","rules":[{"t":"move","p":"payload","pt":"msg","to":"sensor_payload","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"service_down_tap_single","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1100,"y":270,"wires":[["d7581470.a427d"]]},{"id":"8484ddc2.6bd38","type":"change","z":"26fa9897.8dc708","name":"service_action_tap_single","rules":[{"t":"move","p":"payload","pt":"msg","to":"sensor_payload","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"service_action_tap_single","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1100,"y":300,"wires":[["d7581470.a427d"]]},{"id":"3cc9a5ab.875c9a","type":"change","z":"26fa9897.8dc708","name":"service_on_tap_double","rules":[{"t":"move","p":"payload","pt":"msg","to":"sensor_payload","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"service_on_tap_double","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1100,"y":330,"wires":[["d7581470.a427d"]]},{"id":"937f45c2.1e08f","type":"change","z":"26fa9897.8dc708","name":"service_off_tap_double","rules":[{"t":"move","p":"payload","pt":"msg","to":"sensor_payload","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"service_off_tap_double","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1100,"y":360,"wires":[["d7581470.a427d"]]},{"id":"377e4fc.ed3bf3","type":"change","z":"26fa9897.8dc708","name":"service_up_tap_double","rules":[{"t":"move","p":"payload","pt":"msg","to":"sensor_payload","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"service_up_tap_double","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1100,"y":390,"wires":[["d7581470.a427d"]]},{"id":"60ed4e1.afa313","type":"change","z":"26fa9897.8dc708","name":"service_down_tap_double","rules":[{"t":"move","p":"payload","pt":"msg","to":"sensor_payload","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"service_down_tap_double","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1100,"y":420,"wires":[["d7581470.a427d"]]},{"id":"31073fb7.41b068","type":"change","z":"26fa9897.8dc708","name":"service_on_tap_hold","rules":[{"t":"move","p":"payload","pt":"msg","to":"sensor_payload","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"service_on_tap_hold","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1090,"y":480,"wires":[["d7581470.a427d"]]},{"id":"9bdab28c.fbf96","type":"change","z":"26fa9897.8dc708","name":"service_off_tap_hold","rules":[{"t":"move","p":"payload","pt":"msg","to":"sensor_payload","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"service_off_tap_hold","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1090,"y":510,"wires":[["d7581470.a427d"]]},{"id":"8f1e4d50.600a7","type":"change","z":"26fa9897.8dc708","name":"service_up_tap_hold","rules":[{"t":"move","p":"payload","pt":"msg","to":"sensor_payload","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"service_up_tap_hold","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1090,"y":540,"wires":[["d7581470.a427d"]]},{"id":"43071de1.ce27ac","type":"change","z":"26fa9897.8dc708","name":"service_down_tap_hold","rules":[{"t":"move","p":"payload","pt":"msg","to":"sensor_payload","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"service_down_tap_hold","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1100,"y":570,"wires":[["d7581470.a427d"]]},{"id":"9b98da6.c7494a8","type":"change","z":"26fa9897.8dc708","name":"service_action_tap_hold","rules":[{"t":"move","p":"payload","pt":"msg","to":"sensor_payload","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"service_action_tap_hold","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1100,"y":600,"wires":[["d7581470.a427d"]]},{"id":"d7581470.a427d","type":"api-call-service","z":"26fa9897.8dc708","name":"Call Service","server":"af006389.170db","version":1,"service_domain":"","service":"","entityId":"","data":"","dataType":"json","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":1525,"y":390,"wires":[[]]},{"id":"135a5b3d.df8b35","type":"change","z":"26fa9897.8dc708","name":"service_action_tap_double","rules":[{"t":"move","p":"payload","pt":"msg","to":"sensor_payload","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"service_action_tap_double","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1110,"y":450,"wires":[["d7581470.a427d"]]},{"id":"71af814a.898a3","type":"change","z":"26fa9897.8dc708","name":"Turn On \"No Brightness\" Circadian Switch","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\t   \"domain\": \"switch\",\t   \"service\": \"turn_on\",\t   \"data\": {\t       \"entity_id\": msg.circadian_switch & \"_nobrightadj\"\t   }\t}","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":1670,"y":705,"wires":[["d7581470.a427d"]]},{"id":"2f394085.7128c","type":"api-current-state","z":"26fa9897.8dc708","name":"Get State","server":"af006389.170db","version":1,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","override_topic":false,"entity_id":"","state_type":"str","state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","blockInputOverrides":false,"x":1305,"y":705,"wires":[["b48eddb1.46b7e"]]},{"id":"43ca083b.760358","type":"change","z":"26fa9897.8dc708","name":"'Standard' Circiadian Switch","rules":[{"t":"delete","p":"payload","pt":"msg"},{"t":"set","p":"payload.entity_id","pt":"msg","to":"circadian_switch","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1110,"y":705,"wires":[["2f394085.7128c"]]},{"id":"b48eddb1.46b7e","type":"switch","z":"26fa9897.8dc708","name":"Is \"on\"","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"on","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":1430,"y":705,"wires":[["71af814a.898a3"]]},{"id":"5fde6378.56544c","type":"comment","z":"26fa9897.8dc708","name":"If a brightness was adjusted while circadian is enabled, disable circadian brightess adjustments","info":"","x":1320,"y":675,"wires":[]},{"id":"5d4606b3.9c246","type":"server-state-changed","z":"e54719d2.18a8f8","name":"","server":"af006389.170db","version":1,"entityidfilter":"sensor.pico_familyroom","entityidfiltertype":"substring","outputinitially":false,"state_type":"num","haltifstate":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"output_only_on_state_change":true,"x":210,"y":105,"wires":[["e4a5b448.0a377"]]},{"id":"bb9e87f6.ba4848","type":"change","z":"e54719d2.18a8f8","name":"Configure Flow","rules":[{"t":"set","p":"source_remote_state","pt":"msg","to":"msg.payload","tot":"jsonata"},{"t":"set","p":"source_light","pt":"msg","to":"source_light[0]","tot":"msg"},{"t":"set","p":"target_light","pt":"msg","to":"light.familyroom_ceiling","tot":"str"},{"t":"set","p":"circadian_switch","pt":"msg","to":"switch.circadian_lighting_familyroom","tot":"str"},{"t":"set","p":"service_action_tap_single","pt":"msg","to":"[{\"domain\":\"hue\",\"service\":\"hue_activate_scene\",\"data\":{\"group_name\":\"FamilyRoom Ceiling\",\"scene_name\":\"Bright\"}},{\"domain\":\"hue\",\"service\":\"hue_activate_scene\",\"data\":{\"group_name\":\"FamilyRoom Ceiling\",\"scene_name\":\"Dimmed\"}},{\"domain\":\"hue\",\"service\":\"hue_activate_scene\",\"data\":{\"group_name\":\"FamilyRoom Ceiling\",\"scene_name\":\"Relax\"}}]","tot":"json"},{"t":"set","p":"service_action_tap_double","pt":"msg","to":"{\"domain\":\"switch\",\"service\":\"turn_on\",\"data\":{\"entity_id\":\"switch.circadian_lighting_familyroom\"}}","tot":"json"},{"t":"set","p":"service_on_tap_single","pt":"msg","to":"{\t   \"domain\": \"light\",\t   \"service\": \"turn_on\",\t   \"data\": {\t       \"entity_id\": msg.target_light\t   \t   }\t\t}","tot":"jsonata"},{"t":"set","p":"service_off_tap_single","pt":"msg","to":"{\t   \"domain\": \"light\",\t   \"service\": \"turn_off\",\t   \"data\": {\t       \"entity_id\": msg.target_light\t   }\t}","tot":"jsonata"},{"t":"set","p":"service_on_tap_double","pt":"msg","to":"{\t   \"domain\": \"light\",\t   \"service\": \"turn_on\",\t   \"data\": {\t       \"entity_id\": msg.target_light,\t       \"brightness\": 254\t   }\t}","tot":"jsonata"},{"t":"set","p":"service_off_tap_double","pt":"msg","to":"{\t   \"domain\": \"light\",\t   \"service\": \"turn_on\",\t   \"data\": {\t       \"entity_id\": msg.target_light,\t       \"brightness\": 1\t   }\t}","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":890,"y":105,"wires":[["b0cdcf31.dfb4d"]]},{"id":"e4a5b448.0a377","type":"ha-get-entities","z":"e54719d2.18a8f8","server":"af006389.170db","name":"Get Source: light.familyroom_ceiling","rules":[{"property":"entity_id","logic":"is","value":"light.familyroom_ceiling","valueType":"str"}],"output_type":"array","output_empty_results":false,"output_location_type":"msg","output_location":"source_light","output_results_count":1,"x":615,"y":105,"wires":[["bb9e87f6.ba4848"]]},{"id":"b0cdcf31.dfb4d","type":"subflow:26fa9897.8dc708","z":"e54719d2.18a8f8","name":"Pico Remote - Lights","env":[],"x":1120,"y":105,"wires":[]},{"id":"af006389.170db","type":"server","z":"","name":"Home Assistant"}]

Oh wow! Now that is impressive. That’s a bit to chew on, but I really appreciate you posting that.

What do you use double taps for?

Double-tap on for max brightness, double-tap off for min brightness (not off).