UNPKG

lavva.exalushome

Version:

Library implementing communication and abstraction layers for ExalusHome system

471 lines 26.5 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 { downloadStringAsCsv, ParseObjToMap } from "../../Helpers"; import { DevicesService } from "../Devices/DevicesService"; import { DeviceResponseType } from "../Devices/IDevice"; import { ExalusConnectionService } from "../ExalusConnectionService"; import { ResponseResult } from "../FieldChangeResult"; import { UpdatesProvider } from "../Updates/UpdatesProvider"; import { StateHistoryErrorCode } from "./IStatesHistoryService"; import { AvailableState, DataRangeType, StateInterval } from "./StatesHistory"; export class StatesHistoryService { constructor() { this.DELIM = ";"; this._connection = null; this._connection = Api.Get(ExalusConnectionService.ServiceName); } numToCsv(val) { const n = typeof val === "string" ? Number(val.replace(",", ".")) : val; if (!Number.isFinite(n)) return ""; return n.toLocaleString("en-US", { useGrouping: false }); } quoteIfNeeded(field) { return field.includes(this.DELIM) || field.includes('"') ? `"${field.replace(/"/g, '""')}"` : field; } ExportPressureToCsv(device, channel, data) { downloadStringAsCsv(this.ExportAveragingStateValuesToCsv(device, channel, data), `PressureHistory_${device.Name}_${channel}.csv`); } ExportBrightnessToCsv(device, channel, data) { var _a; if (!((_a = data === null || data === void 0 ? void 0 : data.Data) === null || _a === void 0 ? void 0 : _a.length)) return; const lines = []; // ---------- header ---------- const first = data.Data[0]; const header = ["Time", "AggregatedBy"]; header.push(`LuxValue`, `LuxToLinearScale`, `LightCondition`); lines.push(header.join(this.DELIM)); // ---------- data rows ---------- data.Data.forEach(el => { var _a, _b, _c; const row = [ this.quoteIfNeeded(el.Time), this.quoteIfNeeded(el.AggregatedBy) ]; if (el.Values !== null) { row.push(this.numToCsv((_a = el.Values) === null || _a === void 0 ? void 0 : _a.LuxValue)); row.push(this.numToCsv((_b = el.Values) === null || _b === void 0 ? void 0 : _b.LuxToLinearScale)); row.push(this.numToCsv((_c = el.Values) === null || _c === void 0 ? void 0 : _c.LightCondition)); } lines.push(row.join(this.DELIM)); }); downloadStringAsCsv(lines.join("\n"), `BrightnessHistory_${device.Name}_${channel}.csv`); } ExportHumidityToCsv(device, channel, data) { downloadStringAsCsv(this.ExportAveragingStateValuesToCsv(device, channel, data), `HumidityHistory_${device.Name}_${channel}.csv`); } ExportWindSpeedToCsv(device, channel, data) { downloadStringAsCsv(this.ExportAveragingStateValuesToCsv(device, channel, data), `WindSpeedHistory_${device.Name}_${channel}.csv`); } ExportTemperatureToCsv(device, channel, data) { downloadStringAsCsv(this.ExportAveragingStateValuesToCsv(device, channel, data), `TemperatureHistory_${device.Name}_${channel}.csv`); } ExportAveragingStateValuesToCsv(device, channel, data) { var _a; if (!((_a = data === null || data === void 0 ? void 0 : data.Data) === null || _a === void 0 ? void 0 : _a.length)) return ""; const lines = []; // ---------- header ---------- const first = data.Data[0]; const header = ["Time", "AggregatedBy"]; header.push(`LastValue`, `Average`, `Mode`, `Min`, `Max`); lines.push(header.join(this.DELIM)); // ---------- data rows ---------- data.Data.forEach(el => { var _a, _b, _c, _d, _e; const row = [ this.quoteIfNeeded(el.Time), this.quoteIfNeeded(el.AggregatedBy) ]; if (el.Values !== null) { row.push(this.numToCsv((_a = el.Values) === null || _a === void 0 ? void 0 : _a.LastValue)); row.push(this.numToCsv((_b = el.Values) === null || _b === void 0 ? void 0 : _b.Average)); row.push(this.numToCsv((_c = el.Values) === null || _c === void 0 ? void 0 : _c.Mode)); row.push(this.numToCsv((_d = el.Values) === null || _d === void 0 ? void 0 : _d.Min)); row.push(this.numToCsv((_e = el.Values) === null || _e === void 0 ? void 0 : _e.Max)); } lines.push(row.join(this.DELIM)); }); return lines.join("\n"); } ExportEnergyToCsv(device, channel, response) { var _a, _b; let data = response.Data; if (!(data === null || data === void 0 ? void 0 : data.length)) return; const lines = []; // ---------- header ---------- const first = data[0]; const header = ["Time", "AggregatedBy"]; (_a = first.Values) === null || _a === void 0 ? void 0 : _a.MeasurementAveragingParameters.forEach((_v, key) => { header.push(`${key}:LastValue`, `${key}:Average`, `${key}:Mode`, `${key}:Min`, `${key}:Max`); }); (_b = first.Values) === null || _b === void 0 ? void 0 : _b.MeasurementNonAveragingParmeters.forEach((_v, key) => header.push(`${key}`)); lines.push(header.join(this.DELIM)); // ---------- data rows ---------- data.forEach(el => { var _a, _b; const row = [ this.quoteIfNeeded(el.Time), this.quoteIfNeeded(el.AggregatedBy) ]; (_a = el.Values) === null || _a === void 0 ? void 0 : _a.MeasurementAveragingParameters.forEach(vals => { row.push(this.numToCsv(vals.LastValue), this.numToCsv(vals.Average), this.numToCsv(vals.Mode), this.numToCsv(vals.Min), this.numToCsv(vals.Max)); }); (_b = el.Values) === null || _b === void 0 ? void 0 : _b.MeasurementNonAveragingParmeters.forEach(v => row.push(this.numToCsv(v))); lines.push(row.join(this.DELIM)); }); downloadStringAsCsv(lines.join("\n"), `EnergyHistory_${device.Name}_${channel}.csv`); } GetStatesByTimeRangeAsync(dev_1, channel_1, responseType_1, timeRange_1, time_1, limit_1, offset_1) { return __awaiter(this, arguments, void 0, function* (dev, channel, responseType, timeRange, time, limit, offset, orderByDesc = false) { var _a; try { if (!(yield this.DoesSupportPreciseTimeFramesAsync())) return new ResponseResult(StateHistoryErrorCode.FunctionalityNotSupported, `State history data in time range is not supported with this version of controller software, update software to get this functionality.`); let device; if (typeof dev === 'string') { const result = yield Api.Get(DevicesService.ServiceName).GetDevice(dev); if (result != null) device = result; else return new ResponseResult(StateHistoryErrorCode.CannotFindDevice, `Device with specified GUID ${dev} does not exist.`); } else { device = dev; } if (!device.Channels.any(ch => ch.Number === channel)) return new ResponseResult(StateHistoryErrorCode.InvalidChannelNumber, `Given channel not found in device with guid: ${device.Guid}.`); if (!device.Channels.any(ch => ch.Number === channel && ch.AvailableResponseTypes.any(r => r.Type === responseType))) return new ResponseResult(StateHistoryErrorCode.ResponseTypeNotSupported, `Device or channel not supporting requested DeviceResponseType.`); // Prepare request object const request = new StateInTimeRangeRequest(); request.DeviceGuid = device.Guid; request.DeviceChannel = channel; request.StateInterfaceType = responseType; request.ReverseOrder = orderByDesc; request.DataRange = timeRange; request.Time = time; request.Limit = limit; request.Offset = offset; // Optionally, you could use the 'time' parameter for more precise requests if supported const result = yield ((_a = this._connection) === null || _a === void 0 ? void 0 : _a.SendAndWaitForResponseAsync(new GetStatesIntimeRangeRequest(request), 30000, false)); if (result == null) return new ResponseResult(StateHistoryErrorCode.OtherError, `Cannot get state history data - response is null.`); switch (result.Status) { case Status.WrongData: switch (result.Data) { case "IncorrectLimitValue": return new ResponseResult(StateHistoryErrorCode.IncorrectLimitValue, `Cannot get state history data - limit value is incorrect!`); case "IncorrectOffsetValue": return new ResponseResult(StateHistoryErrorCode.IncorrectOffsetValue, `Cannot get state history data - offset value is incorrect!`); case "IncorrectArguments": return new ResponseResult(StateHistoryErrorCode.OtherError, `Cannot get state history data - some parameters are incorrect!`); case "UnsupportedRange": return new ResponseResult(StateHistoryErrorCode.OtherError, `Cannot get state history data - time range is incorrect!`); default: return new ResponseResult(StateHistoryErrorCode.OtherError, `Cannot get state history data - unknown error!`); } case Status.FatalError: return new ResponseResult(StateHistoryErrorCode.FatalError, `Cannot get state history data - an exception occurred in the controller while reading data!`); case Status.ResourceDoesNotExists: return new ResponseResult(StateHistoryErrorCode.ResponseTypeNotSupported, `Device or channel not supporting requested DeviceResponseType or device/channel does not exist. ${result.Data}`); case Status.OK: if (result.Data == null) return new ResponseResult(StateHistoryErrorCode.NoData, `Controller responded with status OK, but response does not contain data!`); result.Data.AggregateDataList = result.Data.AggregateDataList.map(d => { d.AggregateData = ParseObjToMap(d.AggregateData); return d; }); switch (responseType) { case DeviceResponseType.MeasuredEnergy: const parseData = result.Data.Data.map(d => { const result = d; result.Values.MeasurementAveragingParameters = ParseObjToMap(d.Values.MeasurementAveragingParameters); result.Values.MeasurementNonAveragingParmeters = ParseObjToMap(d.Values.MeasurementNonAveragingParmeters); return result; }); result.Data.Data = parseData; return result.Data; default: return result.Data; } default: return new ResponseResult(StateHistoryErrorCode.OtherError, `Cannot get state history data - controller responded with response code ${result.Status}`); } } catch (error) { return new ResponseResult(StateHistoryErrorCode.FatalError, `Cannot get state history data - exception occurs! ${error}`); } }); } GetServiceName() { return StatesHistoryService.ServiceName; } DoesSupportPreciseTimeFramesAsync() { return __awaiter(this, void 0, void 0, function* () { let ver = (yield Api.Get(UpdatesProvider.ServiceName).GetSoftwareRuntimeInfoAsync()).SoftwareVersion.split("."); return Number(ver[0]) > 6 || (Number(ver[0]) === 6 && Number(ver[1]) >= 101); }); } GetStatesByIntervalAsync(dev_1, channel_1, responseType_1, stateFrom_1, limit_1, offset_1) { return __awaiter(this, arguments, void 0, function* (dev, channel, responseType, stateFrom, limit, offset, orderByDesc = false) { var _a; try { if (!(yield this.IsFunctionalitySupportedAsync())) return new ResponseResult(StateHistoryErrorCode.FunctionalityNotSupported, `State history data is not supported with this version of controller software, update software to get this functionality.`); let device; if (typeof dev == 'string') { const result = yield Api.Get(DevicesService.ServiceName).GetDevice(dev); if (result != null) device = result; else return new ResponseResult(StateHistoryErrorCode.CannotFindDevice, `Device with specified GUID ${dev} does not exists.`); } else { device = dev; } if (!device.Channels.any(ch => ch.Number == channel)) return new ResponseResult(StateHistoryErrorCode.InvalidChannelNumber, `Given channel not found in device with guid: ${device.Guid}.`); if (!device.Channels.any(ch => ch.Number == channel && ch.AvailableResponseTypes.any(r => r.Type == responseType))) return new ResponseResult(StateHistoryErrorCode.ResponseTypeNotSupported, `Device or channel not supporting requested DeviceResponseType.`); const request = new StateDataRequest(); request.DeviceGuid = device.Guid; request.DeviceChannel = channel; request.StateInterfaceType = responseType; request.ReverseOrder = orderByDesc; request.Limit = limit; request.Offset = offset; let ver = (yield Api.Get(UpdatesProvider.ServiceName).GetSoftwareRuntimeInfoAsync()).SoftwareVersion.split("."); let isSupported = Number(ver[0]) > 6 || (Number(ver[0]) === 6 && Number(ver[1]) >= 101); if (isSupported) { switch (stateFrom) { case StateInterval.ThisHour: request.Range = StateInterval.Hour; break; case StateInterval.ThisDay: request.Range = StateInterval.Day; break; case StateInterval.ThisWeek: request.Range = StateInterval.Week; break; case StateInterval.ThisMonth: request.Range = StateInterval.Month; break; case StateInterval.ThisYear: request.Range = StateInterval.Year; break; } } else request.Range = stateFrom; const result = yield ((_a = this._connection) === null || _a === void 0 ? void 0 : _a.SendAndWaitForResponseAsync(new GetLastStatesRequest(request), 30000, false)); if (result == null) return new ResponseResult(StateHistoryErrorCode.OtherError, `Cannot get state history data - response is null.`); switch (result.Status) { case Status.WrongData: switch (result.Data) { case "IncorrectLimitValue": return new ResponseResult(StateHistoryErrorCode.IncorrectLimitValue, `Cannot get state history data - limit value is incorrect!`); case "IncorrectOffsetValue": return new ResponseResult(StateHistoryErrorCode.IncorrectOffsetValue, `Cannot get state history data - offset value is incorrect!`); case "IncorrectArguments": return new ResponseResult(StateHistoryErrorCode.OtherError, `Cannot get state history data - some parameters are incorrect!`); default: return new ResponseResult(StateHistoryErrorCode.OtherError, `Cannot get state history data - unknown error!`); } case Status.FatalError: return new ResponseResult(StateHistoryErrorCode.FatalError, `Cannot get state history data - an exception occurred in the controller while reading data!`); case Status.ResourceDoesNotExists: return new ResponseResult(StateHistoryErrorCode.ResponseTypeNotSupported, `Device or channel not supporting requested DeviceResponseType.`); case Status.OK: if (result.Data == null) return new ResponseResult(StateHistoryErrorCode.NoData, `Controller responede with status OK, but response does not contain data!`); result.Data.AggregateDataList = result.Data.AggregateDataList.map(d => { d.AggregateData = ParseObjToMap(d.AggregateData); return d; }); switch (responseType) { case DeviceResponseType.MeasuredEnergy: const parseData = result.Data.Data.map(d => { const result = d; result.Values.MeasurementAveragingParameters = ParseObjToMap(d.Values.MeasurementAveragingParameters); result.Values.MeasurementNonAveragingParmeters = ParseObjToMap(d.Values.MeasurementNonAveragingParmeters); return result; }); result.Data.Data = parseData; return result.Data; default: return result.Data; } default: return new ResponseResult(StateHistoryErrorCode.OtherError, `Cannot get state history data - controller responded with response code ${result.Status}`); } } catch (error) { return new ResponseResult(StateHistoryErrorCode.FatalError, `Cannot get state history data - exeption occurs! ${error}`); } }); } GetAvailableStatesAsync() { 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 GetAvailableStatesRequest(), 12000, false)); if (result == null) return new ResponseResult(StateHistoryErrorCode.OtherError, `Cannot get available states - response is null.`); if (result.Status != Status.OK) return new ResponseResult(StateHistoryErrorCode.OtherError, `Cannot get available states - controller responded with response code ${result.Status}.`); if (result.Data == null) return new ResponseResult(StateHistoryErrorCode.NoData, `Cannot get available states - controller responede with status OK, but response does not contain data!`); return result.Data.map(r => { const x = new AvailableState(); x.StateInterfaceType = r.StateInterfaceType; x.StateObjectType = r.StateObjectType; return x; }); } catch (error) { return new ResponseResult(StateHistoryErrorCode.FatalError, `Cannot get available states - exeption occurs! ${error}`); } }); } IsFunctionalitySupportedAsync() { return __awaiter(this, void 0, void 0, function* () { let ver = (yield Api.Get(UpdatesProvider.ServiceName).GetSoftwareRuntimeInfoAsync()).SoftwareVersion.split("."); return Number(ver[0]) > 3 || (Number(ver[0]) === 3 && Number(ver[1]) >= 42); }); } GetAvailableStatesPerChannelAsync(dev, channel) { return __awaiter(this, void 0, void 0, function* () { var _a; let device; if (typeof dev == 'string') { const result = yield Api.Get(DevicesService.ServiceName).GetDevice(dev); if (result != null) device = result; else return new ResponseResult(StateHistoryErrorCode.CannotFindDevice, `Device with specified GUID ${dev} does not exists.`); } else { device = dev; } const result = yield ((_a = this._connection) === null || _a === void 0 ? void 0 : _a.SendAndWaitForResponseAsync(new GetAvailableStatesPerChannelRequest(new ChannelParamRequest(device.Guid, channel)), 8000, false)); if (result == null) return new ResponseResult(StateHistoryErrorCode.OtherError, `Cannot get available states - response is null.`); switch (result.Status) { case Status.ResourceDoesNotExists: { switch (result.Data) { case "DeviceNotFound": return new ResponseResult(StateHistoryErrorCode.CannotFindDevice, `Cannot get available states - device does not exists!`); case "ChannelNotFound": return new ResponseResult(StateHistoryErrorCode.InvalidChannelNumber, `Cannot get available states - wrong channel!`); default: return new ResponseResult(StateHistoryErrorCode.OtherError, `Cannot get available states - unknown error!`); } } case Status.OK: { if (result.Data == null) return new ResponseResult(StateHistoryErrorCode.NoData, `Cannot get available states - controller responede with status OK, but response does not contain data!`); const remappedStates = result.Data.AvailableStates.map(r => { const x = new AvailableState(); x.StateInterfaceType = r.StateInterfaceType; x.StateObjectType = r.StateObjectType; return x; }); result.Data.AvailableStates = remappedStates; return result.Data; } default: return new ResponseResult(StateHistoryErrorCode.OtherError, `Cannot get available states - controller responded with response code ${result.Status}`); } }); } } StatesHistoryService.ServiceName = "StatesHistoryService"; class GetLastStatesRequest extends DataFrame { constructor(data) { super(); this.Resource = "/statehistory/states/get/last"; this.Method = Method.Get; this.Data = data; } } class StateDataRequest { constructor() { this.DeviceGuid = ""; this.DeviceChannel = 0; this.StateInterfaceType = ""; this.Range = StateInterval.Day; this.ReverseOrder = false; this.Limit = 0; this.Offset = 0; } } class GetStatesIntimeRangeRequest extends DataFrame { constructor(data) { super(); this.Resource = "/statehistory/states/get/timerange"; this.Method = Method.Get; this.Data = data; } } class StateInTimeRangeRequest { constructor() { this.DeviceGuid = ""; this.DeviceChannel = 0; this.StateInterfaceType = ""; this.DataRange = DataRangeType.Day; this.Time = new Date(); this.ReverseOrder = false; this.Limit = 0; this.Offset = 0; } } class GetAvailableStatesRequest extends DataFrame { constructor() { super(); this.Resource = "/statehistory/states/available"; this.Method = Method.Get; } } class GetAvailableStatesPerChannelRequest extends DataFrame { constructor(request) { super(); this.Resource = "/statehistory/states/available/per/channel"; this.Method = Method.Get; this.Data = request; } } class ChannelParamRequest { constructor(guid, channel) { this.DeviceGuid = guid; this.Channel = channel; } } class AvailableStateResponse { constructor() { this.StateInterfaceType = ""; this.StateObjectType = ""; } } class AvailableStatePerChannelResponse { constructor() { this.DeviceGuid = ""; this.Channel = 0; this.AvailableStates = []; } } //# sourceMappingURL=StatesHistoryService.js.map