homebridge-loxone-proxy
Version:
Homebridge Dynamic Platform Plugin which exposes a Loxone System to Homekit.
217 lines • 9.24 kB
JavaScript
"use strict";
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;
};
Object.defineProperty(exports, "__esModule", { value: true });
const uuid_1 = require("uuid");
const LxCommunicator = __importStar(require("lxcommunicator"));
const WebSocketConfig = LxCommunicator.WebSocketConfig;
class LoxoneHandler {
constructor(platform) {
this.socket = undefined;
this.loxdata = undefined;
this.log = platform.log;
this.host = platform.config.host;
this.port = platform.config.port;
this.tls = platform.config.TLS;
this.username = platform.config.username;
this.password = platform.config.password;
this.uuidCallbacks = {};
this.uuidCache = {};
this.startListener();
}
startListener() {
if (typeof this.socket === 'undefined') {
const uuid = (0, uuid_1.v4)();
const proto = this.tls ? WebSocketConfig.protocol.WSS : WebSocketConfig.protocol.WS;
const webSocketConfig = new WebSocketConfig(proto, uuid, 'homebridge', WebSocketConfig.permission.APP, false);
const handleAnyEvent = (uuid, message) => {
if (Object.prototype.hasOwnProperty.call(this.uuidCallbacks, uuid)) {
if (typeof message === 'string') {
if (message.includes('->')) {
const parts = message.split('->');
if (parts.length === 2) {
message = parts[1].trim();
}
}
message = { uuid: uuid, value: message };
}
this.uuidCallbacks[uuid].forEach(callback => callback(message));
}
this.uuidCache[uuid] = message;
};
webSocketConfig.delegate = {
socketOnDataProgress: (socket, progress) => {
this.log.debug('data progress ' + progress);
},
socketOnTokenConfirmed: (socket, response) => {
this.log.debug('token confirmed');
},
socketOnTokenReceived: (socket, result) => {
this.log.debug('token received');
},
socketOnConnectionClosed: (socket, code) => {
this.log.info('Socket closed ' + code);
if (code !== LxCommunicator.SupportCode.WEBSOCKET_MANUAL_CLOSE) {
this.reconnect();
}
},
socketOnEventReceived: (socket, events, type) => {
for (const evt of events) {
switch (type) {
case LxCommunicator.BinaryEvent.Type.EVENT:
handleAnyEvent(evt.uuid, evt.value);
handleAnyEvent(evt.uuid, evt);
break;
case LxCommunicator.BinaryEvent.Type.EVENTTEXT:
handleAnyEvent(evt.uuid, evt.text);
break;
case LxCommunicator.BinaryEvent.Type.WEATHER:
handleAnyEvent(evt.uuid, evt);
break;
default:
break;
}
}
},
};
this.socket = new LxCommunicator.WebSocket(webSocketConfig);
this.connect()
.catch(error => {
this.log.error('Couldn\'t open socket: ' + error);
this.reconnect();
});
}
}
async connect() {
this.log.info(`Trying to connect to Miniserver at ${this.host}:${this.port} (TLS=${this.tls})`);
let url;
if (this.tls) {
url = `https://${this.host}`;
if (this.port && this.port !== 443) {
url += `:${this.port}`;
}
}
else {
url = `http://${this.host}:${this.port}`;
}
try {
await this.socket.open(url, this.username, this.password);
const file = await this.socket.send('data/LoxAPP3.json');
this.loxdata = JSON.parse(file);
this.startBinaryStatusUpdates();
this.log.info('Connected to Miniserver');
return true;
}
catch (error) {
this.log.error('Connection failed: ' + error);
try {
this.socket.close();
}
catch (_a) { }
return false;
}
}
startBinaryStatusUpdates() {
this.log.debug('[LoxoneHandler] Enabling binary status updates...');
this.socket.send('jdev/sps/enablebinstatusupdate');
}
reconnect(attempt = 0) {
const delay = Math.min(10000 * (attempt + 1), 60000);
this.log.info(`Reconnecting in ${delay / 1000}s...`);
setTimeout(async () => {
const success = await this.connect();
if (!success && attempt < 10) {
this.reconnect(attempt + 1);
}
else if (!success) {
this.log.error('Max reconnect attempts reached');
}
}, delay);
}
registerListenerForUUID(uuid, callback) {
if (Object.prototype.hasOwnProperty.call(this.uuidCallbacks, uuid)) {
this.uuidCallbacks[uuid].push(callback);
}
else {
this.uuidCallbacks[uuid] = [callback];
}
if (uuid in this.uuidCache) {
this.uuidCallbacks[uuid].forEach(cb => cb(this.uuidCache[uuid]));
}
}
sendCommand(uuid, action) {
return this.socket.send(`jdev/sps/io/${uuid}/${action}`, 2)
.catch(err => this.log.error(`sendCommand failed: ${err}`));
}
getsecuredDetails(uuid) {
return this.socket.send(`jdev/sps/io/${uuid}/securedDetails`);
}
getActiveCommunicationToken() {
var _a, _b, _c;
try {
const tokenHandler = (_a = this.socket) === null || _a === void 0 ? void 0 : _a._tokenHandler;
if (!(tokenHandler === null || tokenHandler === void 0 ? void 0 : tokenHandler.getToken)) {
return undefined;
}
const appToken = (_b = tokenHandler.getToken(WebSocketConfig.permission.APP, this.username)) !== null && _b !== void 0 ? _b : tokenHandler.getToken(WebSocketConfig.permission.APP);
if (appToken === null || appToken === void 0 ? void 0 : appToken.token) {
return appToken.token;
}
const webToken = (_c = tokenHandler.getToken(WebSocketConfig.permission.WEB, this.username)) !== null && _c !== void 0 ? _c : tokenHandler.getToken(WebSocketConfig.permission.WEB);
if (webToken === null || webToken === void 0 ? void 0 : webToken.token) {
return webToken.token;
}
return undefined;
}
catch (_d) {
return undefined;
}
}
getLastCachedValue(uuid) {
return this.uuidCache[uuid];
}
pushCachedState(accessory, uuid) {
var _a, _b, _c;
const value = this.getLastCachedValue(uuid);
if (value === undefined) {
this.log.debug(`[pushCachedState] No cached value found for UUID: ${uuid}`);
return;
}
const itemState = (_a = accessory.ItemStates) === null || _a === void 0 ? void 0 : _a[uuid];
if (!itemState) {
this.log.warn(`[pushCachedState] UUID not registered in ItemStates for device ${(_b = accessory.device) === null || _b === void 0 ? void 0 : _b.name}`);
return;
}
const message = {
uuid,
state: itemState.state,
service: itemState.service,
value,
};
this.log.debug(`[pushCachedState] Pushing Cached state for ${(_c = accessory.device) === null || _c === void 0 ? void 0 : _c.name} [${itemState.state}] = ${value}`);
accessory.callBackHandler(message);
}
}
exports.default = LoxoneHandler;
//# sourceMappingURL=LoxoneHandler.js.map