lavva.exalushome.extalife
Version:
Library implementing communication and abstraction layers for ExtaLife API in ExalusHome system
359 lines • 20.2 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 { ResponseResult, FieldChangeResultType } from "lavva.exalushome/build/js/Services/FieldChangeResult";
import { GetParamsErrorCode } from "../../../ExtaLife";
import { SbrAlarmLightDelay, SbrAlarmLightDuration, SbrAutoCloseTime } from "./ISbrConfigService";
import { Api } from "lavva.exalushome";
import { ExalusConnectionService } from "lavva.exalushome/build/js/Services/ExalusConnectionService";
import { LoggerService } from "lavva.exalushome/build/js/Services/Logging/LoggerService";
import { ControllerConfigurationService } from "lavva.exalushome/build/js/Services/Controller/ControllerConfigurationService";
import { DataFrame, Method, Status } from "lavva.exalushome/build/js/DataFrame";
import { UpdatesProvider } from "lavva.exalushome/build/js/Services/Updates/UpdatesProvider";
import { DevicesService } from "lavva.exalushome/build/js/Services/Devices/DevicesService";
export class SbrConfigService {
constructor() {
this._connection = Api.Get(ExalusConnectionService.ServiceName);
this._logger = Api.Get(LoggerService.ServiceName);
this._controllerConfigurationService = Api.Get(ControllerConfigurationService.ServiceName);
}
GetServiceName() {
return SbrConfigService.ServiceName;
}
SetAutoCloseTimeAsync(deviceChannel, timeInMiliseconds, resetOnCrossToOn) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
let enteredConfigurationMode = false;
try {
if (timeInMiliseconds < 60000 || timeInMiliseconds > 3600000)
return new ResponseResult(FieldChangeResultType.WrongData, `Cannot set auto close time - invalid value. Time must be in range from 60_000 to 3_600_000 ms.`);
yield this._controllerConfigurationService.EnterConfigurationModeAsync();
enteredConfigurationMode = true;
const result = yield ((_a = this._connection) === null || _a === void 0 ? void 0 : _a.SendAndWaitForResponseAsync(new SetAutoCloseTimeRequest(deviceChannel, timeInMiliseconds, resetOnCrossToOn), 8000, false));
if (result == null || result.Status == null)
return new ResponseResult(FieldChangeResultType.UnknownError, `Cannot set auto close time - unknown error.`);
if (result.Status !== Status.OK)
return new ResponseResult(FieldChangeResultType.UnknownError, `Cannot set auto close time - controller responded with error code: ${result.Status}.`);
return new ResponseResult(FieldChangeResultType.Changed, `Auto close time set successfully.`);
}
catch (error) {
return new ResponseResult(FieldChangeResultType.UnknownError, `Cannot get auto close time - unknown error.`);
}
finally {
if (enteredConfigurationMode) {
this._logger.Error(SbrConfigService.ServiceName, `EXIT CONFIGURATION MODE WILL BE FIRED! SetAutoCloseTimeAsync()`);
yield this._controllerConfigurationService.ExitConfigurationModeAsync();
}
}
});
}
GetAutoCloseTimeAsync(deviceChannel) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
try {
const result = yield ((_a = this._connection) === null || _a === void 0 ? void 0 : _a.SendAndWaitForResponseAsync(new GetAutoCloseTimeRequest(deviceChannel), 8000, false));
switch (result === null || result === void 0 ? void 0 : result.Status) {
case Status.OK:
return result.Data;
default:
return new ResponseResult(GetParamsErrorCode.UnknownError, `Cannot get auto close time`);
}
}
catch (error) {
return new ResponseResult(GetParamsErrorCode.UnknownError, `Cannot get auto close time error: ${error}`);
}
});
}
SetAlarmLightDelayAsync(deviceChannel, lightDelayInMiliseconds) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
let enteredConfigurationMode = false;
try {
if (lightDelayInMiliseconds < 0 || lightDelayInMiliseconds > 120000)
return new ResponseResult(FieldChangeResultType.WrongData, `Cannot set light delay time - invalid value. Time must be in range from 60_000 to 3_600_000 ms.`);
yield this._controllerConfigurationService.EnterConfigurationModeAsync();
enteredConfigurationMode = true;
const result = yield ((_a = this._connection) === null || _a === void 0 ? void 0 : _a.SendAndWaitForResponseAsync(new SetAlarmLightDelayRequest(deviceChannel, lightDelayInMiliseconds), 8000, false));
if (result == null || result.Status == null)
return new ResponseResult(FieldChangeResultType.UnknownError, `Cannot set light delay time - unknown error.`);
if (result.Status !== Status.OK)
return new ResponseResult(FieldChangeResultType.UnknownError, `Cannot set light delay time - controller responded with error code: ${result.Status}.`);
return new ResponseResult(FieldChangeResultType.Changed, `Light delay set successfully.`);
}
catch (error) {
return new ResponseResult(FieldChangeResultType.UnknownError, `Cannot get light delay time - unknown error.`);
}
finally {
if (enteredConfigurationMode) {
this._logger.Error(SbrConfigService.ServiceName, `EXIT CONFIGURATION MODE WILL BE FIRED! SetAlarmLightDelayAsync()`);
yield this._controllerConfigurationService.ExitConfigurationModeAsync();
}
}
});
}
GetAlarmLightDelayAsync(deviceChannel) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
try {
const result = yield ((_a = this._connection) === null || _a === void 0 ? void 0 : _a.SendAndWaitForResponseAsync(new GetAlarmLightDelayRequest(deviceChannel), 8000, false));
switch (result === null || result === void 0 ? void 0 : result.Status) {
case Status.OK:
return result.Data;
default:
return new ResponseResult(GetParamsErrorCode.UnknownError, `Cannot get light delay time`);
}
}
catch (error) {
return new ResponseResult(GetParamsErrorCode.UnknownError, `Cannot get auto light delay error: ${error}`);
}
});
}
SetAlarmLightDurationAsync(deviceChannel, lightDurationInMiliseconds) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
let enteredConfigurationMode = false;
try {
if (lightDurationInMiliseconds < 0 || lightDurationInMiliseconds > 3600000)
return new ResponseResult(FieldChangeResultType.WrongData, `Cannot set alarm light duration time - invalid value. Time must be in range from 60_000 to 3_600_000 ms.`);
yield this._controllerConfigurationService.EnterConfigurationModeAsync();
enteredConfigurationMode = true;
const result = yield ((_a = this._connection) === null || _a === void 0 ? void 0 : _a.SendAndWaitForResponseAsync(new SetAlarmLightDurationRequest(deviceChannel, lightDurationInMiliseconds), 8000, false));
if (result == null || result.Status == null)
return new ResponseResult(FieldChangeResultType.UnknownError, `Cannot set alarm light duration time - unknown error.`);
if (result.Status !== Status.OK)
return new ResponseResult(FieldChangeResultType.UnknownError, `Cannot set alarm light duration time - controller responded with error code: ${result.Status}.`);
return new ResponseResult(FieldChangeResultType.Changed, `Alarm light duration set successfully.`);
}
catch (error) {
return new ResponseResult(FieldChangeResultType.UnknownError, `Cannot get alarm light duration time - unknown error.`);
}
finally {
if (enteredConfigurationMode) {
this._logger.Error(SbrConfigService.ServiceName, `EXIT CONFIGURATION MODE WILL BE FIRED! SetAlarmLightDurationAsync()`);
yield this._controllerConfigurationService.ExitConfigurationModeAsync();
}
}
});
}
GetAlarmLightDurationAsync(deviceChannel) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
try {
const result = yield ((_a = this._connection) === null || _a === void 0 ? void 0 : _a.SendAndWaitForResponseAsync(new GetAlarmLightDurationRequest(deviceChannel), 8000, false));
switch (result === null || result === void 0 ? void 0 : result.Status) {
case Status.OK:
return result.Data;
default:
return new ResponseResult(GetParamsErrorCode.UnknownError, `Cannot get light delay time`);
}
}
catch (error) {
return new ResponseResult(GetParamsErrorCode.UnknownError, `Cannot get auto light delay error: ${error}`);
}
});
}
GetServiceInfoAsync(deviceChannel) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
try {
const result = yield ((_a = this._connection) === null || _a === void 0 ? void 0 : _a.SendAndWaitForResponseAsync(new GetServiceInfoRequest(deviceChannel), 8000, false));
switch (result === null || result === void 0 ? void 0 : result.Status) {
case Status.OK:
return result.Data;
default:
return new ResponseResult(GetParamsErrorCode.UnknownError, `Cannot get service info, response status: ${result === null || result === void 0 ? void 0 : result.Status}`);
}
}
catch (error) {
return new ResponseResult(GetParamsErrorCode.UnknownError, `Cannot get service info error: ${error}`);
}
});
}
IsPinSecureSupportedAsync(deviceChannel) {
return __awaiter(this, void 0, void 0, function* () {
const version = yield Api.Get(UpdatesProvider.ServiceName).GetSoftwareRuntimeInfoAsync();
const [major, minor] = version.SoftwareVersion.split(".").map(v => parseInt(v));
if ((major >= 7 && minor >= 42) && deviceChannel.Number == 1) {
return true;
}
return false;
});
}
SetPinSecureAsync(deviceChannel, pinSecureEnabled, pinCode) {
return __awaiter(this, void 0, void 0, function* () {
try {
const devicesApi = Api.Get(DevicesService.ServiceName);
const channel = devicesApi.GetDeviceChannelByChannelId(deviceChannel.ChannelId);
if (channel == null)
return new ResponseResult(FieldChangeResultType.WrongData, `Cannot set pin secure - channel with id ${deviceChannel.ChannelId} not found.`);
let customData = [];
customData.push({ dataId: SecureGateControllCustomData.IsEnabledBool.toString(), data: "true" });
if (pinSecureEnabled) {
if (pinCode == null || pinCode.length < 4 || !/^\d+$/.test(pinCode)) {
return new ResponseResult(FieldChangeResultType.WrongData, `Cannot set pin secure - invalid pin code, pin code must be at least 4 digits long and contain only digits.`);
}
customData.push({ dataId: SecureGateControllCustomData.IsEnabledBool.toString(), data: "true" });
customData.push({ dataId: SecureGateControllCustomData.PinHash.toString(), data: yield this.CreatePinHashAsync(pinCode) });
}
else {
customData.push({ dataId: SecureGateControllCustomData.IsEnabledBool.toString(), data: "false" });
customData.push({ dataId: SecureGateControllCustomData.PinHash.toString(), data: "" });
}
for (const entry of customData) {
yield channel.SetCustomDataAsync(entry.dataId, entry.data);
}
return new ResponseResult(FieldChangeResultType.Changed, `Pin secure ${pinSecureEnabled ? "enabled" : "disabled"}.`);
}
catch (error) {
return new ResponseResult(FieldChangeResultType.UnknownError, `Cannot set pin secure - unknown error.`);
}
});
}
GetPinSecureStatusAsync(deviceChannel) {
return __awaiter(this, void 0, void 0, function* () {
var _a, _b, _c;
try {
const devicesApi = Api.Get(DevicesService.ServiceName);
const channel = devicesApi.GetDeviceChannelByChannelId(deviceChannel.ChannelId);
if (channel == null)
return new ResponseResult(GetParamsErrorCode.UnknownError, `Cannot get pin secure status - channel with id ${deviceChannel.ChannelId} not found.`);
const enabled = ((_a = channel.CustomData) === null || _a === void 0 ? void 0 : _a[SecureGateControllCustomData.IsEnabledBool.toString()]) === "true";
// Additionally ensure that when enabled there is a hash stored
if (enabled) {
const hash = (_c = (_b = channel.CustomData) === null || _b === void 0 ? void 0 : _b[SecureGateControllCustomData.PinHash.toString()]) !== null && _c !== void 0 ? _c : "";
return hash.length > 0;
}
return false;
}
catch (error) {
return new ResponseResult(GetParamsErrorCode.UnknownError, `Cannot get pin secure status - unknown error.`);
}
});
}
// Generates a hash string in the format: Base64(salt):Base64(SHA256(salt || UTF8(pin))) compatible with backend VerifyHash
CreatePinHashAsync(pinCode) {
return __awaiter(this, void 0, void 0, function* () {
const salt = this.getRandomBytes(16);
const pinBytes = new TextEncoder().encode(pinCode);
const input = new Uint8Array(salt.length + pinBytes.length);
input.set(salt, 0);
input.set(pinBytes, salt.length);
const hashBytes = yield this.sha256(input);
const saltB64 = this.bytesToBase64(salt);
const hashB64 = this.bytesToBase64(hashBytes);
return `${saltB64}:${hashB64}`;
});
}
getRandomBytes(length) {
if (typeof globalThis !== "undefined" && globalThis.crypto && typeof globalThis.crypto.getRandomValues === "function") {
const arr = new Uint8Array(length);
globalThis.crypto.getRandomValues(arr);
return arr;
}
// Fallback (non-cryptographic) only if crypto is unavailable; should not happen in supported environments
const arr = new Uint8Array(length);
for (let i = 0; i < length; i++)
arr[i] = Math.floor(Math.random() * 256);
return arr;
}
sha256(data) {
return __awaiter(this, void 0, void 0, function* () {
if (typeof globalThis !== "undefined" && globalThis.crypto && globalThis.crypto.subtle) {
const digest = yield globalThis.crypto.subtle.digest("SHA-256", data);
return new Uint8Array(digest);
}
throw new Error("Crypto.subtle not available for SHA-256 hashing");
});
}
bytesToBase64(bytes) {
// Prefer Node Buffer if available (e.g., in Node / Electron)
const maybeBuffer = globalThis.Buffer;
if (typeof maybeBuffer !== "undefined" && typeof maybeBuffer.from === "function") {
return maybeBuffer.from(bytes).toString("base64");
}
// Browser-safe base64 encoding using btoa
let binary = "";
const chunkSize = 0x8000;
for (let i = 0; i < bytes.length; i += chunkSize) {
const sub = bytes.subarray(i, i + chunkSize);
binary += String.fromCharCode.apply(null, Array.from(sub));
}
return btoa(binary);
}
}
SbrConfigService.ServiceName = "SbrConfigService";
var SecureGateControllCustomData;
(function (SecureGateControllCustomData) {
SecureGateControllCustomData["IsEnabledBool"] = "IsEnabledBool";
SecureGateControllCustomData["PinHash"] = "PinHash";
})(SecureGateControllCustomData || (SecureGateControllCustomData = {}));
class GetAutoCloseTimeRequest extends DataFrame {
constructor(deviceChannel) {
super();
this.Resource = "/extalife/device/parameters/sbr/autoclosetime";
this.Method = Method.Get;
this.Data = deviceChannel.GetDevice().Guid;
}
}
class SetAutoCloseTimeRequest extends DataFrame {
constructor(DeviceChannel, timeInMiliseconds, resetOnCrossToOn) {
super();
this.Resource = "/extalife/device/parameters/sbr/autoclosetime";
this.Method = Method.Put;
this.Data = new SbrAutoCloseTime();
this.Data.DeviceGuid = DeviceChannel.GetDevice().Guid;
this.Data.TimeInMiliseconds = timeInMiliseconds;
this.Data.ResetOnCrossToOn = resetOnCrossToOn;
}
}
class GetAlarmLightDelayRequest extends DataFrame {
constructor(deviceChannel) {
super();
this.Resource = "/extalife/device/parameters/sbr/alarmlightdelay";
this.Method = Method.Get;
this.Data = deviceChannel.GetDevice().Guid;
}
}
class SetAlarmLightDelayRequest extends DataFrame {
constructor(DeviceChannel, lightDelayInMiliseconds) {
super();
this.Resource = "/extalife/device/parameters/sbr/alarmlightdelay";
this.Method = Method.Put;
this.Data = new SbrAlarmLightDelay();
this.Data.DeviceGuid = DeviceChannel.GetDevice().Guid;
this.Data.LightDelayInMiliseconds = lightDelayInMiliseconds;
}
}
class GetAlarmLightDurationRequest extends DataFrame {
constructor(deviceChannel) {
super();
this.Resource = "/extalife/device/parameters/sbr/lightduration";
this.Method = Method.Get;
this.Data = deviceChannel.GetDevice().Guid;
}
}
class SetAlarmLightDurationRequest extends DataFrame {
constructor(DeviceChannel, lightDurationInMiliseconds) {
super();
this.Resource = "/extalife/device/parameters/sbr/lightduration";
this.Method = Method.Put;
this.Data = new SbrAlarmLightDuration();
this.Data.DeviceGuid = DeviceChannel.GetDevice().Guid;
this.Data.LightDurationInMiliseconds = lightDurationInMiliseconds;
}
}
class GetServiceInfoRequest extends DataFrame {
constructor(deviceChannel) {
super();
this.Resource = "/extalife/device/parameters/sbr/serviceinfo";
this.Method = Method.Get;
this.Data = deviceChannel.GetDevice().Guid;
}
}
//# sourceMappingURL=SbrConfigService.js.map