Add DDF zcl:cluster parse fn command - #8631
Conversation
This allows specifying multiple clusters as filter. Mostly useful for moving switches from button_maps.json to pure DDF implementation.
{"fn": "zcl:cluster", "cl": "0xFC00", "eval": "..."}
{"fn": "zcl:cluster", "cl": ["0xFC00", "0xFC01"], "script": "handler.js"}
Note this function doesn't support specifying cmd and attr, this must be handled in respective handler.
Seperate PR's will use this to move switches out of button_maps.json.
|
I like this! After fixing #8613, we can ditch I would like to take a more declarative approach: define a list of The device-specific var button_event = require('button_event.js')
button_event([
[0x0006, 0x01, 1002],
[0x0006, 0x00, 2002],
[0x0008, [0x01, 0x05], () => { return ZclFrame.at(0) === 0x00 ? 1001 : 2001 }],
[0x0008, [0x03, 0x07], () => { return Item.val === 1001 ? 1003 : 2003 }]
])The generic javascript would be: function button_event_entry(cluster, cmd, val) {
if (ClusterId === cluster) {
if (ZclFrame.cmd === cmd || (Array.isArray(cmd) && cmd.includes(ZclFrame.cmd))) {
return (typeof val === 'function') ? val() : val
}
}
return null
}
function button_event (list) {
for (var entry of list) {
var event = button_event_entry(entry[0], entry[1], entry[2])
if (event != null) {
Item.val = event
return
}
}
}
module.export = button_eventNote the use of JavaScript's weak typing to specify command as a simple value or an array, and value as a simple value or inline function. |
|
Good idea I like the declarative approach, it's much cleaner than my if/if else mess. The option to specify a function as third parameter should cover most if not all switches. I'll update the PR to make your |
Adapted from Ebaauw dresden-elektronik#8631 (comment)
|
With the new const char *PF_button_event = "function button_event(list) {"
"for (var i = 0; i < list.length; i++) { var e = list[i];"
"if (!Array.isArray(e) || e.length !== 3) return;"
"if (ClusterId === e[0] && (ZclFrame.cmd === e[1] || (Array.isArray(e[1]) && e[1].indexOf(ZclFrame.cmd) >= 0))) {"
"Item.val = typeof e[2] === 'function' ? e[2]() : e[2]; return;"
"}} }";
if (duk_peval_string(ctx, PF_button_event) != 0)
{
const char *str = duk_safe_to_string(ctx, -1);
DBG_Printf(DBG_JS, "failed to define button_event: %s\n", str);
}
duk_pop(ctx);Sucessfully tested with Ikea on/off switch. The // move 0x01, move w. onoff 0x05, stop w. onoff 0x07
// [clusterId, commandId, button | function]
button_event([
[6, 1, 1002],
[6, 0, 2002],
[8, 5, 1001],
[8, 7, function(){ return (Item.val === 1001 ? 1003 : 2003); }],
[8, 1, 2001]
])Imho that's less noise compared to former button_maps.json entry: "ikeaOnOffMap": {
"vendor": "IKEA",
"doc": "TRÅDFRI on/off switch",
"modelids": ["TRADFRI on/off switch"],
"buttons": [
{"S_BUTTON_1": "On"},
{"S_BUTTON_2": "Off"}
],
"map": [
[1, "0x01", "ONOFF", "ON", "0", "S_BUTTON_1", "S_BUTTON_ACTION_SHORT_RELEASED", "On"],
[1, "0x01", "LEVEL_CONTROL", "MOVE_WITH_ON_OFF", "0", "S_BUTTON_1", "S_BUTTON_ACTION_HOLD", "Move up (with on/off)"],
[1, "0x01", "LEVEL_CONTROL", "STOP_WITH_ON_OFF", "0", "S_BUTTON_1", "S_BUTTON_ACTION_LONG_RELEASED", "Stop (with on/off)"],
[1, "0x01", "ONOFF", "OFF", "0", "S_BUTTON_2", "S_BUTTON_ACTION_SHORT_RELEASED", "Off"],
[1, "0x01", "LEVEL_CONTROL", "MOVE", "1", "S_BUTTON_2", "S_BUTTON_ACTION_HOLD", "Move down"],
[1, "0x01", "LEVEL_CONTROL", "STOP_WITH_ON_OFF", "1", "S_BUTTON_2", "S_BUTTON_ACTION_LONG_RELEASED", "Stop"]
]
}, |
|
Cool, looking good. Note that I used arrays for the Move and Move (with On/Off) as well as for the Stop and Stop (with On/Off) commands. I've seen devices changing their behaviour with newer firmware, as well as with clones of the same OEM device. I find it weird that the IKEA switch sends Move (down) on hold, but Stop (with On/Off) on release. Are we sure that's correct? |
|
Just realised, we have some switches where the endpoint is used as well. Maybe add a fourth element to the list entries? Logically the endpoint should be the first element, but only a few devices would actually use different endpoints. And probably need an array value for |
|
The DateCode of my switch is 20230308 and SwBuildId is 24.4.6, not sure what is different on older/newer versions. Above code does work for all events here. Note the original button map also only had used ZCL commands 0x00, 0x01 and 0x01, 0x05, 0x07 for level control cluster. I also noticed that 0x07 doesn't have any ZCL payload it's just the commandId. We can add add endpoint to the function (or provide different function since it isn't the common case), there is also the global property |
That should be good enough, I guess. |
The
zcl:clusterDDF parse function allows specifying multiple clusters as filter. Mostly useful for moving switches from button_maps.json to pure DDF implementation.Note this function doesn't support specifying
cmd,mfandattr, these must be handled in respective Javascript handler. Theependpoint parameter is 255 (any source endpoint) by default.Seperate PRs will use this to move switches out of
button_maps.json.Edit: as suggested by @ebaauw there is also a new global Javascript function
button_eventfor declarative handling similar tobutton_maps.json.Example for Ikea on/off switch:
tradfri_on_off_buttonevent.js