Device triggers for scenes implemented

This commit is contained in:
Victor Hagelbäck 2021-04-26 13:13:10 +02:00
parent b10583336e
commit 74e381f009
2 changed files with 60 additions and 18 deletions

View file

@ -72,6 +72,8 @@ class DeviceRegistry {
...this.sceneDevices, ...this.sceneDevices,
[scene.uniqueId]: scene, [scene.uniqueId]: scene,
}; };
this.sceneUniqueIdByBleOutputAddress[scene.bleOutputAddress] = scene.uniqueId;
logger.verbose( logger.verbose(
`Added/updated scene: ${JSON.stringify(scene)}. ${ `Added/updated scene: ${JSON.stringify(scene)}. ${
Object.keys(this.sceneDevices).length Object.keys(this.sceneDevices).length

View file

@ -19,20 +19,6 @@ const MQTT_TYPES = {
SWITCH: 'switch', SWITCH: 'switch',
}; };
const getMqttType = (/** @type {{ uniqueId: string; type: string; }} */ plug) => (plug.type === 'scene' ? MQTT_TYPES.SCENE : plug.type);
const getBaseTopic = (/** @type {{ uniqueId: string; type: string; }} */ plug) => `${discoveryPrefix}/${getMqttType(plug)}/${nodeId}/${plug.uniqueId}`;
const getSceneEventTopic = () => 'plejd/event/scene';
const getSubscribePath = () => `${discoveryPrefix}/+/${nodeId}/#`;
const getTopicName = (
/** @type {{ uniqueId: string; type: string; }} */ plug,
/** @type {'config' | 'state' | 'availability' | 'set'} */ topicType,
) => `${getBaseTopic(plug)}/${topicType}`;
// Very loosely check if string is a GUID/UUID
const isGuid = (s) => /^\w{8}-\w{4}-\w{4}-\w{4}-\w{12}$/.test(s);
const TOPICS = { const TOPICS = {
CONFIG: 'config', CONFIG: 'config',
STATE: 'state', STATE: 'state',
@ -40,6 +26,21 @@ const TOPICS = {
COMMAND: 'set', COMMAND: 'set',
}; };
const getMqttType = (/** @type {{ uniqueId: string; type: string; }} */ plug) => (plug.type === 'scene' ? MQTT_TYPES.SCENE : plug.type);
const getBaseTopic = (/** @type {{ uniqueId: string; type: string; }} */ plug) => `${discoveryPrefix}/${getMqttType(plug)}/${nodeId}/${plug.uniqueId}`;
const getTopicName = (
/** @type {{ uniqueId: string; type: string; }} */ plug,
/** @type {'config' | 'state' | 'availability' | 'set'} */ topicType,
) => `${getBaseTopic(plug)}/${topicType}`;
const getSceneEventTopic = (sceneId) => `${getTopicName({ uniqueId: `${sceneId}_trigger`, type: 'device_automation' }, 'state')}`;
const getSubscribePath = () => `${discoveryPrefix}/+/${nodeId}/#`;
// Very loosely check if string is a GUID/UUID
const isGuid = (s) => /^\w{8}-\w{4}-\w{4}-\w{4}-\w{12}$/.test(s);
const decodeTopicRegexp = new RegExp( const decodeTopicRegexp = new RegExp(
/(?<prefix>[^[]+)\/(?<type>.+)\/plejd\/(?<id>.+)\/(?<command>config|state|availability|set|scene)/, /(?<prefix>[^[]+)\/(?<type>.+)\/plejd\/(?<id>.+)\/(?<command>config|state|availability|set|scene)/,
); );
@ -75,7 +76,7 @@ const getLightDiscoveryPayload = (
}, },
}); });
const getScenehDiscoveryPayload = ( const getSceneDiscoveryPayload = (
/** @type {import('./types/DeviceRegistry').OutputDevice} */ sceneDevice, /** @type {import('./types/DeviceRegistry').OutputDevice} */ sceneDevice,
) => ({ ) => ({
name: sceneDevice.name, name: sceneDevice.name,
@ -88,6 +89,26 @@ const getScenehDiscoveryPayload = (
retain: false, retain: false,
}); });
const getSceneDeviceTriggerhDiscoveryPayload = (
/** @type {import('./types/DeviceRegistry').OutputDevice} */ sceneDevice,
) => ({
automation_type: 'trigger',
'~': getBaseTopic({
uniqueId: sceneDevice.uniqueId,
type: 'device_automation',
}),
qos: 1,
topic: `~/${TOPICS.STATE}`,
type: 'scene',
subtype: 'trigger',
device: {
identifiers: `${sceneDevice.uniqueId}`,
manufacturer: 'Plejd',
model: sceneDevice.typeName,
name: sceneDevice.name,
},
});
// #endregion // #endregion
const getMqttStateString = (/** @type {boolean} */ state) => (state ? 'ON' : 'OFF'); const getMqttStateString = (/** @type {boolean} */ state) => (state ? 'ON' : 'OFF');
@ -272,15 +293,34 @@ class MqttClient extends EventEmitter {
allSceneDevices.forEach((sceneDevice) => { allSceneDevices.forEach((sceneDevice) => {
logger.debug(`Sending discovery for ${sceneDevice.name}`); logger.debug(`Sending discovery for ${sceneDevice.name}`);
const configPayload = getScenehDiscoveryPayload(sceneDevice); const sceneConfigPayload = getSceneDiscoveryPayload(sceneDevice);
logger.info( logger.info(
`Discovered ${sceneDevice.typeName} (${sceneDevice.type}) named ${sceneDevice.name} (${sceneDevice.bleOutputAddress} : ${sceneDevice.uniqueId}).`, `Discovered ${sceneDevice.typeName} (${sceneDevice.type}) named ${sceneDevice.name} (${sceneDevice.bleOutputAddress} : ${sceneDevice.uniqueId}).`,
); );
this.client.publish(getTopicName(sceneDevice, 'config'), JSON.stringify(configPayload), { this.client.publish(getTopicName(sceneDevice, 'config'), JSON.stringify(sceneConfigPayload), {
retain: true, retain: true,
qos: 1, qos: 1,
}); });
const sceneTriggerConfigPayload = getSceneDeviceTriggerhDiscoveryPayload(sceneDevice);
this.client.publish(
getTopicName(
{
...sceneDevice,
uniqueId: `${sceneDevice.uniqueId}_trigger`,
type: 'device_automation',
},
'config',
),
JSON.stringify(sceneTriggerConfigPayload),
{
retain: true,
qos: 1,
},
);
setTimeout(() => { setTimeout(() => {
this.client.publish(getTopicName(sceneDevice, 'availability'), AVAILABLILITY.ONLINE, { this.client.publish(getTopicName(sceneDevice, 'availability'), AVAILABLILITY.ONLINE, {
retain: true, retain: true,
@ -338,7 +378,7 @@ class MqttClient extends EventEmitter {
*/ */
sceneTriggered(sceneId) { sceneTriggered(sceneId) {
logger.verbose(`Scene triggered: ${sceneId}`); logger.verbose(`Scene triggered: ${sceneId}`);
this.client.publish(getSceneEventTopic(), JSON.stringify({ scene: sceneId }), { qos: 1 }); this.client.publish(getSceneEventTopic(sceneId), '', { qos: 1 });
} }
} }