UNPKG

lavva.exalushome

Version:

Library implementing communication and abstraction layers for ExalusHome system

339 lines 19.3 kB
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()); }); }; import { Api } from "../Api"; import { DataFrame, Method, Status } from "../DataFrame"; import { TypedEvent } from "../TypedEvent"; import { ControllerConfigurationService } from "./Controller/ControllerConfigurationService"; import { ConnectionResult, ConnectionState } from "./IExalusConnectionService"; import { LoggerService } from "./Logging/LoggerService"; import { SessionService } from "./Session/SessionService"; import { WebApiCacheService } from "./WebApi/WebApiCacheService"; export class LocalNetworkExalusConnectionService { constructor() { this._errorOccuredEvent = new TypedEvent(); this._connectionStateChangedEvent = new TypedEvent(); this._dataReceivedEvent = new TypedEvent(); this._onMessageReceived = new TypedEvent(); this._log = Api.Get(LoggerService.ServiceName); this._controllerConfiguration = null; this._cache = null; this._session = null; this._logPackets = true; this.socket = null; this._address = null; this._port = "81"; this._isOpen = false; this._pin = undefined; this._serial = undefined; this._auth = null; this._initialized = false; this._pingInterval = 5000; this._pingIntervalId = null; this._lastReceivedPacketTime = Date.now(); } SendAndHandleStreamAsync(dataFrame, streamHandler, logTransmission) { throw new Error("Method not implemented."); } Initialize() { if (this._initialized) return; this._initialized = true; this._controllerConfiguration = Api.Get(ControllerConfigurationService.ServiceName); this._cache = Api.Get(WebApiCacheService.ServiceName); this._session = Api.Get(SessionService.ServiceName); } checkIfAuthInfoIsCorrectAsync(authInfo) { return __awaiter(this, void 0, void 0, function* () { const url = `http://${window.location.hostname}/controller_info`; let res = yield fetch(url, { method: 'GET' }); if (res.ok) return (yield res.text()) === `${authInfo.SerialNumber}:${authInfo.PIN}`; return false; }); } PingControllerAsync() { return __awaiter(this, void 0, void 0, function* () { var _a; if (((_a = this.socket) === null || _a === void 0 ? void 0 : _a.readyState) !== WebSocket.OPEN) return false; if (this._lastReceivedPacketTime !== null && Date.now() - this._lastReceivedPacketTime < this._pingInterval) return false; let frame = new DataFrame(); frame.Resource = "/system/ping"; frame.Method = Method.Get; let result = yield this.SendAndWaitForResponseAsync(frame, 2000, false, false) .then(() => true) .catch(() => false); return result; }); } SubscribeTo(resourceId, handler) { let sub = (frame) => { if (frame.Resource === resourceId) handler(frame); }; this.OnDataReceivedEvent().Subscribe(sub); return () => this.OnDataReceivedEvent().Unsubscribe(sub); } GetControllerSerialNumber() { return this._serial; } GetControllerPin() { return this._pin; } SetServersBrokerAddress(address) { throw new Error("Method not implemented."); } SetDefaultPacketsBrokerAddress(address) { throw new Error("Method not implemented."); } GetServerAddressAsync() { if (Api.IsRunningFromLocalNetwork()) this._address = `${window.location.hostname}:${this._port}`; return Promise.resolve(this._address); } ConnectAsync(address) { return new Promise((resolve, reject) => { var _a; this.Initialize(); (_a = this.socket) === null || _a === void 0 ? void 0 : _a.close(); this.socket = new WebSocket(`ws://${address}/api`); this._log.Debug(LocalNetworkExalusConnectionService.ServiceName, `Connecting to the WebSockets server ${address}`); this.socket.onerror = (ev) => { this._log.Debug(LocalNetworkExalusConnectionService.ServiceName, `Error occured in the WebSockets server ${address} info: \n${JSON.stringify(ev)}`); this._isOpen = false; this._errorOccuredEvent.Invoke(["WebSockets", ev.toString()]); return reject(ConnectionResult.FailedToConnect); }; this.socket.onopen = (ev) => { this._log.Debug(LocalNetworkExalusConnectionService.ServiceName, `Connected to the WebSockets server ${address} info: \n${JSON.stringify(ev)}`); this._isOpen = true; this._connectionStateChangedEvent.Invoke(ConnectionState.Connected); this._pingIntervalId = setInterval(() => __awaiter(this, void 0, void 0, function* () { yield this.PingControllerAsync(); }), this._pingInterval); return resolve(ConnectionResult.Connected); }; this.socket.onclose = (ev) => { this._log.Debug(LocalNetworkExalusConnectionService.ServiceName, `Disconnected from the WebSockets server ${address} info: \n${JSON.stringify(ev)}`); this._isOpen = false; this._connectionStateChangedEvent.Invoke(ConnectionState.Disconnected); }; this.socket.onmessage = (ev) => { this._log.Debug(LocalNetworkExalusConnectionService.ServiceName, `Message received from the WebSockets server ${address}: ${ev.data}`); this._onMessageReceived.Invoke(ev.data); try { let frame = JSON.parse(ev.data); this._log.Debug(LocalNetworkExalusConnectionService.ServiceName, `Received data frame: ${frame.Status}`); this._dataReceivedEvent.Invoke(frame); } catch (err) { this._log.Error(LocalNetworkExalusConnectionService.ServiceName, `Failed to parse message: ${ev.data}`); } }; this._connectionStateChangedEvent.Invoke(ConnectionState.Connecting); }); } AuthorizeAsync(authorizationInfo) { return __awaiter(this, void 0, void 0, function* () { Api.WorksInContextOf = authorizationInfo.SerialNumber; let res = yield this.checkIfAuthInfoIsCorrectAsync(authorizationInfo); if (res) { this._log.Debug(LocalNetworkExalusConnectionService.ServiceName, `Authorization successful for controller ${authorizationInfo.SerialNumber}`); this._pin = authorizationInfo.PIN; this._serial = authorizationInfo.SerialNumber; this._auth = authorizationInfo; this._connectionStateChangedEvent.Invoke(ConnectionState.ConnectedAndAuthorized); return true; } else return false; }); } ConnectAndAuthorizeAsync(authorizationInfo) { return __awaiter(this, void 0, void 0, function* () { Api.WorksInContextOf = authorizationInfo.SerialNumber; this._log.Debug(LocalNetworkExalusConnectionService.ServiceName, `Connecting and authorizing to the controller ${authorizationInfo.SerialNumber}`); let autRes = yield this.AuthorizeAsync(authorizationInfo); if (!autRes) return ConnectionResult.AuthorizationFailed; let addr = yield this.GetServerAddressAsync(); if (addr == null) return ConnectionResult.FailedToConnectToServer; return yield this.ConnectAsync(addr); }); } DisconnectAsync() { var _a; this._log.Debug(LocalNetworkExalusConnectionService.ServiceName, `Disconnecting from the WebSockets server ${this._address}`); this._connectionStateChangedEvent.Invoke(ConnectionState.Disconnecting); if (this._pingIntervalId !== null) clearInterval(this._pingIntervalId); (_a = this.socket) === null || _a === void 0 ? void 0 : _a.close(); return Promise.resolve(); } IsConnected() { return this._isOpen; } EnablePacketsLogging() { this._log.Debug(LocalNetworkExalusConnectionService.ServiceName, `Packets logging enabled`); this._logPackets = true; } DisablePacketsLogging() { this._log.Debug(LocalNetworkExalusConnectionService.ServiceName, `Packets logging disabled`); this._logPackets = false; } SendData(data, logTransmission) { var _a; let fr = JSON.stringify(data); if (logTransmission) this._log.Debug(LocalNetworkExalusConnectionService.ServiceName, `Sending data frame: ${fr}`); if (this._isOpen) (_a = this.socket) === null || _a === void 0 ? void 0 : _a.send(fr); else this._log.Error(LocalNetworkExalusConnectionService.ServiceName, `Failed to send data frame: ${fr}`); return this._isOpen; } SendAsync(dataFrame) { if (this._isOpen) return Promise.resolve(this.SendData(dataFrame, this._logPackets)); else return Promise.resolve(false); } SendAndWaitForResponseAsync(dataFrame, timeout, useCache, logTransmission = true) { return this.SendAndWaitForResponseWithRepeatAsync(dataFrame, timeout, useCache, logTransmission); } SendAndHandleResponseAsync(dataFrame, timeout, dataHandler, logTransmission) { return __awaiter(this, void 0, void 0, function* () { var _a; let timeoutId = 0; const startTime = Date.now(); if (!this.IsConnected()) throw new Error("Connection is not established"); if (dataFrame.Resource !== "/users/user/login") yield ((_a = this._session) === null || _a === void 0 ? void 0 : _a.WaitForSessionCreationAsync()); return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { const enableTimeout = () => window.setTimeout(() => { this._dataReceivedEvent.Unsubscribe(onReceivedFrame); let errorMessage = `Response timeout, resource: ${dataFrame.Resource} method: ${dataFrame.Method} transaction id: ${dataFrame.TransactionId}`; this._log.Error(LocalNetworkExalusConnectionService.ServiceName, errorMessage); reject(new Error(errorMessage)); }, timeout); let onReceivedFrame = (receivedFrame) => __awaiter(this, void 0, void 0, function* () { var _a, _b, _c; if ((receivedFrame === null || receivedFrame === void 0 ? void 0 : receivedFrame.TransactionId) == dataFrame.TransactionId) { window.clearTimeout(timeoutId); const difference = Date.now() - startTime; if (logTransmission || window["packets"] === true) this._log.Debug(LocalNetworkExalusConnectionService.ServiceName, `Received response for: ${receivedFrame === null || receivedFrame === void 0 ? void 0 : receivedFrame.Resource} ${receivedFrame === null || receivedFrame === void 0 ? void 0 : receivedFrame.Method} id: ${receivedFrame === null || receivedFrame === void 0 ? void 0 : receivedFrame.TransactionId} in ${difference}ms`); if ((receivedFrame === null || receivedFrame === void 0 ? void 0 : receivedFrame.Status) === Status.UserIsNotLoggedIn) { this._dataReceivedEvent.Unsubscribe(onReceivedFrame); ((_a = this._session) === null || _a === void 0 ? void 0 : _a.OnUserLoggedOutEvent()).Invoke((_b = this._session) === null || _b === void 0 ? void 0 : _b.User); yield ((_c = this._session) === null || _c === void 0 ? void 0 : _c.RestoreSessionAsync()); resolve(yield this.SendAndHandleResponseAsync(dataFrame, timeout, dataHandler, logTransmission)); } switch (receivedFrame === null || receivedFrame === void 0 ? void 0 : receivedFrame.Status) { case Status.MultiDataResponseStart: case Status.MultiDataResponse: dataHandler(receivedFrame); timeoutId = enableTimeout(); break; case Status.MultiDataResponseStop: case Status.FatalError: case Status.Error: dataHandler(receivedFrame); this._dataReceivedEvent.Unsubscribe(onReceivedFrame); resolve(); break; default: let errorMessage = `Failed to process MultiDataResponse - recived incorrect response status: ${receivedFrame === null || receivedFrame === void 0 ? void 0 : receivedFrame.Status} method: ${dataFrame.Method} transaction id: ${dataFrame.TransactionId} make sure that the requested endpoint uses MultiDataResponse, otherwise use SendAndWaitForResponseAsync<T> method.`; reject(new Error(errorMessage)); break; } } }); this._dataReceivedEvent.Subscribe(onReceivedFrame); timeoutId = enableTimeout(); if (!this.SendData(dataFrame, logTransmission)) { let errorMessage = `Failed to send request, resource: ${dataFrame.Resource} method: ${dataFrame.Method} transaction id: ${dataFrame.TransactionId}`; reject(new Error(errorMessage)); } })); }); } SendAndWaitForResponseWithRepeatAsync(dataFrame_1, timeout_1, useCache_1) { return __awaiter(this, arguments, void 0, function* (dataFrame, timeout, useCache, repeat = true, logTransmission = true) { var _a, _b, _c; if (dataFrame.Method === Method.Get && useCache) { if (!(yield ((_a = this._controllerConfiguration) === null || _a === void 0 ? void 0 : _a.DidCofigurationChangeAsync()))) { let res = (_b = this._cache) === null || _b === void 0 ? void 0 : _b.GetCache(dataFrame); if (res !== null) { return Promise.resolve(res); } } } let timeoutId = 0; const startTime = Date.now(); if (!this.IsConnected()) throw new Error("Connection is not established"); if (dataFrame.Resource !== "/users/user/login") yield ((_c = this._session) === null || _c === void 0 ? void 0 : _c.WaitForSessionCreationAsync()); return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { let onReceivedFrame = (receivedFrame) => __awaiter(this, void 0, void 0, function* () { var _a, _b, _c, _d; if ((receivedFrame === null || receivedFrame === void 0 ? void 0 : receivedFrame.TransactionId) == dataFrame.TransactionId) { window.clearTimeout(timeoutId); this._dataReceivedEvent.Unsubscribe(onReceivedFrame); const difference = Date.now() - startTime; if (logTransmission) this._log.Debug(LocalNetworkExalusConnectionService.ServiceName, `Received response for: ${receivedFrame === null || receivedFrame === void 0 ? void 0 : receivedFrame.Resource} ${receivedFrame === null || receivedFrame === void 0 ? void 0 : receivedFrame.Method} id: ${receivedFrame === null || receivedFrame === void 0 ? void 0 : receivedFrame.TransactionId} in ${difference}ms`); if (dataFrame.Method === Method.Get && useCache) (_a = this._cache) === null || _a === void 0 ? void 0 : _a.Cache(receivedFrame); if (!useCache && (receivedFrame === null || receivedFrame === void 0 ? void 0 : receivedFrame.Status) === Status.UserIsNotLoggedIn && repeat) { ((_b = this._session) === null || _b === void 0 ? void 0 : _b.OnUserLoggedOutEvent()).Invoke((_c = this._session) === null || _c === void 0 ? void 0 : _c.User); yield ((_d = this._session) === null || _d === void 0 ? void 0 : _d.RestoreSessionAsync()); resolve(yield this.SendAndWaitForResponseWithRepeatAsync(dataFrame, timeout, useCache, false, logTransmission)); } resolve(receivedFrame); } }); this._dataReceivedEvent.Subscribe(onReceivedFrame); timeoutId = window.setTimeout(() => { this._dataReceivedEvent.Unsubscribe(onReceivedFrame); let errorMessage = `Response timeout, resource: ${dataFrame.Resource} method: ${dataFrame.Method} transaction id: ${dataFrame.TransactionId}`; this._log.Error(LocalNetworkExalusConnectionService.ServiceName, errorMessage); reject(new Error(errorMessage)); }, timeout); if (!this.SendData(dataFrame, logTransmission)) { let errorMessage = `Failed to send request, resource: ${dataFrame.Resource} method: ${dataFrame.Method} transaction id: ${dataFrame.TransactionId}`; reject(new Error(errorMessage)); } })); }); } OnDataReceivedEvent() { return this._dataReceivedEvent; } OnErrorOccuredEvent() { return this._errorOccuredEvent; } OnConnectionStateChangedEvent() { return this._connectionStateChangedEvent; } GetAuthorizationInfo() { return this._auth; } GetServiceName() { return LocalNetworkExalusConnectionService.ServiceName; } } LocalNetworkExalusConnectionService.ServiceName = "ExalusConnectionService"; //# sourceMappingURL=LocalNetworkExalusConnectionService.js.map