A little tip for fellow Node Developers

My Zwave Node is going through a massive cleanse (v10), and in doing so optimising A LOT.
My node subscribes to 20+ comm API messages from the runtime counterparts, to facilitate the UI

I needed a way to shrink this and make it more manageable

  • Subscribing
  • Unsubscribing (Yes!!! I clean things up :wink:)

I present a little snippet for you all to benefit from - for me it works really well!

Enjoy

const setSubscription = (subscribe) => {
		const Hooks = [
			{ address: `zwave-js/ui/${networkId}/status`, method: commsStatus },
			{ address: `zwave-js/ui/${networkId}/s2/grant`, method: commsGrant },
			{ address: `zwave-js/ui/${networkId}/s2/dsk`, method: commsDSK },
			{ Another },
			{ Another },
			{ Another * 20 more }
		];

		switch (subscribe) {
			case true:
				Hooks.forEach((H) => {
					RED.comms.subscribe(H.address, H.method);
				});
				break;

			case false:
				Hooks.forEach((H) => {
					RED.comms.unsubscribe(H.address, H.method);
				});
                break;
		}
}

/* Example */
setSubscription(true | false)

/* Example  responder */
const commsDSK = (topic, data) =>{
   // do stuff
}

Full example:

const setSubscription = (subscribe) => {
		const Hooks = [
			{ address: `zwave-js/ui/${networkId}/status`, method: commsStatus },
			{ address: `zwave-js/ui/${networkId}/s2/grant`, method: commsGrant },
			{ address: `zwave-js/ui/${networkId}/s2/dsk`, method: commsDSK },
			{ address: `zwave-js/ui/${networkId}/nodes/added`, method: commsNodeAdded },
			{ address: `zwave-js/ui/${networkId}/nodes/removed`, method: commsNodeRemoved },
			{ address: `zwave-js/ui/${networkId}/nodes/interviewstarted`, method: commsNodeState },
			{ address: `zwave-js/ui/${networkId}/nodes/interviewfailed`, method: commsNodeState },
			{ address: `zwave-js/ui/${networkId}/nodes/interviewed`, method: commsNodeState },
			{ address: `zwave-js/ui/${networkId}/nodes/ready`, method: commsNodeState },
			{ address: `zwave-js/ui/${networkId}/nodes/sleep`, method: commsNodeState },
			{ address: `zwave-js/ui/${networkId}/nodes/awake`, method: commsNodeState },
			{ address: `zwave-js/ui/${networkId}/nodes/dead`, method: commsNodeState },
			{ address: `zwave-js/ui/${networkId}/controller/slave/dsk`, method: commsHandleSlaveOps },
			{ address: `zwave-js/ui/${networkId}/controller/slave/joined`, method: commsHandleSlaveOps },
			{ address: `zwave-js/ui/${networkId}/controller/slave/left`, method: commsHandleSlaveOps },
			{ address: `zwave-js/ui/${networkId}/nodes/valueadded`, method: commsHandleValueUpdate },
			{ address: `zwave-js/ui/${networkId}/nodes/valueupdate`, method: commsHandleValueUpdate },
			{ address: `zwave-js/ui/${networkId}/controller/nvm/backupprogress`, method: commsNVMBackupProgressReport },
			{ address: `zwave-js/ui/${networkId}/controller/nvm/restoreprogress`, method: commsNVMRestoreProgressReport },
			{ address: `zwave-js/ui/${networkId}/controller/firmwareupdate/progress`, method: commsCFirmwareReport },
			{ address: `zwave-js/ui/${networkId}/controller/firmwareupdate/finished`, method: commsCFirmwareReport }
		];

		switch (subscribe) {
			case true:
				Hooks.forEach((H) => {
					RED.comms.subscribe(H.address, H.method);
				});
				break;
			case false:
				Hooks.forEach((H) => {
					RED.comms.unsubscribe(H.address, H.method);
				});
                break;
		}
};

Usage

setSubscription(true | false)

again - enjoy

1 Like

You could remove the switch

const setSubscription = (subscribe) => {
		const Hooks = [
			{ address: `zwave-js/ui/${networkId}/status`, method: commsStatus },
			{ address: `zwave-js/ui/${networkId}/s2/grant`, method: commsGrant },
			{ address: `zwave-js/ui/${networkId}/s2/dsk`, method: commsDSK },
			{ address: `zwave-js/ui/${networkId}/nodes/added`, method: commsNodeAdded },
			{ address: `zwave-js/ui/${networkId}/nodes/removed`, method: commsNodeRemoved },
			{ address: `zwave-js/ui/${networkId}/nodes/interviewstarted`, method: commsNodeState },
			{ address: `zwave-js/ui/${networkId}/nodes/interviewfailed`, method: commsNodeState },
			{ address: `zwave-js/ui/${networkId}/nodes/interviewed`, method: commsNodeState },
			{ address: `zwave-js/ui/${networkId}/nodes/ready`, method: commsNodeState },
			{ address: `zwave-js/ui/${networkId}/nodes/sleep`, method: commsNodeState },
			{ address: `zwave-js/ui/${networkId}/nodes/awake`, method: commsNodeState },
			{ address: `zwave-js/ui/${networkId}/nodes/dead`, method: commsNodeState },
			{ address: `zwave-js/ui/${networkId}/controller/slave/dsk`, method: commsHandleSlaveOps },
			{ address: `zwave-js/ui/${networkId}/controller/slave/joined`, method: commsHandleSlaveOps },
			{ address: `zwave-js/ui/${networkId}/controller/slave/left`, method: commsHandleSlaveOps },
			{ address: `zwave-js/ui/${networkId}/nodes/valueadded`, method: commsHandleValueUpdate },
			{ address: `zwave-js/ui/${networkId}/nodes/valueupdate`, method: commsHandleValueUpdate },
			{ address: `zwave-js/ui/${networkId}/controller/nvm/backupprogress`, method: commsNVMBackupProgressReport },
			{ address: `zwave-js/ui/${networkId}/controller/nvm/restoreprogress`, method: commsNVMRestoreProgressReport },
			{ address: `zwave-js/ui/${networkId}/controller/firmwareupdate/progress`, method: commsCFirmwareReport },
			{ address: `zwave-js/ui/${networkId}/controller/firmwareupdate/finished`, method: commsCFirmwareReport }
		];
        const op = RED.comms[subscribe ? "subscribe" : "unsubscribe"];

	Hooks.forEach((H) => op(H.address, H.method));
				
		}
};
1 Like

