Improve MQTT behavior and add experimental parsing for color temp support from Plejd API (#298)
* Update underlying docker containers and dependencies. * Minor linting and code issues fixes * Update supported devices section * Improve Mqtt message properties - retain, etc. - Retain discovery messages - Don't retain others - Set QoS to 1 consistently to ensure at least once delivery - Set session timeout to ensure a reasonable TTL on messages * Code and logic to remove any retained mqtt messages for SET/STATE/AVAILABILITY * Temporary restructure of init flow for mqtt. - No longer wait for HA birth message - Don't listen to incoming messages until old retained messages have been purged. - More details in https://github.com/icanos/hassio-plejd/issues/218 * Fix lingering incorrect access of connectedDevice.id * Fix to avoid Home Assistant setting retain flag on MQTT SET STATE messages * Parse new TRAIT=15, assuming this means dimmable and tunable white * Clarify TRAITS bitmask values * Add experimental parsing for color temp support from Plejd API * Lint fixes * Remove null coalescing operator since we don't compile code * Handle case where outputSettings is null in PlejdApi * Solve MQTT errors due to deprecated api color_temp and unsupported removal of retained state messages
This commit is contained in:
parent
a789d913d7
commit
b3c6334f0c
10 changed files with 255 additions and 147 deletions
|
|
@ -4,7 +4,7 @@ const mqtt = require('mqtt');
|
|||
const Configuration = require('./Configuration');
|
||||
const Logger = require('./Logger');
|
||||
|
||||
const startTopics = ['hass/status', 'homeassistant/status'];
|
||||
// const startTopics = ['hass/status', 'homeassistant/status'];
|
||||
|
||||
const logger = Logger.getLogger('plejd-mqtt');
|
||||
|
||||
|
|
@ -24,7 +24,7 @@ const TOPIC_TYPES = {
|
|||
CONFIG: 'config',
|
||||
STATE: 'state',
|
||||
AVAILABILITY: 'availability',
|
||||
COMMAND: 'set',
|
||||
SET: 'set',
|
||||
};
|
||||
|
||||
const getBaseTopic = (/** @type { string } */ uniqueId, /** @type { string } */ mqttDeviceType) =>
|
||||
|
|
@ -43,9 +43,8 @@ const getSceneEventTopic = (/** @type {string} */ sceneId) =>
|
|||
`${getTopicName(getTriggerUniqueId(sceneId), MQTT_TYPES.DEVICE_AUTOMATION, TOPIC_TYPES.STATE)}`;
|
||||
const getSubscribePath = () => `${discoveryPrefix}/+/${nodeId}/#`;
|
||||
|
||||
const decodeTopicRegexp = new RegExp(
|
||||
/(?<prefix>[^[]+)\/(?<type>.+)\/plejd\/(?<id>.+)\/(?<command>config|state|availability|set|scene)/,
|
||||
);
|
||||
const decodeTopicRegexp =
|
||||
/(?<prefix>[^[]+)\/(?<type>.+)\/plejd\/(?<id>.+)\/(?<command>config|state|availability|set|scene)/;
|
||||
|
||||
const decodeTopic = (topic) => {
|
||||
const matches = decodeTopicRegexp.exec(topic);
|
||||
|
|
@ -62,11 +61,11 @@ const getOutputDeviceDiscoveryPayload = (
|
|||
unique_id: device.uniqueId,
|
||||
'~': getBaseTopic(device.uniqueId, device.type),
|
||||
state_topic: `~/${TOPIC_TYPES.STATE}`,
|
||||
command_topic: `~/${TOPIC_TYPES.COMMAND}`,
|
||||
command_topic: `~/${TOPIC_TYPES.SET}`,
|
||||
availability_topic: `~/${TOPIC_TYPES.AVAILABILITY}`,
|
||||
optimistic: false,
|
||||
qos: 1,
|
||||
retain: true,
|
||||
retain: false, // State update messages from HA should not be retained
|
||||
device: {
|
||||
identifiers: `${device.uniqueId}`,
|
||||
manufacturer: 'Plejd',
|
||||
|
|
@ -76,6 +75,15 @@ const getOutputDeviceDiscoveryPayload = (
|
|||
sw_version: device.version,
|
||||
},
|
||||
...(device.type === MQTT_TYPES.LIGHT ? { brightness: device.dimmable, schema: 'json' } : {}),
|
||||
...(device.type === MQTT_TYPES.LIGHT &&
|
||||
device.colorTempSettings &&
|
||||
device.colorTempSettings.behavior === 'adjustable'
|
||||
? {
|
||||
min_mireds: 1000000 / device.colorTempSettings.minTemperatureLimit,
|
||||
max_mireds: 1000000 / device.colorTempSettings.maxTemperatureLimit,
|
||||
supported_color_modes: ['color_temp'],
|
||||
}
|
||||
: {}),
|
||||
});
|
||||
|
||||
const getSceneDiscoveryPayload = (
|
||||
|
|
@ -84,11 +92,11 @@ const getSceneDiscoveryPayload = (
|
|||
name: sceneDevice.name,
|
||||
unique_id: sceneDevice.uniqueId,
|
||||
'~': getBaseTopic(sceneDevice.uniqueId, MQTT_TYPES.SCENE),
|
||||
command_topic: `~/${TOPIC_TYPES.COMMAND}`,
|
||||
command_topic: `~/${TOPIC_TYPES.SET}`,
|
||||
availability_topic: `~/${TOPIC_TYPES.AVAILABILITY}`,
|
||||
payload_on: 'ON',
|
||||
qos: 1,
|
||||
retain: false,
|
||||
retain: false, // State update messages from HA should not be retained
|
||||
});
|
||||
|
||||
const getInputDeviceTriggerDiscoveryPayload = (
|
||||
|
|
@ -98,9 +106,10 @@ const getInputDeviceTriggerDiscoveryPayload = (
|
|||
payload: `${inputDevice.input}`,
|
||||
'~': getBaseTopic(inputDevice.deviceId, MQTT_TYPES.DEVICE_AUTOMATION),
|
||||
qos: 1,
|
||||
retain: true, // Discovery messages should be retained to account for HA restarts
|
||||
subtype: `button_${inputDevice.input + 1}`,
|
||||
topic: `~/${TOPIC_TYPES.STATE}`,
|
||||
type: 'button_short_press',
|
||||
subtype: `button_${inputDevice.input + 1}`,
|
||||
device: {
|
||||
identifiers: `${inputDevice.deviceId}`,
|
||||
manufacturer: 'Plejd',
|
||||
|
|
@ -115,6 +124,7 @@ const getSceneDeviceTriggerhDiscoveryPayload = (
|
|||
automation_type: 'trigger',
|
||||
'~': getBaseTopic(`${sceneDevice.uniqueId}_trig`, MQTT_TYPES.DEVICE_AUTOMATION),
|
||||
qos: 1,
|
||||
retain: true, // Discovery messages should be retained to account for HA restarts
|
||||
topic: `~/${TOPIC_TYPES.STATE}`,
|
||||
type: 'scene',
|
||||
subtype: 'trigger',
|
||||
|
|
@ -127,7 +137,7 @@ const getSceneDeviceTriggerhDiscoveryPayload = (
|
|||
});
|
||||
|
||||
const getMqttStateString = (/** @type {boolean} */ state) => (state ? 'ON' : 'OFF');
|
||||
const AVAILABLILITY = { ONLINE: 'online', OFFLINE: 'offline' };
|
||||
const AVAILABLITY = { ONLINE: 'online', OFFLINE: 'offline' };
|
||||
|
||||
class MqttClient extends EventEmitter {
|
||||
/** @type {import('DeviceRegistry')} */
|
||||
|
|
@ -152,9 +162,13 @@ class MqttClient extends EventEmitter {
|
|||
logger.info('Initializing MQTT connection for Plejd addon');
|
||||
|
||||
this.client = mqtt.connect(this.config.mqttBroker, {
|
||||
clean: true, // We're moving to not saving mqtt messages
|
||||
clientId: `hassio-plejd_${Math.random().toString(16).substr(2, 8)}`,
|
||||
password: this.config.mqttPassword,
|
||||
protocolVersion: 4, // v5 not supported by HassIO Mosquitto
|
||||
properties: {
|
||||
sessionExpiryInterval: 120, // 2 minutes sessions for the QoS, after that old messages are discarded
|
||||
},
|
||||
protocolVersion: 5,
|
||||
queueQoSZero: true,
|
||||
username: this.config.mqttUsername,
|
||||
});
|
||||
|
|
@ -166,28 +180,25 @@ class MqttClient extends EventEmitter {
|
|||
this.client.on('connect', () => {
|
||||
logger.info('Connected to MQTT.');
|
||||
|
||||
this.client.subscribe(
|
||||
startTopics,
|
||||
// Add below when mqtt v5 is supported in Mosquitto 1.6 or 2.0 and forward
|
||||
// {
|
||||
// qos: 1,
|
||||
// nl: true, // don't echo back messages sent
|
||||
// rap: true, // retain as published - don't force retain = 0
|
||||
// },
|
||||
(err) => {
|
||||
if (err) {
|
||||
logger.error('Unable to subscribe to status topics', err);
|
||||
}
|
||||
this.emit(MqttClient.EVENTS.connected);
|
||||
|
||||
this.emit(MqttClient.EVENTS.connected);
|
||||
},
|
||||
);
|
||||
// Testing to skip listening to HA birth messages all together
|
||||
// this.client.subscribe(
|
||||
// startTopics,
|
||||
// {
|
||||
// qos: 1,
|
||||
// nl: true, // don't echo back messages sent
|
||||
// rap: true, // retain as published - don't force retain = 0
|
||||
// rh: 0, // Retain handling 0 presumably ignores retained messages
|
||||
// },
|
||||
// (err) => {
|
||||
// if (err) {
|
||||
// logger.error('Unable to subscribe to status topics', err);
|
||||
// }
|
||||
|
||||
this.client.subscribe(getSubscribePath(), (err) => {
|
||||
if (err) {
|
||||
logger.error('Unable to subscribe to control topics');
|
||||
}
|
||||
});
|
||||
// this.emit(MqttClient.EVENTS.connected);
|
||||
// },
|
||||
// );
|
||||
});
|
||||
|
||||
this.client.on('close', () => {
|
||||
|
|
@ -197,64 +208,60 @@ class MqttClient extends EventEmitter {
|
|||
|
||||
this.client.on('message', (topic, message) => {
|
||||
try {
|
||||
if (startTopics.includes(topic)) {
|
||||
logger.info('Home Assistant has started. lets do discovery.');
|
||||
this.emit(MqttClient.EVENTS.connected);
|
||||
} else {
|
||||
logger.verbose(`Received mqtt message on ${topic}`);
|
||||
const decodedTopic = decodeTopic(topic);
|
||||
if (decodedTopic) {
|
||||
/** @type {import('types/DeviceRegistry').OutputDevice} */
|
||||
let device;
|
||||
logger.verbose(`Received mqtt message on ${topic}`);
|
||||
const decodedTopic = decodeTopic(topic);
|
||||
if (decodedTopic) {
|
||||
/** @type {import('types/DeviceRegistry').OutputDevice} */
|
||||
let device;
|
||||
|
||||
if (decodedTopic.type === MQTT_TYPES.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 isJsonMessage = messageString.startsWith('{');
|
||||
const command = isJsonMessage ? JSON.parse(messageString) : messageString;
|
||||
|
||||
const deviceName = device ? device.name : '';
|
||||
|
||||
switch (decodedTopic.command) {
|
||||
case 'set':
|
||||
logger.verbose(
|
||||
`Got mqtt SET command for ${decodedTopic.type}, ${deviceName} (${decodedTopic.id}): ${messageString}`,
|
||||
);
|
||||
|
||||
if (device) {
|
||||
this.emit(MqttClient.EVENTS.stateChanged, device, command);
|
||||
} else {
|
||||
logger.warn(
|
||||
`Device for topic ${topic} not found! Can happen if HA calls previously existing devices.`,
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'state':
|
||||
case 'config':
|
||||
case 'availability':
|
||||
logger.verbose(
|
||||
`Sent mqtt ${decodedTopic.command} command for ${
|
||||
decodedTopic.type
|
||||
}, ${deviceName} (${decodedTopic.id}). ${
|
||||
decodedTopic.command === 'availability' ? messageString : ''
|
||||
}`,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
logger.verbose(`Warning: Unknown command ${decodedTopic.command} in decoded topic`);
|
||||
}
|
||||
if (decodedTopic.type === MQTT_TYPES.SCENE) {
|
||||
logger.verbose(`Getting scene ${decodedTopic.id} from registry`);
|
||||
device = this.deviceRegistry.getScene(decodedTopic.id);
|
||||
} else {
|
||||
logger.verbose(
|
||||
`Warning: Got unrecognized mqtt command on '${topic}': ${message.toString()}`,
|
||||
);
|
||||
logger.verbose(`Getting device ${decodedTopic.id} from registry`);
|
||||
device = this.deviceRegistry.getOutputDevice(decodedTopic.id);
|
||||
}
|
||||
|
||||
const messageString = message.toString();
|
||||
const isJsonMessage = messageString.startsWith('{');
|
||||
const command = isJsonMessage ? JSON.parse(messageString) : messageString;
|
||||
|
||||
const deviceName = device ? device.name : '';
|
||||
|
||||
switch (decodedTopic.command) {
|
||||
case 'set':
|
||||
logger.verbose(
|
||||
`Got mqtt SET command for ${decodedTopic.type}, ${deviceName} (${decodedTopic.id}): ${messageString}`,
|
||||
);
|
||||
|
||||
if (device) {
|
||||
this.emit(MqttClient.EVENTS.stateChanged, device, command);
|
||||
} else {
|
||||
logger.warn(
|
||||
`Device for topic ${topic} not found! Can happen if HA calls previously existing devices.`,
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'state':
|
||||
case 'config':
|
||||
case 'availability':
|
||||
logger.verbose(
|
||||
`Sent mqtt ${decodedTopic.command} command for ${
|
||||
decodedTopic.type
|
||||
}, ${deviceName} (${decodedTopic.id}). ${
|
||||
decodedTopic.command === 'availability' ? messageString : ''
|
||||
}`,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
logger.verbose(`Warning: Unknown command ${decodedTopic.command} in decoded topic`);
|
||||
}
|
||||
} else {
|
||||
logger.verbose(
|
||||
`Warning: Got unrecognized mqtt command on '${topic}': ${message.toString()}`,
|
||||
);
|
||||
}
|
||||
// }
|
||||
} catch (err) {
|
||||
logger.error(`Error processing mqtt message on topic ${topic}`, err);
|
||||
}
|
||||
|
|
@ -275,7 +282,7 @@ class MqttClient extends EventEmitter {
|
|||
const mqttType = outputDevice.type === 'switch' ? MQTT_TYPES.SWITCH : MQTT_TYPES.LIGHT;
|
||||
this.client.publish(
|
||||
getTopicName(outputDevice.uniqueId, mqttType, 'availability'),
|
||||
AVAILABLILITY.OFFLINE,
|
||||
AVAILABLITY.OFFLINE,
|
||||
{
|
||||
retain: true,
|
||||
qos: 1,
|
||||
|
|
@ -287,7 +294,7 @@ class MqttClient extends EventEmitter {
|
|||
allSceneDevices.forEach((sceneDevice) => {
|
||||
this.client.publish(
|
||||
getTopicName(sceneDevice.uniqueId, MQTT_TYPES.SCENE, TOPIC_TYPES.AVAILABILITY),
|
||||
AVAILABLILITY.OFFLINE,
|
||||
AVAILABLITY.OFFLINE,
|
||||
{
|
||||
retain: true,
|
||||
qos: 1,
|
||||
|
|
@ -298,37 +305,67 @@ class MqttClient extends EventEmitter {
|
|||
}
|
||||
|
||||
sendDiscoveryToHomeAssistant() {
|
||||
// -------- DISCOVERY FOR OUTPUT DEVICES -------------
|
||||
|
||||
const allOutputDevices = this.deviceRegistry.getAllOutputDevices();
|
||||
logger.info(`Sending discovery for ${allOutputDevices.length} Plejd output devices`);
|
||||
allOutputDevices.forEach((outputDevice) => {
|
||||
logger.debug(`Sending discovery for ${outputDevice.name}`);
|
||||
|
||||
const configPayload = getOutputDeviceDiscoveryPayload(outputDevice);
|
||||
logger.info(
|
||||
`Discovered ${outputDevice.typeName} (${outputDevice.type}) named ${outputDevice.name} (${outputDevice.bleOutputAddress} : ${outputDevice.uniqueId}).`,
|
||||
);
|
||||
|
||||
// Publish mqtt CONFIG message which will create the device in Home Assistant
|
||||
const mqttType = outputDevice.type === 'switch' ? MQTT_TYPES.SWITCH : MQTT_TYPES.LIGHT;
|
||||
this.client.publish(
|
||||
getTopicName(outputDevice.uniqueId, mqttType, TOPIC_TYPES.CONFIG),
|
||||
JSON.stringify(configPayload),
|
||||
{
|
||||
retain: true,
|
||||
retain: true, // Discovery messages should be retained to account for HA and MQTT broker restarts
|
||||
qos: 1,
|
||||
},
|
||||
);
|
||||
|
||||
logger.info(
|
||||
`Sent discovery message for ${outputDevice.typeName} (${outputDevice.type}) named ${outputDevice.name} (${outputDevice.bleOutputAddress} : ${outputDevice.uniqueId}).`,
|
||||
);
|
||||
|
||||
// -------- CLEANUP RETAINED MESSAGES FOR OUTPUT DEVICES -------------
|
||||
|
||||
logger.debug(
|
||||
`Forcefully removing any retained SET, STATE, and AVAILABILITY messages for ${outputDevice.name}`,
|
||||
);
|
||||
|
||||
// Forcefully remove retained (from Home Assistant) SET messages (wanted state from HA)
|
||||
this.client.publish(getTopicName(outputDevice.uniqueId, mqttType, TOPIC_TYPES.SET), null, {
|
||||
retain: true, // Retain true to remove previously retained message
|
||||
qos: 1,
|
||||
});
|
||||
|
||||
// Forcefully remove retained (from us, v0.11 and before) AVAILABILITY messages
|
||||
this.client.publish(
|
||||
getTopicName(outputDevice.uniqueId, mqttType, TOPIC_TYPES.AVAILABLILITY),
|
||||
null,
|
||||
{
|
||||
retain: true, // Retain true to remove previously retained message
|
||||
qos: 1,
|
||||
},
|
||||
);
|
||||
|
||||
logger.debug(`Removal messages sent for ${outputDevice.name}`);
|
||||
|
||||
logger.debug(`Setting device as AVAILABILITY = ONLINE: ${outputDevice.name}`);
|
||||
|
||||
this.client.publish(
|
||||
getTopicName(outputDevice.uniqueId, mqttType, TOPIC_TYPES.AVAILABILITY),
|
||||
AVAILABLITY.ONLINE,
|
||||
{
|
||||
retain: false, // Availability messages should NOT be retained
|
||||
qos: 1,
|
||||
},
|
||||
);
|
||||
setTimeout(() => {
|
||||
this.client.publish(
|
||||
getTopicName(outputDevice.uniqueId, mqttType, TOPIC_TYPES.AVAILABILITY),
|
||||
AVAILABLILITY.ONLINE,
|
||||
{
|
||||
retain: true,
|
||||
qos: 1,
|
||||
},
|
||||
);
|
||||
}, 2000);
|
||||
});
|
||||
|
||||
// -------- DISCOVERY FOR INPUT DEVICES -------------
|
||||
|
||||
const allInputDevices = this.deviceRegistry.getAllInputDevices();
|
||||
logger.info(`Sending discovery for ${allInputDevices.length} Plejd input devices`);
|
||||
allInputDevices.forEach((inputDevice) => {
|
||||
|
|
@ -349,12 +386,14 @@ class MqttClient extends EventEmitter {
|
|||
getTopicName(inputDevice.uniqueId, MQTT_TYPES.DEVICE_AUTOMATION, TOPIC_TYPES.CONFIG),
|
||||
JSON.stringify(inputInputPayload),
|
||||
{
|
||||
retain: true,
|
||||
retain: true, // Discovery messages should be retained to account for HA restarts
|
||||
qos: 1,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
// -------- DISCOVERY FOR SCENE DEVICES -------------
|
||||
|
||||
const allSceneDevices = this.deviceRegistry.getAllSceneDevices();
|
||||
logger.info(`Sending discovery for ${allSceneDevices.length} Plejd scene devices`);
|
||||
allSceneDevices.forEach((sceneDevice) => {
|
||||
|
|
@ -369,7 +408,7 @@ class MqttClient extends EventEmitter {
|
|||
getTopicName(sceneDevice.uniqueId, MQTT_TYPES.SCENE, TOPIC_TYPES.CONFIG),
|
||||
JSON.stringify(sceneConfigPayload),
|
||||
{
|
||||
retain: true,
|
||||
retain: true, // Discovery messages should be retained to account for HA restarts
|
||||
qos: 1,
|
||||
},
|
||||
);
|
||||
|
|
@ -384,22 +423,39 @@ class MqttClient extends EventEmitter {
|
|||
),
|
||||
JSON.stringify(sceneTriggerConfigPayload),
|
||||
{
|
||||
retain: true,
|
||||
retain: true, // Discovery messages should be retained to account for HA restarts
|
||||
qos: 1,
|
||||
},
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
this.client.publish(
|
||||
getTopicName(sceneDevice.uniqueId, MQTT_TYPES.SCENE, TOPIC_TYPES.AVAILABILITY),
|
||||
AVAILABLILITY.ONLINE,
|
||||
{
|
||||
retain: true,
|
||||
qos: 1,
|
||||
},
|
||||
);
|
||||
}, 2000);
|
||||
// setTimeout(() => {
|
||||
this.client.publish(
|
||||
getTopicName(sceneDevice.uniqueId, MQTT_TYPES.SCENE, TOPIC_TYPES.AVAILABILITY),
|
||||
AVAILABLITY.ONLINE,
|
||||
{
|
||||
retain: true, // Discovery messages should be retained to account for HA restarts
|
||||
qos: 1,
|
||||
},
|
||||
);
|
||||
// }, 2000);
|
||||
});
|
||||
|
||||
// -------- SUBSCRIBE TO INCOMING MESSAGES -------------
|
||||
|
||||
this.client.subscribe(
|
||||
getSubscribePath(),
|
||||
{
|
||||
qos: 1,
|
||||
nl: true, // don't echo back messages sent
|
||||
rap: true, // retain as published - don't force retain = 0
|
||||
rh: 0, // Retain handling 0 presumably ignores retained messages
|
||||
},
|
||||
(err) => {
|
||||
if (err) {
|
||||
logger.error('Unable to subscribe to control topics');
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -440,13 +496,13 @@ class MqttClient extends EventEmitter {
|
|||
|
||||
const mqttType = device.type === 'switch' ? MQTT_TYPES.SWITCH : MQTT_TYPES.LIGHT;
|
||||
this.client.publish(getTopicName(device.uniqueId, mqttType, TOPIC_TYPES.STATE), payload, {
|
||||
retain: true,
|
||||
retain: false,
|
||||
qos: 1,
|
||||
});
|
||||
// this.client.publish(
|
||||
// getTopicName(device.uniqueId, mqttType, TOPIC_TYPES.AVAILABILITY),
|
||||
// AVAILABLILITY.ONLINE,
|
||||
// { retain: true, qos: 1 },
|
||||
// AVAILABILITY.ONLINE,
|
||||
// { retain: false, qos: 1 },
|
||||
// );
|
||||
}
|
||||
|
||||
|
|
@ -456,7 +512,10 @@ class MqttClient extends EventEmitter {
|
|||
*/
|
||||
buttonPressed(deviceId, deviceInput) {
|
||||
logger.verbose(`Button ${deviceInput} pressed for deviceId ${deviceId}`);
|
||||
this.client.publish(getButtonEventTopic(deviceId), `${deviceInput}`, { qos: 1 });
|
||||
this.client.publish(getButtonEventTopic(deviceId), `${deviceInput}`, {
|
||||
retain: false,
|
||||
qos: 1,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -464,7 +523,10 @@ class MqttClient extends EventEmitter {
|
|||
*/
|
||||
sceneTriggered(sceneId) {
|
||||
logger.verbose(`Scene triggered: ${sceneId}`);
|
||||
this.client.publish(getSceneEventTopic(sceneId), '', { qos: 1 });
|
||||
this.client.publish(getSceneEventTopic(sceneId), '', {
|
||||
qos: 1,
|
||||
retain: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue