node-red-contrib-home-assistant-websocket
Version:
Node-RED integration with Home Assistant through websocket and REST API
186 lines (185 loc) • 7.52 kB
JavaScript
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SUPERVISOR_URL = exports.HaEvent = exports.homeAssistantConnections = void 0;
exports.hasCredentials = hasCredentials;
exports.createHomeAssistantClient = createHomeAssistantClient;
exports.getHomeAssistant = getHomeAssistant;
exports.closeHomeAssistant = closeHomeAssistant;
const events_1 = require("events");
const url_1 = require("url");
const ClientEvents_1 = __importDefault(require("../common/events/ClientEvents"));
const globals_1 = require("../globals");
const Comms_1 = __importDefault(require("../nodes/config-server/Comms"));
const ConnectionLog_1 = __importDefault(require("../nodes/config-server/ConnectionLog"));
const EditorContext_1 = __importDefault(require("../nodes/config-server/EditorContext"));
const HomeAssistant_1 = __importDefault(require("./HomeAssistant"));
const Http_1 = __importDefault(require("./Http"));
const Websocket_1 = __importStar(require("./Websocket"));
exports.homeAssistantConnections = new Map();
var HaEvent;
(function (HaEvent) {
HaEvent["AreaRegistryUpdated"] = "areas_updated";
HaEvent["AutomationTriggered"] = "automation_triggered";
HaEvent["DeviceRegistryUpdated"] = "devices_updated";
HaEvent["FloorRegistryUpdated"] = "floors_updated";
HaEvent["Integration"] = "nodered";
HaEvent["LabelRegistryUpdated"] = "labels_updated";
HaEvent["EntityRegistryUpdated"] = "entity_registry_updated";
HaEvent["ServicesUpdated"] = "services_updated";
HaEvent["StateChanged"] = "state_changed";
HaEvent["TagScanned"] = "tag_scanned";
HaEvent["ValueChange"] = "value_change";
})(HaEvent || (exports.HaEvent = HaEvent = {}));
exports.SUPERVISOR_URL = 'http://supervisor/core';
function hasCredentials(credentials) {
return !!credentials.host && !!credentials.access_token;
}
function createHomeAssistantClient(node) {
let homeAssistant = exports.homeAssistantConnections.get(node.id);
if (homeAssistant)
return homeAssistant;
if (!hasCredentials(node.credentials) && !node.config.addon) {
throw new Error('No credentials provided');
}
const eventBus = new events_1.EventEmitter();
eventBus.setMaxListeners(0);
const creds = createCredentials(node.credentials, node.config);
const httpConfig = createHttpConfig(creds, node.config);
const websocketConfig = createWebsocketConfig(creds, node.config);
const httpAPI = new Http_1.default(httpConfig);
const websocketAPI = new Websocket_1.default(websocketConfig, eventBus);
homeAssistant = new HomeAssistant_1.default({
websocketAPI,
httpAPI,
eventBus,
});
const clientEvents = new ClientEvents_1.default({
node,
emitter: homeAssistant.eventBus,
});
clientEvents.addListener(Websocket_1.ClientEvent.Connected, homeAssistant.subscribeEvents.bind(homeAssistant), {
once: true,
});
// eslint-disable-next-line no-new
new Comms_1.default(node.id, homeAssistant, clientEvents);
// eslint-disable-next-line no-new
new ConnectionLog_1.default(node, clientEvents);
if (node.config.enableGlobalContextStore) {
// eslint-disable-next-line no-new
new EditorContext_1.default(node, clientEvents);
}
homeAssistant.websocket.connect().catch((e) => {
node.error(e);
});
exports.homeAssistantConnections.set(node.id, homeAssistant);
return homeAssistant;
}
function createCredentials(credentials, config) {
let host;
if (!credentials) {
throw new Error('No credentials');
}
// eslint-disable-next-line camelcase
let accessToken = credentials.access_token;
// Check if using HA Add-on and import proxy token
const addonBaseUrls = ['http://hassio/homeassistant', exports.SUPERVISOR_URL];
if (config.addon || addonBaseUrls.includes(credentials.host)) {
if (!process.env.SUPERVISOR_TOKEN) {
throw new Error('Supervisor token not found.');
}
host = exports.SUPERVISOR_URL;
// eslint-disable-next-line camelcase
accessToken = process.env.SUPERVISOR_TOKEN;
}
else {
host = getBaseUrl(credentials.host);
}
return {
host,
// eslint-disable-next-line camelcase
access_token: accessToken,
};
}
function createHttpConfig(credentials, config) {
return {
access_token: credentials.access_token,
host: credentials.host,
rejectUnauthorizedCerts: config.rejectUnauthorizedCerts,
};
}
function createWebsocketConfig(credentials, config = {
rejectUnauthorizedCerts: true,
connectionDelay: true,
}) {
var _a, _b, _c;
const connectionDelay = credentials.host !== exports.SUPERVISOR_URL
? false
: ((_a = config.connectionDelay) !== null && _a !== void 0 ? _a : false);
const heartbeatInterval = (_b = Number(config.heartbeatInterval)) !== null && _b !== void 0 ? _b : 0;
const heartbeat = config.heartbeat && Number.isInteger(heartbeatInterval)
? heartbeatInterval
: 0;
return {
access_token: credentials.access_token,
host: credentials.host,
rejectUnauthorizedCerts: (_c = config.rejectUnauthorizedCerts) !== null && _c !== void 0 ? _c : true,
connectionDelay,
heartbeatInterval: heartbeat,
};
}
function getBaseUrl(url) {
validateBaseUrl(url);
return url.trim();
}
function validateBaseUrl(baseUrl) {
if (!baseUrl) {
throw new Error(globals_1.RED._('config-server.errors.empty_base_url'));
}
let parsedUrl;
try {
parsedUrl = new url_1.URL(baseUrl);
}
catch (e) {
throw new Error(globals_1.RED._('config-server.errors.invalid_base_url'));
}
if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {
throw new Error(globals_1.RED._('config-server.errors.invalid_protocol'));
}
}
function getHomeAssistant(serverNode) {
var _a;
return ((_a = exports.homeAssistantConnections.get(serverNode.id)) !== null && _a !== void 0 ? _a : createHomeAssistantClient(serverNode));
}
function closeHomeAssistant(nodeId) {
const homeAssistant = exports.homeAssistantConnections.get(nodeId);
if (homeAssistant) {
homeAssistant.close();
exports.homeAssistantConnections.delete(nodeId);
}
}
;