Evan smaller - Thanks @AllanOricil

1 Like

"and this... is... to go... even further beyond" (ssj3 ref)

const op = RED.comms[(subscribe ? "" : "un") + "subscribe"];

@AllanOricil

Went with your 1st.

For all - makes managing your comm subscriptions easier to manage.

const setSubscription = (subscribe) => {
		const Hooks = [
			{ address: `zwave-js/ui/${networkId}/status`, method: commsStatus },
			{ address: `zwave-js/ui/${networkId}/s2/grant`, method: commsGrant },
			{ address: `zwave-js/ui/${networkId}/s2/dsk`, method: commsDSK },
			{ address: `zwave-js/ui/${networkId}/nodes/added`, method: commsNodeAdded },
			{ address: `zwave-js/ui/${networkId}/nodes/removed`, method: commsNodeRemoved },
			{ address: `zwave-js/ui/${networkId}/nodes/interviewstarted`, method: commsNodeState },
			{ address: `zwave-js/ui/${networkId}/nodes/interviewfailed`, method: commsNodeState },
			{ address: `zwave-js/ui/${networkId}/nodes/interviewed`, method: commsNodeState },
			{ address: `zwave-js/ui/${networkId}/nodes/ready`, method: commsNodeState },
			{ address: `zwave-js/ui/${networkId}/nodes/sleep`, method: commsNodeState },
			{ address: `zwave-js/ui/${networkId}/nodes/awake`, method: commsNodeState },
			{ address: `zwave-js/ui/${networkId}/nodes/dead`, method: commsNodeState },
			{ address: `zwave-js/ui/${networkId}/controller/slave/dsk`, method: commsHandleSlaveOps },
			{ address: `zwave-js/ui/${networkId}/controller/slave/joined`, method: commsHandleSlaveOps },
			{ address: `zwave-js/ui/${networkId}/controller/slave/left`, method: commsHandleSlaveOps },
			{ address: `zwave-js/ui/${networkId}/nodes/valueadded`, method: commsHandleValueUpdate },
			{ address: `zwave-js/ui/${networkId}/nodes/valueupdate`, method: commsHandleValueUpdate },
			{ address: `zwave-js/ui/${networkId}/controller/nvm/backupprogress`, method: commsNVMBackupProgressReport },
			{ address: `zwave-js/ui/${networkId}/controller/nvm/restoreprogress`, method: commsNVMRestoreProgressReport },
			{ address: `zwave-js/ui/${networkId}/controller/firmwareupdate/progress`, method: commsCFirmwareReport },
			{ address: `zwave-js/ui/${networkId}/controller/firmwareupdate/finished`, method: commsCFirmwareReport }
		];

		const op = RED.comms[subscribe ? 'subscribe' : 'unsubscribe'];
		Hooks.forEach((H) => op(H.address, H.method));
};

Example

setSubscription(true | false)

Just add all your mappings

If you named your functions comms_s2_grant, comms_s2_dsk, comms_nodes_added you could have done this in a maintenace free way:

const setSubscription = (subscribe) => {
	const op = RED.comms[subscribe ? 'subscribe' : 'unsubscribe'];
	Object.entries(this)
		.filter(([key, val]) => typeof val === 'function' && key.startsWith('comms_'))
		.forEach(([key, handler]) => {
			const path = key.split("_").slice(1).join('/') // eg controller/slave/dsk
			op(`zwave-js/ui/${networkId}/${path}`, handler)
		})
}

Any future method you add that needs hooking up, just name it comms_this_that()

assumptions about functions being iin this scope were made - but hopefully you get the idea

3 Likes

I thought we were alright @Steve-Mcl, then you come in and undermime my work, and clever thinking!

:winking_face_with_tongue:

Nice! - I do play with the approaches you wrote every so often, I saw low hanging fruit, and went for it.

But Nice! maybe I could optimise it further later with that - thank you :folded_hands:

2 Likes

You do know that anytime now, someone's gonna post a 1 line jsonata solution for you....

We all do. I learn so much from guys like @Steve-Mcl who make nice forum contributions.

3 Likes

The joys of working full-time with node.js/JavaScript. :smiley: The rest of us plod along, slowly learning over the years. But then, that's a major part of the joy for me at least! :mage:

3 Likes