node-red-contrib-home-assistant-websocket
Version:
Node-RED integration with Home Assistant through websocket and REST API
261 lines (260 loc) • 14.6 kB
JavaScript
"use strict";
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var _JSONataService_homeAssistant, _JSONataService_node;
Object.defineProperty(exports, "__esModule", { value: true });
const lodash_1 = require("lodash");
const globals_1 = require("../../globals");
const utils_1 = require("../../helpers/utils");
const JSONataError_1 = __importDefault(require("../errors/JSONataError"));
function evaluateJSONataExpression(expr, message) {
return new Promise((resolve, reject) => {
globals_1.RED.util.evaluateJSONataExpression(expr, message, (err, res) => {
if (err) {
reject(err);
}
else {
resolve(res);
}
});
});
}
function isJSONataError(error) {
if (typeof error !== 'object' || !error)
return false;
return ('code' in error &&
'message' in error &&
'stack' in error &&
'token' in error);
}
class JSONataService {
constructor({ homeAssistant, node, }) {
_JSONataService_homeAssistant.set(this, void 0);
_JSONataService_node.set(this, void 0);
__classPrivateFieldSet(this, _JSONataService_homeAssistant, homeAssistant, "f");
__classPrivateFieldSet(this, _JSONataService_node, node, "f");
}
async evaluate(expression, objs = {}) {
let expr;
try {
expr = globals_1.RED.util.prepareJSONataExpression(expression, __classPrivateFieldGet(this, _JSONataService_node, "f"));
}
catch (err) {
if (isJSONataError(err)) {
throw new JSONataError_1.default(err);
}
throw err;
}
const { entity, message, prevEntity } = objs;
expr.assign('areas', this.areas.bind(this));
expr.assign('areaDevices', this.areaDevices.bind(this));
expr.assign('areaEntities', this.areaEntities.bind(this));
expr.assign('deviceEntities', this.deviceEntities.bind(this));
expr.assign('device', this.device.bind(this));
expr.assign('entity', () => entity);
expr.assign('entities', (val) => {
var _a, _b;
return (_b = (_a = __classPrivateFieldGet(this, _JSONataService_homeAssistant, "f")) === null || _a === void 0 ? void 0 : _a.websocket) === null || _b === void 0 ? void 0 : _b.getState(val);
});
expr.assign('outputData', (obj) => {
if (!obj) {
const filtered = Object.keys(objs).reduce((acc, key) => {
// ignore message as it already accessable
if (key !== 'message' && objs[key] !== undefined) {
acc[key] = objs[key];
}
return acc;
}, {});
return filtered;
}
return objs[obj];
});
expr.assign('prevEntity', () => prevEntity);
expr.assign('randomNumber', lodash_1.random);
expr.assign('sampleSize', lodash_1.sampleSize);
try {
// await here to catch JSONataError
return await evaluateJSONataExpression(expr, message);
}
catch (err) {
if (isJSONataError(err)) {
throw new JSONataError_1.default(err);
}
throw err;
}
}
/**
* This method retrieves all devices associated with a specific area ID.
* It first retrieves the area using the provided area ID.
* If the area exists, it retrieves all devices.
* It then iterates over each device, checking if it is associated with the area.
* If the device is associated with the area, it is added to the list of devices in the area.
* If the area does not exist, or if there are no devices associated with the area, it returns an empty array.
*
* @param areaId - The ID of the area for which to retrieve the devices.
* @returns An array of devices associated with the area, or an empty array if the area does not exist or has no associated devices.
*/
areaDevices(areaId) {
var _a, _b, _c, _d;
const areas = (_b = (_a = __classPrivateFieldGet(this, _JSONataService_homeAssistant, "f")) === null || _a === void 0 ? void 0 : _a.websocket) === null || _b === void 0 ? void 0 : _b.getArea(areaId);
const devicesInArea = [];
if (areas) {
const devices = (_d = (_c = __classPrivateFieldGet(this, _JSONataService_homeAssistant, "f")) === null || _c === void 0 ? void 0 : _c.websocket) === null || _d === void 0 ? void 0 : _d.getDevices();
if (devices) {
devices.forEach((device) => {
if (device.area_id === areaId) {
devicesInArea.push(device);
}
});
}
}
return devicesInArea;
}
/**
* This method retrieves all entities associated with a specific area ID.
* It first retrieves the area using the provided area ID.
* If the area exists, it retrieves all entities and devices.
* It then iterates over each entity, checking if it is associated with the area either directly or through a device.
* If the entity is associated with the area, it is added to the list of entities in the area.
* If the area does not exist, or if there are no entities associated with the area, it returns an empty array.
*
* @param areaId - The ID of the area for which to retrieve the entities.
* @returns An array of entities associated with the area, or an empty array if the area does not exist or has no associated entities.
*/
areaEntities(areaId) {
var _a, _b, _c, _d, _e, _f;
const areas = (_b = (_a = __classPrivateFieldGet(this, _JSONataService_homeAssistant, "f")) === null || _a === void 0 ? void 0 : _a.websocket) === null || _b === void 0 ? void 0 : _b.getArea(areaId);
const entitiesInArea = [];
if (areas) {
const devices = (_d = (_c = __classPrivateFieldGet(this, _JSONataService_homeAssistant, "f")) === null || _c === void 0 ? void 0 : _c.websocket) === null || _d === void 0 ? void 0 : _d.getDevices();
(_f = (_e = __classPrivateFieldGet(this, _JSONataService_homeAssistant, "f")) === null || _e === void 0 ? void 0 : _e.websocket) === null || _f === void 0 ? void 0 : _f.getEntities().forEach((entry) => {
var _a, _b;
const entity = (_b = (_a = __classPrivateFieldGet(this, _JSONataService_homeAssistant, "f")) === null || _a === void 0 ? void 0 : _a.websocket) === null || _b === void 0 ? void 0 : _b.getState(entry.entity_id);
if (entity) {
if (entry.area_id === areaId) {
entitiesInArea.push(entity);
}
else {
const device = devices === null || devices === void 0 ? void 0 : devices.find((device) => device.id === entry.device_id);
if ((device === null || device === void 0 ? void 0 : device.area_id) === areaId) {
entitiesInArea.push(entity);
}
}
}
});
}
return entitiesInArea;
}
areas(lookup) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
if (typeof lookup !== 'string') {
return (_b = (_a = __classPrivateFieldGet(this, _JSONataService_homeAssistant, "f")) === null || _a === void 0 ? void 0 : _a.websocket) === null || _b === void 0 ? void 0 : _b.getAreas();
}
const areas = (_d = (_c = __classPrivateFieldGet(this, _JSONataService_homeAssistant, "f")) === null || _c === void 0 ? void 0 : _c.websocket) === null || _d === void 0 ? void 0 : _d.getAreas();
const area = areas === null || areas === void 0 ? void 0 : areas.find((area) => area.area_id === lookup);
if (area) {
return area;
}
if ((0, utils_1.validEntityId)(lookup)) {
const entity = (_f = (_e = __classPrivateFieldGet(this, _JSONataService_homeAssistant, "f")) === null || _e === void 0 ? void 0 : _e.websocket) === null || _f === void 0 ? void 0 : _f.getEntity(lookup);
if (entity) {
// check if entity has area id and return area name
if (entity.area_id) {
const area = areas === null || areas === void 0 ? void 0 : areas.find((area) => area.area_id === entity.area_id);
if (area) {
return area;
}
}
// check if entity has device id and return area name
if (entity.device_id) {
const device = (_h = (_g = __classPrivateFieldGet(this, _JSONataService_homeAssistant, "f")) === null || _g === void 0 ? void 0 : _g.websocket) === null || _h === void 0 ? void 0 : _h.getDevice(entity.device_id);
if (device) {
if (device.area_id) {
const area = areas === null || areas === void 0 ? void 0 : areas.find((area) => area.area_id === device.area_id);
if (area) {
return area;
}
}
}
}
}
}
// fallback to see if lookup is a device id
const devices = (_k = (_j = __classPrivateFieldGet(this, _JSONataService_homeAssistant, "f")) === null || _j === void 0 ? void 0 : _j.websocket) === null || _k === void 0 ? void 0 : _k.getDevices();
const device = devices === null || devices === void 0 ? void 0 : devices.find((device) => device.id === lookup);
if (device) {
if (device.area_id) {
const area = areas === null || areas === void 0 ? void 0 : areas.find((area) => area.area_id === device.area_id);
if (area) {
return area;
}
}
}
}
/**
* This method retrieves a device based on a provided lookup value.
* The lookup value can be an entity ID or a device name.
* If the lookup value is an entity ID, it retrieves the entity and checks if it has a device ID.
* If the entity has a device ID, it retrieves the device that matches the device ID.
* If the lookup value is a device name, it retrieves the device that matches the name.
* If the lookup value does not match any entity or device, it returns undefined.
*
* @param lookup - The lookup value, which can be an entity ID or a device name.
* @returns The device associated with the entity or the device that matches the name, or undefined if the lookup value does not match any entity or device.
*/
device(lookup) {
var _a, _b, _c, _d;
const entities = (_b = (_a = __classPrivateFieldGet(this, _JSONataService_homeAssistant, "f")) === null || _a === void 0 ? void 0 : _a.websocket) === null || _b === void 0 ? void 0 : _b.getEntity(lookup);
const devices = (_d = (_c = __classPrivateFieldGet(this, _JSONataService_homeAssistant, "f")) === null || _c === void 0 ? void 0 : _c.websocket) === null || _d === void 0 ? void 0 : _d.getDevices();
if (entities) {
if (devices) {
return devices.find((device) => device.id === entities.device_id);
}
}
const device = devices === null || devices === void 0 ? void 0 : devices.find((device) => device.name_by_user === lookup || device.name === lookup);
if (device) {
return device;
}
}
/**
* This method retrieves all entities associated with a specific device.
* It first retrieves the device using the provided device ID.
* If the device exists, it retrieves all entities.
* It then filters the entities to include only those associated with the device.
* If the device does not exist, or if there are no entities associated with the device, it returns an empty array.
*
* @param deviceId - The ID of the device for which to retrieve the entities.
* @returns An array of entities associated with the device, or an empty array if the device does not exist or has no associated entities.
*/
deviceEntities(deviceId) {
var _a, _b, _c, _d;
const devices = (_b = (_a = __classPrivateFieldGet(this, _JSONataService_homeAssistant, "f")) === null || _a === void 0 ? void 0 : _a.websocket) === null || _b === void 0 ? void 0 : _b.getDevice(deviceId);
const entities = [];
if (devices) {
(_d = (_c = __classPrivateFieldGet(this, _JSONataService_homeAssistant, "f")) === null || _c === void 0 ? void 0 : _c.websocket) === null || _d === void 0 ? void 0 : _d.getEntities().forEach((entry) => {
var _a, _b;
if (entry.device_id === deviceId) {
const entity = (_b = (_a = __classPrivateFieldGet(this, _JSONataService_homeAssistant, "f")) === null || _a === void 0 ? void 0 : _a.websocket) === null || _b === void 0 ? void 0 : _b.getState(entry.entity_id);
if (entity) {
entities.push(entity);
}
}
});
}
return entities;
}
}
_JSONataService_homeAssistant = new WeakMap(), _JSONataService_node = new WeakMap();
exports.default = JSONataService;