UNPKG

@di-zed/yandex-smart-home

Version:

The Yandex Smart Home skills for the different device types.

251 lines (250 loc) 10.5 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const mqttService_1 = __importDefault(require("./mqttService")); const configProvider_1 = __importDefault(require("../providers/configProvider")); const redisProvider_1 = __importDefault(require("../providers/redisProvider")); const mqttRepository_1 = __importDefault(require("../repositories/mqttRepository")); const topicExpiredEvent_1 = __importDefault(require("../events/topicExpiredEvent")); /** * Topic Service. */ class TopicService { constructor() { /** * The Topic Timeout IDs. * * @protected */ this.topicTimeoutIds = {}; } /** * Prepare Message for the MQTT Topic. * * @param aliceValue * @param topicData * @returns Promise<string> */ convertAliceValueToMqttMessage(aliceValue, topicData) { return __awaiter(this, void 0, void 0, function* () { let mqttMessage = ''; if (topicData) { for (const [message, value] of Object.entries(topicData.messageValueMapping)) { if (value === aliceValue && message !== '__default') { mqttMessage = String(message).toLowerCase(); } } } if (mqttMessage === '') { switch (typeof aliceValue) { case 'boolean': mqttMessage = aliceValue ? 'on' : 'off'; break; case 'object': mqttMessage = JSON.stringify(aliceValue); break; default: mqttMessage = String(aliceValue).toLowerCase(); } } const functionConvertAliceValueToMqttMessage = configProvider_1.default.getConfigOption('functionConvertAliceValueToMqttMessage'); if (typeof functionConvertAliceValueToMqttMessage === 'function') { return (yield functionConvertAliceValueToMqttMessage(aliceValue, mqttMessage, topicData)); } return mqttMessage; }); } /** * Prepare Value for the Alice Device Capability State. * * @param mqttMessage * @param topicData * @returns Promise<any> */ convertMqttMessageToAliceValue(mqttMessage, topicData) { return __awaiter(this, void 0, void 0, function* () { let aliceValue = undefined; const handledMessage = mqttMessage.toLowerCase(); if (topicData) { if (topicData.messageValueMapping[handledMessage] !== undefined) { aliceValue = topicData.messageValueMapping[handledMessage]; } else if (topicData.messageValueMapping['__default'] !== undefined) { aliceValue = topicData.messageValueMapping['__default']; } } if (aliceValue === undefined) { if (handledMessage === 'on' || handledMessage === 'off') { aliceValue = handledMessage === 'on'; } else if (!isNaN(Number(handledMessage))) { aliceValue = Number(handledMessage); } else { try { const value = JSON.parse(mqttMessage); if (value && typeof value === 'object') { aliceValue = value; } } catch (err) { aliceValue = handledMessage.toString(); } } } const functionConvertMqttMessageToAliceValue = configProvider_1.default.getConfigOption('functionConvertMqttMessageToAliceValue'); if (typeof functionConvertMqttMessageToAliceValue === 'function') { return (yield functionConvertMqttMessageToAliceValue(mqttMessage, aliceValue, topicData)); } return aliceValue; }); } /** * Set Topic Message to the Cache. * * @param topic * @param message * @returns Promise<boolean> */ setTopicMessage(topic, message) { return __awaiter(this, void 0, void 0, function* () { const redisClient = yield redisProvider_1.default.getClientAsync(); const result = yield redisClient.hSet('topics', topic, message); const topicLifetimes = [ { topicType: 'availableTopic', lifetimeSec: parseInt(process.env.TOPIC_AVAILABLE_CACHE_LIFETIME_SEC, 10), }, { topicType: 'commandTopic', lifetimeSec: parseInt(process.env.TOPIC_COMMAND_CACHE_LIFETIME_SEC, 10), }, { topicType: 'stateTopic', lifetimeSec: parseInt(process.env.TOPIC_STATE_CACHE_LIFETIME_SEC, 10), }, ]; for (const topicLifetime of topicLifetimes) { if (!isNaN(topicLifetime.lifetimeSec)) { if (yield mqttService_1.default.isTopicType(topic, topicLifetime.topicType)) { yield redisClient.sendCommand(['HEXPIRE', 'topics', String(topicLifetime.lifetimeSec), 'FIELDS', '1', topic]); // Event when the topic has disappeared from the Redis storage: const self = this; clearTimeout(this.topicTimeoutIds[topic]); this.topicTimeoutIds[topic] = setTimeout(function () { delete self.topicTimeoutIds[topic]; topicExpiredEvent_1.default.execute(topic, message).catch((err) => { console.log('ERROR! Topic Expired Event.', err instanceof Error ? err.message : err); }); }, topicLifetime.lifetimeSec * 1000 * 1.5); break; } } } return result > 0; }); } /** * Get Topic Message from the Cache. * * @param topic * @returns Promise<string | undefined> */ getTopicMessage(topic) { return __awaiter(this, void 0, void 0, function* () { const redisClient = yield redisProvider_1.default.getClientAsync(); const value = yield redisClient.hGet('topics', topic); return value !== undefined && value !== null ? value : undefined; }); } /** * Get Value from the State Topic by Keys. * * @param topic * @param keys * @returns Promise<string | undefined> */ getStateTopicValueByKey(topic, keys) { return __awaiter(this, void 0, void 0, function* () { if (!topic.trim() || keys.length === 0) { return undefined; } if (!(yield mqttService_1.default.isTopicType(topic, 'stateTopic'))) { return undefined; } let result = undefined; const message = yield this.getTopicMessage(topic); if (message !== undefined) { try { const data = JSON.parse(message); keys.forEach((key) => { if (result === undefined) { result = data[key] !== undefined ? String(data[key]) : undefined; } }); } catch (err) { return undefined; } } return result; }); } /** * Get State Topic changes. * * @param oldMessage * @param newMessage * @param deviceType * @returns Promise<ChangedCommandTopicInterface[]> */ getStateTopicChanges(oldMessage_1, newMessage_1) { return __awaiter(this, arguments, void 0, function* (oldMessage, newMessage, deviceType = '') { const isStateTopicChecked = process.env.TOPIC_STATE_CHECK_IF_COMMAND_IS_UNDEFINED.trim() === '1'; if (!isStateTopicChecked) { return []; } const result = []; const configTopics = yield mqttRepository_1.default.getConfigTopics(); try { const oldStateTopic = oldMessage ? JSON.parse(oldMessage) : {}; const newStateTopic = JSON.parse(newMessage); for (const configTopic of configTopics) { if (deviceType) { if (configTopic.deviceType !== deviceType) { continue; } } for (const commandTopic of configTopic.commandTopics) { for (const key of commandTopic.topicStateKeys || []) { if (oldStateTopic[key] !== newStateTopic[key]) { const item = Object.assign(commandTopic, { deviceType: configTopic.deviceType, mqttValueOld: oldStateTopic[key], mqttValueNew: newStateTopic[key], }); result.push(item); } } } } } catch (err) { return []; } return result; }); } } exports.default = new TopicService();