node-red-contrib-home-assistant-websocket
Version:
Node-RED integration with Home Assistant through websocket and REST API
101 lines (100 loc) • 4.41 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = createSocket;
exports.atLeastHaVersion = atLeastHaVersion;
const debug_1 = __importDefault(require("debug"));
const home_assistant_js_websocket_1 = require("home-assistant-js-websocket");
const ws_1 = __importDefault(require("ws"));
const Websocket_1 = require("./Websocket");
const debug = (0, debug_1.default)('home-assistant:socket');
/*
* Pretty much a copy from https://github.com/home-assistant/home-assistant-js-websocket
*/
function createSocket({ auth, connectionDelay, eventBus, rejectUnauthorizedCerts, url, }) {
debug('[Auth Phase] Initializing', url);
function connect(promResolve, promReject) {
debug('[Auth Phase] New connection', url);
eventBus.emit(Websocket_1.ClientEvent.Connecting);
const socket = new ws_1.default(url, {
rejectUnauthorized: rejectUnauthorizedCerts,
});
// If invalid auth, we will not try to reconnect.
let invalidAuth = false;
const onOpen = async () => {
try {
socket.send(JSON.stringify(auth));
}
catch (err) {
invalidAuth = err === home_assistant_js_websocket_1.ERR_INVALID_AUTH;
socket.close();
}
};
const onMessage = (data, isBinary) => {
if (isBinary)
return;
const message = JSON.parse(data.toString());
debug('[Auth Phase] Received', message);
switch (message.type) {
case home_assistant_js_websocket_1.MSG_TYPE_AUTH_INVALID:
invalidAuth = true;
socket.close();
break;
case home_assistant_js_websocket_1.MSG_TYPE_AUTH_OK:
socket.off('open', onOpen);
socket.off('message', onMessage);
socket.off('close', onClose);
socket.off('error', onClose);
socket.haVersion = message.ha_version;
// enable coalesce messages if supported
if (atLeastHaVersion(socket.haVersion, 2022, 9)) {
socket.send(JSON.stringify({
type: 'supported_features',
id: 1,
features: { coalesce_messages: 1 },
}));
}
promResolve(socket);
break;
default:
if (message.type !== home_assistant_js_websocket_1.MSG_TYPE_AUTH_REQUIRED) {
debug('[Auth Phase] Unhandled message', message);
}
}
};
const onClose = () => {
// If we are in error handler make sure close handler doesn't also fire.
socket.off('close', onClose);
if (invalidAuth) {
promReject(home_assistant_js_websocket_1.ERR_INVALID_AUTH);
return;
}
// Try again in a second
setTimeout(() => connect(promResolve, promReject), 5000);
};
socket.on('open', onOpen);
socket.on('message', onMessage);
socket.on('close', onClose);
socket.on('error', onClose);
}
return new Promise((resolve, reject) => {
// if hass.io, do a 5 second delay so it doesn't spam the hass.io proxy
// https://github.com/zachowj/node-red-contrib-home-assistant-websocket/issues/76
setTimeout(() => connect(resolve, reject), connectionDelay !== false ? 5000 : 0);
});
}
// https://github.com/home-assistant/home-assistant-js-websocket/blob/95f166b29a09fc1841bd0c1f312391ceb2812520/lib/util.ts#L45
function atLeastHaVersion(version, major, minor, patch) {
const [haMajor, haMinor, haPatch] = version.split('.', 3);
return (Number(haMajor) > major ||
(Number(haMajor) === major &&
(patch === undefined
? Number(haMinor) >= minor
: Number(haMinor) > minor)) ||
(patch !== undefined &&
Number(haMajor) === major &&
Number(haMinor) === minor &&
Number(haPatch) >= patch));
}
;