lavva.exalushome
Version:
Library implementing communication and abstraction layers for ExalusHome system
339 lines • 19.3 kB
JavaScript
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