Revert scenes to register as switches over mqtt
This commit is contained in:
parent
7c8373d2c7
commit
c646bc55eb
1 changed files with 60 additions and 27 deletions
|
|
@ -13,21 +13,32 @@ const logger = Logger.getLogger('plejd-mqtt');
|
||||||
const discoveryPrefix = 'homeassistant';
|
const discoveryPrefix = 'homeassistant';
|
||||||
const nodeId = 'plejd';
|
const nodeId = 'plejd';
|
||||||
|
|
||||||
|
const MQTT_TYPES = {
|
||||||
|
LIGHT: 'light',
|
||||||
|
SCENE: 'switch', // A bit problematic. Will assume scene if length === guid
|
||||||
|
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 getSubscribePath = () => `${discoveryPrefix}/+/${nodeId}/#`;
|
||||||
const getBaseTopic = (/** @type {{ uniqueId: string; type: string; }} */ plug) => `${discoveryPrefix}/${plug.type}/${nodeId}/${plug.uniqueId}`;
|
|
||||||
|
|
||||||
const getTopicName = (
|
const getTopicName = (
|
||||||
/** @type {{ uniqueId: string; type: string; }} */ plug,
|
/** @type {{ uniqueId: string; type: string; }} */ plug,
|
||||||
/** @type {'config' | 'state' | 'availability' | 'set'} */ topicType,
|
/** @type {'config' | 'state' | 'availability' | 'set'} */ topicType,
|
||||||
) => `${getBaseTopic(plug)}/${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',
|
||||||
AVAILABILITY: 'availability',
|
AVAILABILITY: 'availability',
|
||||||
COMMAND: 'set',
|
COMMAND: 'set',
|
||||||
};
|
};
|
||||||
const getSceneEventTopic = () => 'plejd/event/scene';
|
|
||||||
|
|
||||||
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)/,
|
||||||
|
|
@ -69,10 +80,12 @@ const getScenehDiscoveryPayload = (
|
||||||
) => ({
|
) => ({
|
||||||
name: sceneDevice.name,
|
name: sceneDevice.name,
|
||||||
'~': getBaseTopic(sceneDevice),
|
'~': getBaseTopic(sceneDevice),
|
||||||
|
state_topic: `~/${TOPICS.STATE}`,
|
||||||
command_topic: `~/${TOPICS.COMMAND}`,
|
command_topic: `~/${TOPICS.COMMAND}`,
|
||||||
|
availability_topic: `~/${TOPICS.AVAILABILITY}`,
|
||||||
optimistic: false,
|
optimistic: false,
|
||||||
qos: 1,
|
qos: 1,
|
||||||
retain: true,
|
retain: false,
|
||||||
device: {
|
device: {
|
||||||
identifiers: `${sceneDevice.uniqueId}`,
|
identifiers: `${sceneDevice.uniqueId}`,
|
||||||
manufacturer: 'Plejd',
|
manufacturer: 'Plejd',
|
||||||
|
|
@ -96,6 +109,9 @@ class MqttClient extends EventEmitter {
|
||||||
stateChanged: 'stateChanged',
|
stateChanged: 'stateChanged',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import("DeviceRegistry")} deviceRegistry
|
||||||
|
*/
|
||||||
constructor(deviceRegistry) {
|
constructor(deviceRegistry) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
|
@ -107,8 +123,10 @@ class MqttClient extends EventEmitter {
|
||||||
logger.info('Initializing MQTT connection for Plejd addon');
|
logger.info('Initializing MQTT connection for Plejd addon');
|
||||||
|
|
||||||
this.client = mqtt.connect(this.config.mqttBroker, {
|
this.client = mqtt.connect(this.config.mqttBroker, {
|
||||||
username: this.config.mqttUsername,
|
clientId: `hassio-plejd_${Math.random().toString(16).substr(2, 8)}`,
|
||||||
password: this.config.mqttPassword,
|
password: this.config.mqttPassword,
|
||||||
|
queueQoSZero: true,
|
||||||
|
username: this.config.mqttUsername,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.client.on('error', (err) => {
|
this.client.on('error', (err) => {
|
||||||
|
|
@ -144,28 +162,25 @@ class MqttClient extends EventEmitter {
|
||||||
logger.info('Home Assistant has started. lets do discovery.');
|
logger.info('Home Assistant has started. lets do discovery.');
|
||||||
this.emit(MqttClient.EVENTS.connected);
|
this.emit(MqttClient.EVENTS.connected);
|
||||||
} else {
|
} else {
|
||||||
|
logger.verbose(`Mqtt command ${topic}`);
|
||||||
const decodedTopic = decodeTopic(topic);
|
const decodedTopic = decodeTopic(topic);
|
||||||
if (decodedTopic) {
|
if (decodedTopic) {
|
||||||
let device = this.deviceRegistry.getOutputDevice(decodedTopic.id);
|
/** @type {import('types/DeviceRegistry').OutputDevice} */
|
||||||
|
let device;
|
||||||
|
|
||||||
|
if (decodedTopic.type === 'switch' && isGuid(decodedTopic.id)) {
|
||||||
|
// UUID device id => It's a scene
|
||||||
|
logger.verbose(`Getting scene ${decodedTopic.id} from registry`);
|
||||||
|
device = this.deviceRegistry.getScene(decodedTopic.id);
|
||||||
|
} else {
|
||||||
|
logger.verbose(`Getting device ${decodedTopic.id} from registry`);
|
||||||
|
device = this.deviceRegistry.getOutputDevice(decodedTopic.id);
|
||||||
|
}
|
||||||
|
|
||||||
const messageString = message.toString();
|
const messageString = message.toString();
|
||||||
const isJsonMessage = messageString.startsWith('{');
|
const isJsonMessage = messageString.startsWith('{');
|
||||||
const command = isJsonMessage ? JSON.parse(messageString) : messageString;
|
const command = isJsonMessage ? JSON.parse(messageString) : messageString;
|
||||||
|
|
||||||
if (
|
|
||||||
!isJsonMessage
|
|
||||||
&& messageString === 'ON'
|
|
||||||
&& this.deviceRegistry.getScene(decodedTopic.id)
|
|
||||||
) {
|
|
||||||
// Guess that id that got state command without dim value belongs to Scene, not Device
|
|
||||||
// This guess could very well be wrong depending on the installation...
|
|
||||||
logger.warn(
|
|
||||||
`Device id ${decodedTopic.id} belongs to both scene and device, guessing Scene is what should be set to ON. `
|
|
||||||
+ 'OFF commands still sent to device.',
|
|
||||||
);
|
|
||||||
device = this.deviceRegistry.getScene(decodedTopic.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
const deviceName = device ? device.name : '';
|
const deviceName = device ? device.name : '';
|
||||||
|
|
||||||
switch (decodedTopic.command) {
|
switch (decodedTopic.command) {
|
||||||
|
|
@ -218,7 +233,10 @@ class MqttClient extends EventEmitter {
|
||||||
|
|
||||||
disconnect(callback) {
|
disconnect(callback) {
|
||||||
this.deviceRegistry.getAllOutputDevices().forEach((outputDevice) => {
|
this.deviceRegistry.getAllOutputDevices().forEach((outputDevice) => {
|
||||||
this.client.publish(getTopicName(outputDevice, 'availability'), AVAILABLILITY.OFFLINE);
|
this.client.publish(getTopicName(outputDevice, 'availability'), AVAILABLILITY.OFFLINE, {
|
||||||
|
retain: true,
|
||||||
|
qos: 1,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
this.client.end(callback);
|
this.client.end(callback);
|
||||||
}
|
}
|
||||||
|
|
@ -234,9 +252,15 @@ class MqttClient extends EventEmitter {
|
||||||
`Discovered ${outputDevice.typeName} (${outputDevice.type}) named ${outputDevice.name} (${outputDevice.bleOutputAddress} : ${outputDevice.uniqueId}).`,
|
`Discovered ${outputDevice.typeName} (${outputDevice.type}) named ${outputDevice.name} (${outputDevice.bleOutputAddress} : ${outputDevice.uniqueId}).`,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.client.publish(getTopicName(outputDevice, 'config'), JSON.stringify(configPayload));
|
this.client.publish(getTopicName(outputDevice, 'config'), JSON.stringify(configPayload), {
|
||||||
|
retain: true,
|
||||||
|
qos: 1,
|
||||||
|
});
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.client.publish(getTopicName(outputDevice, 'availability'), AVAILABLILITY.ONLINE);
|
this.client.publish(getTopicName(outputDevice, 'availability'), AVAILABLILITY.ONLINE, {
|
||||||
|
retain: true,
|
||||||
|
qos: 1,
|
||||||
|
});
|
||||||
}, 2000);
|
}, 2000);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -250,9 +274,15 @@ class MqttClient extends EventEmitter {
|
||||||
`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(configPayload), {
|
||||||
|
retain: true,
|
||||||
|
qos: 1,
|
||||||
|
});
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.client.publish(getTopicName(sceneDevice, 'availability'), AVAILABLILITY.ONLINE);
|
this.client.publish(getTopicName(sceneDevice, 'availability'), AVAILABLILITY.ONLINE, {
|
||||||
|
retain: true,
|
||||||
|
qos: 1,
|
||||||
|
});
|
||||||
}, 2000);
|
}, 2000);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -293,8 +323,11 @@ class MqttClient extends EventEmitter {
|
||||||
payload = JSON.stringify(payload);
|
payload = JSON.stringify(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.client.publish(getTopicName(device, 'state'), payload);
|
this.client.publish(getTopicName(device, 'state'), payload, { retain: true, qos: 1 });
|
||||||
this.client.publish(getTopicName(device, 'availability'), AVAILABLILITY.ONLINE);
|
this.client.publish(getTopicName(device, 'availability'), AVAILABLILITY.ONLINE, {
|
||||||
|
retain: true,
|
||||||
|
qos: 1,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -302,7 +335,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 }));
|
this.client.publish(getSceneEventTopic(), JSON.stringify({ scene: sceneId }), { qos: 1 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue