@enyo-energy/energy-app-sdk 0.0.110 → 0.0.112

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,8 +6,10 @@ const forecast_types_js_1 = require("./forecast-types.cjs");
6
6
  const forecast_utils_js_1 = require("./forecast-utils.cjs");
7
7
  /**
8
8
  * Builds a 24-hour heatpump electrical consumption forecast for a single
9
- * heatpump appliance from historical `HeatpumpValuesUpdateV1` data-bus
10
- * messages, with live updates merged in as new readings arrive.
9
+ * heatpump appliance. Historical buckets are sourced from the dedicated
10
+ * `useTimeseries().getHeatpumpPowerTimeseries()` endpoint, and live
11
+ * `HeatpumpValuesUpdateV1` data-bus messages are merged in as new readings
12
+ * arrive.
11
13
  *
12
14
  * Heatpump operation is strongly tied to outdoor temperature (which itself is
13
15
  * weekday-cyclic on average), so the algorithm uses a same-weekday
@@ -50,8 +52,9 @@ class HeatpumpConsumptionForecast {
50
52
  this.source = options.source;
51
53
  }
52
54
  /**
53
- * Pulls historical `HeatpumpValuesUpdateV1` messages, aggregates them into
54
- * 15-minute slots, and starts listening to live heatpump events. Idempotent.
55
+ * Loads historical heatpump power buckets via
56
+ * `useTimeseries().getHeatpumpPowerTimeseries()` and starts listening to
57
+ * live `HeatpumpValuesUpdateV1` events for ongoing updates. Idempotent.
55
58
  */
56
59
  async initialize() {
57
60
  if (this.initialized)
@@ -59,35 +62,20 @@ class HeatpumpConsumptionForecast {
59
62
  this.initialized = true;
60
63
  const now = Date.now();
61
64
  const startMs = now - this.config.historyDays * 24 * 60 * 60 * 1000;
62
- const response = await this.app.useTimeseries().queryDataBusMessages({
63
- applianceId: this.applianceId,
65
+ const response = await this.app.useTimeseries().getHeatpumpPowerTimeseries({
64
66
  startDateIso: new Date(startMs).toISOString(),
65
67
  endDateIso: new Date(now).toISOString(),
66
- messageTypes: [enyo_data_bus_value_js_1.EnyoDataBusMessageEnum.HeatpumpValuesUpdateV1],
67
- limit: 100000,
68
+ applianceIds: [this.applianceId],
69
+ resolution: this.config.resolution,
68
70
  });
69
- const slotAggregator = new Map();
70
- for (const raw of response.messages) {
71
- const message = raw;
72
- const power = message.data.values.powerW;
73
- if (typeof power !== 'number' || Number.isNaN(power))
74
- continue;
75
- const slotMs = (0, forecast_utils_js_1.roundDownTo15Minutes)(new Date(message.timestampIso).getTime());
76
- const existing = slotAggregator.get(slotMs);
77
- if (existing) {
78
- existing.sum += power;
79
- existing.count += 1;
80
- }
81
- else {
82
- slotAggregator.set(slotMs, { sum: power, count: 1 });
83
- }
84
- }
85
- const slots = Array.from(slotAggregator.keys()).sort((a, b) => a - b);
86
- for (const slotMs of slots) {
87
- const agg = slotAggregator.get(slotMs);
71
+ for (const entry of response.entries) {
88
72
  this.history.push({
89
- timestampIso: new Date(slotMs).toISOString(),
90
- values: { powerW: agg.sum / agg.count, samples: agg.count },
73
+ timestampIso: entry.timestampIso,
74
+ values: {
75
+ powerW: entry.heatpumpPowerW,
76
+ powerWh: entry.heatpumpPowerWh,
77
+ samples: 1,
78
+ },
91
79
  });
92
80
  }
93
81
  this.listenerId = this.app.useDataBus().listenForMessages([enyo_data_bus_value_js_1.EnyoDataBusMessageEnum.HeatpumpValuesUpdateV1], (msg) => this.onHeatpumpValues(msg));
@@ -179,10 +167,12 @@ class HeatpumpConsumptionForecast {
179
167
  if (this.currentSlotMs === undefined || this.currentSlotSamples === 0)
180
168
  return;
181
169
  const avg = this.currentSlotPowerSum / this.currentSlotSamples;
182
- (0, forecast_utils_js_1.upsertLiveBucket)(this.history, this.currentSlotMs, { powerW: avg, samples: this.currentSlotSamples }, (existing, incoming) => {
170
+ (0, forecast_utils_js_1.upsertLiveBucket)(this.history, this.currentSlotMs, { powerW: avg, powerWh: (0, forecast_utils_js_1.wToWhPer15Min)(avg), samples: this.currentSlotSamples }, (existing, incoming) => {
183
171
  const total = existing.samples + incoming.samples;
172
+ const mergedPowerW = (existing.powerW * existing.samples + incoming.powerW * incoming.samples) / total;
184
173
  return {
185
- powerW: (existing.powerW * existing.samples + incoming.powerW * incoming.samples) / total,
174
+ powerW: mergedPowerW,
175
+ powerWh: (0, forecast_utils_js_1.wToWhPer15Min)(mergedPowerW),
186
176
  samples: total,
187
177
  };
188
178
  });
@@ -9,8 +9,10 @@ import { BaseForecast, Forecaster, ForecastConfig } from './forecast-types.cjs';
9
9
  export type HeatpumpConsumptionForecastResult = BaseForecast<EnyoDataBusHeatpumpConsumptionForecastV1['data']>;
10
10
  /**
11
11
  * Builds a 24-hour heatpump electrical consumption forecast for a single
12
- * heatpump appliance from historical `HeatpumpValuesUpdateV1` data-bus
13
- * messages, with live updates merged in as new readings arrive.
12
+ * heatpump appliance. Historical buckets are sourced from the dedicated
13
+ * `useTimeseries().getHeatpumpPowerTimeseries()` endpoint, and live
14
+ * `HeatpumpValuesUpdateV1` data-bus messages are merged in as new readings
15
+ * arrive.
14
16
  *
15
17
  * Heatpump operation is strongly tied to outdoor temperature (which itself is
16
18
  * weekday-cyclic on average), so the algorithm uses a same-weekday
@@ -51,8 +53,9 @@ export declare class HeatpumpConsumptionForecast implements Forecaster {
51
53
  config?: ForecastConfig;
52
54
  });
53
55
  /**
54
- * Pulls historical `HeatpumpValuesUpdateV1` messages, aggregates them into
55
- * 15-minute slots, and starts listening to live heatpump events. Idempotent.
56
+ * Loads historical heatpump power buckets via
57
+ * `useTimeseries().getHeatpumpPowerTimeseries()` and starts listening to
58
+ * live `HeatpumpValuesUpdateV1` events for ongoing updates. Idempotent.
56
59
  */
57
60
  initialize(): Promise<void>;
58
61
  /**
@@ -1,4 +1,4 @@
1
- import { DataBusMessageQueryRequest, DataBusMessageQueryResponse, PvProductionTimeseriesRequest, PvProductionTimeseriesResponse, BatterySocTimeseriesRequest, BatterySocTimeseriesResponse, BatteryPowerTimeseriesRequest, BatteryPowerTimeseriesResponse, MeterValuesTimeseriesRequest, MeterValuesTimeseriesResponse, GridPowerTimeseriesRequest, GridPowerTimeseriesResponse, HomeConsumptionTimeseriesRequest, HomeConsumptionTimeseriesResponse, HeatpumpTemperatureTimeseriesRequest, HeatpumpTemperatureTimeseriesResponse, TemperatureSensorTimeseriesRequest, TemperatureSensorTimeseriesResponse, AirConditioningPowerTimeseriesRequest, AirConditioningPowerTimeseriesResponse, AirConditioningTemperatureTimeseriesRequest, AirConditioningTemperatureTimeseriesResponse } from "../types/enyo-timeseries.cjs";
1
+ import { DataBusMessageQueryRequest, DataBusMessageQueryResponse, PvProductionTimeseriesRequest, PvProductionTimeseriesResponse, BatterySocTimeseriesRequest, BatterySocTimeseriesResponse, BatteryPowerTimeseriesRequest, BatteryPowerTimeseriesResponse, MeterValuesTimeseriesRequest, MeterValuesTimeseriesResponse, GridPowerTimeseriesRequest, GridPowerTimeseriesResponse, HomeConsumptionTimeseriesRequest, HomeConsumptionTimeseriesResponse, HeatpumpTemperatureTimeseriesRequest, HeatpumpTemperatureTimeseriesResponse, HeatpumpPowerTimeseriesRequest, HeatpumpPowerTimeseriesResponse, TemperatureSensorTimeseriesRequest, TemperatureSensorTimeseriesResponse, AirConditioningPowerTimeseriesRequest, AirConditioningPowerTimeseriesResponse, AirConditioningTemperatureTimeseriesRequest, AirConditioningTemperatureTimeseriesResponse } from "../types/enyo-timeseries.cjs";
2
2
  /**
3
3
  * Interface for querying historical energy data with configurable bucket granularity.
4
4
  * Provides methods to retrieve aggregated timeseries data for various energy metrics
@@ -159,6 +159,33 @@ export interface EnergyAppTimeseries {
159
159
  * ```
160
160
  */
161
161
  getHeatpumpTemperatureTimeseries(request: HeatpumpTemperatureTimeseriesRequest): Promise<HeatpumpTemperatureTimeseriesResponse>;
162
+ /**
163
+ * Retrieves heatpump power timeseries data aggregated in time buckets.
164
+ * Returns time-weighted average electrical power (W) and cumulative
165
+ * electrical energy (Wh) for each bucket. When the heatpump reports them,
166
+ * also returns the split between space heating and domestic hot water for
167
+ * both electrical consumption and thermal energy delivered.
168
+ *
169
+ * Split fields are optional because not all heatpumps report meter values
170
+ * for the heating / domestic-hot-water categories.
171
+ *
172
+ * @param request - The query parameters including date range and optional appliance filter
173
+ * @returns Promise resolving to heatpump power entries with total electrical consumption
174
+ *
175
+ * @example
176
+ * ```typescript
177
+ * const response = await timeseries.getHeatpumpPowerTimeseries({
178
+ * startDateIso: '2024-01-01T00:00:00Z',
179
+ * endDateIso: '2024-01-02T00:00:00Z'
180
+ * });
181
+ * console.log(`Total heatpump consumption: ${response.totalHeatpumpPowerWh} Wh`);
182
+ * if (response.totalHeatGenerationHeatingWh && response.totalPowerConsumptionHeatingWh) {
183
+ * const cop = response.totalHeatGenerationHeatingWh / response.totalPowerConsumptionHeatingWh;
184
+ * console.log(`Heating COP: ${cop.toFixed(2)}`);
185
+ * }
186
+ * ```
187
+ */
188
+ getHeatpumpPowerTimeseries(request: HeatpumpPowerTimeseriesRequest): Promise<HeatpumpPowerTimeseriesResponse>;
162
189
  /**
163
190
  * Retrieves temperature sensor timeseries data aggregated in time buckets.
164
191
  * Returns per-sensor average temperature readings for each bucket, along with
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.EnyoApplianceTopologyFeatureEnum = exports.EnyoApplianceConnectionType = exports.EnyoApplianceStateEnum = exports.EnyoApplianceTypeEnum = void 0;
3
+ exports.EnyoApplianceTopologyFeatureEnum = exports.EnyoApplianceConnectionType = exports.EnyoApplianceStatusEnum = exports.EnyoApplianceStateEnum = exports.EnyoApplianceTypeEnum = void 0;
4
4
  var EnyoApplianceTypeEnum;
5
5
  (function (EnyoApplianceTypeEnum) {
6
6
  EnyoApplianceTypeEnum["Inverter"] = "Inverter";
@@ -18,6 +18,20 @@ var EnyoApplianceStateEnum;
18
18
  EnyoApplianceStateEnum["Offline"] = "offline";
19
19
  EnyoApplianceStateEnum["ConfigurationRequired"] = "configuration-required";
20
20
  })(EnyoApplianceStateEnum || (exports.EnyoApplianceStateEnum = EnyoApplianceStateEnum = {}));
21
+ /**
22
+ * Health status of an appliance. Orthogonal to {@link EnyoApplianceStateEnum},
23
+ * which describes connectivity. `Healthy` means the appliance is operating
24
+ * normally; `Faulted` means it has reported an internal error and may need
25
+ * attention. Vendor- or protocol-specific details should be conveyed via
26
+ * accompanying error codes.
27
+ */
28
+ var EnyoApplianceStatusEnum;
29
+ (function (EnyoApplianceStatusEnum) {
30
+ /** Appliance is operating normally */
31
+ EnyoApplianceStatusEnum["Healthy"] = "healthy";
32
+ /** Appliance has reported an internal fault */
33
+ EnyoApplianceStatusEnum["Faulted"] = "faulted";
34
+ })(EnyoApplianceStatusEnum || (exports.EnyoApplianceStatusEnum = EnyoApplianceStatusEnum = {}));
21
35
  var EnyoApplianceConnectionType;
22
36
  (function (EnyoApplianceConnectionType) {
23
37
  EnyoApplianceConnectionType["Connector"] = "Connector";
@@ -25,6 +25,19 @@ export declare enum EnyoApplianceStateEnum {
25
25
  Offline = "offline",
26
26
  ConfigurationRequired = "configuration-required"
27
27
  }
28
+ /**
29
+ * Health status of an appliance. Orthogonal to {@link EnyoApplianceStateEnum},
30
+ * which describes connectivity. `Healthy` means the appliance is operating
31
+ * normally; `Faulted` means it has reported an internal error and may need
32
+ * attention. Vendor- or protocol-specific details should be conveyed via
33
+ * accompanying error codes.
34
+ */
35
+ export declare enum EnyoApplianceStatusEnum {
36
+ /** Appliance is operating normally */
37
+ Healthy = "healthy",
38
+ /** Appliance has reported an internal fault */
39
+ Faulted = "faulted"
40
+ }
28
41
  export interface EnyoApplianceNetworkMetadata {
29
42
  /** If the appliance is connected via cellular network, you can put the imsi here*/
30
43
  imsi?: string;
@@ -55,6 +68,8 @@ export interface EnyoApplianceMetadata {
55
68
  hostname?: string;
56
69
  ipAddress?: string;
57
70
  state?: EnyoApplianceStateEnum;
71
+ /** Health status of the appliance (e.g. healthy or faulted) */
72
+ status?: EnyoApplianceStatusEnum;
58
73
  network?: EnyoApplianceNetworkMetadata;
59
74
  modbus?: EnyoApplianceModbusMetadata;
60
75
  /** Optional MQTT configuration */
@@ -113,6 +113,7 @@ var EnyoDataBusMessageEnum;
113
113
  EnyoDataBusMessageEnum["MeterValuesUpdateV1"] = "MeterValuesUpdateV1";
114
114
  EnyoDataBusMessageEnum["BatteryValuesUpdateV1"] = "BatteryValuesUpdateV1";
115
115
  EnyoDataBusMessageEnum["ApplianceFlexibilityAnnouncementV1"] = "ApplianceFlexibilityAnnouncementV1";
116
+ EnyoDataBusMessageEnum["ApplianceStateUpdateV1"] = "ApplianceStateUpdateV1";
116
117
  EnyoDataBusMessageEnum["HeatpumpValuesUpdateV1"] = "HeatpumpValuesUpdateV1";
117
118
  EnyoDataBusMessageEnum["ChargingStartedV1"] = "ChargingStartedV1";
118
119
  EnyoDataBusMessageEnum["ChargingMeterValuesUpdateV1"] = "ChargingMeterValuesUpdateV1";
@@ -1,4 +1,4 @@
1
- import { EnyoApplianceStateEnum, EnyoApplianceTypeEnum } from "./enyo-appliance.cjs";
1
+ import { EnyoApplianceStateEnum, EnyoApplianceStatusEnum, EnyoApplianceTypeEnum } from "./enyo-appliance.cjs";
2
2
  import { EnyoSourceEnum } from "./enyo-source.enum.cjs";
3
3
  import { EnyoOcppRelativeSchedule } from "./enyo-ocpp.cjs";
4
4
  import { EnyoChargerApplianceStatusEnum } from "./enyo-charger-appliance.cjs";
@@ -152,6 +152,7 @@ export declare enum EnyoDataBusMessageEnum {
152
152
  MeterValuesUpdateV1 = "MeterValuesUpdateV1",
153
153
  BatteryValuesUpdateV1 = "BatteryValuesUpdateV1",
154
154
  ApplianceFlexibilityAnnouncementV1 = "ApplianceFlexibilityAnnouncementV1",
155
+ ApplianceStateUpdateV1 = "ApplianceStateUpdateV1",
155
156
  HeatpumpValuesUpdateV1 = "HeatpumpValuesUpdateV1",
156
157
  ChargingStartedV1 = "ChargingStartedV1",
157
158
  ChargingMeterValuesUpdateV1 = "ChargingMeterValuesUpdateV1",
@@ -276,6 +277,8 @@ export interface EnyoDataBusInverterValuesV1String {
276
277
  powerW?: number;
277
278
  current?: number;
278
279
  state?: EnyoStringStateEnum;
280
+ /** Cumulative energy meter reading for this DC string in Watt hours */
281
+ meterValueWh?: number;
279
282
  }
280
283
  export interface EnyoDataBusInverterValuesV1 extends EnyoDataBusMessage {
281
284
  type: 'message';
@@ -303,6 +306,8 @@ export interface EnyoDataBusInverterValuesV1 extends EnyoDataBusMessage {
303
306
  dcPowerW?: number;
304
307
  /** Active power Limitation of the Inverter */
305
308
  activePowerLimitationW?: number;
309
+ /** Cumulative AC energy meter reading of the inverter in Watt hours */
310
+ meterValueWh?: number;
306
311
  /** DC String values. Please only provide active strings */
307
312
  strings?: EnyoDataBusInverterValuesV1String[];
308
313
  };
@@ -320,6 +325,31 @@ export interface EnyoDataBusApplianceFlexibilityAnnouncementV1 extends EnyoDataB
320
325
  };
321
326
  };
322
327
  }
328
+ /**
329
+ * Message sent when an appliance's connectivity state and/or health status
330
+ * changes. At least one of `state` or `status` should be set; both may be
331
+ * provided together if they change in the same event. `errorCodes` carries
332
+ * vendor- or protocol-specific codes that explain a transition into a
333
+ * `faulted` status.
334
+ */
335
+ export interface EnyoDataBusApplianceStateUpdateV1 extends EnyoDataBusMessage {
336
+ type: 'message';
337
+ message: EnyoDataBusMessageEnum.ApplianceStateUpdateV1;
338
+ /** ID of the appliance whose state and/or status changed */
339
+ applianceId: string;
340
+ data: {
341
+ /** New connectivity state of the appliance, if it changed */
342
+ state?: EnyoApplianceStateEnum;
343
+ /** New health status of the appliance, if it changed */
344
+ status?: EnyoApplianceStatusEnum;
345
+ /**
346
+ * Optional vendor- or protocol-specific error codes that explain the
347
+ * current status. Typically populated when transitioning into
348
+ * `faulted`; omitted or empty when there is nothing to report.
349
+ */
350
+ errorCodes?: string[];
351
+ };
352
+ }
323
353
  export interface EnyoDataBusChargingStartedV1 extends EnyoDataBusMessage {
324
354
  type: 'message';
325
355
  message: EnyoDataBusMessageEnum.ChargingStartedV1;
@@ -342,6 +342,51 @@ export interface HeatpumpTemperatureTimeseriesResponse extends TimeseriesRespons
342
342
  /** Average buffer tank temperature in degrees Celsius across all buckets */
343
343
  averageBufferTankTemperatureC?: number;
344
344
  }
345
+ /**
346
+ * A single entry in the heatpump power timeseries.
347
+ * Contains electrical power and energy values, optional split between space
348
+ * heating and domestic hot water consumption, and optional thermal energy
349
+ * delivered (heat generation) for the same split, for a single time bucket.
350
+ */
351
+ export interface HeatpumpPowerTimeseriesEntry extends TimeseriesEntryBase {
352
+ /** Time-weighted average heatpump electrical power consumption in Watts for this bucket */
353
+ heatpumpPowerW: number;
354
+ /** Cumulative heatpump electrical energy consumption in Watt-hours for this bucket */
355
+ heatpumpPowerWh: number;
356
+ /** Electrical energy consumed for space heating in Watt-hours for this bucket */
357
+ powerConsumptionHeatingWh?: number;
358
+ /** Electrical energy consumed for domestic hot water in Watt-hours for this bucket */
359
+ powerConsumptionDomesticHotWaterWh?: number;
360
+ /** Thermal energy delivered for space heating in Watt-hours for this bucket */
361
+ heatGenerationHeatingWh?: number;
362
+ /** Thermal energy delivered for domestic hot water in Watt-hours for this bucket */
363
+ heatGenerationDomesticHotWaterWh?: number;
364
+ }
365
+ /**
366
+ * Request parameters for querying heatpump power timeseries data.
367
+ */
368
+ export interface HeatpumpPowerTimeseriesRequest extends TimeseriesRequestBase {
369
+ }
370
+ /**
371
+ * Response containing heatpump power timeseries data.
372
+ * Aggregated totals for the heating / domestic-hot-water splits and for
373
+ * heat generation are optional because not all heatpumps report meter values
374
+ * for every category.
375
+ */
376
+ export interface HeatpumpPowerTimeseriesResponse extends TimeseriesResponseBase {
377
+ /** Array of heatpump power entries, one per time bucket */
378
+ entries: HeatpumpPowerTimeseriesEntry[];
379
+ /** Total heatpump electrical energy consumption in Watt-hours across all buckets */
380
+ totalHeatpumpPowerWh: number;
381
+ /** Total electrical energy consumed for space heating in Watt-hours across all buckets */
382
+ totalPowerConsumptionHeatingWh?: number;
383
+ /** Total electrical energy consumed for domestic hot water in Watt-hours across all buckets */
384
+ totalPowerConsumptionDomesticHotWaterWh?: number;
385
+ /** Total thermal energy delivered for space heating in Watt-hours across all buckets */
386
+ totalHeatGenerationHeatingWh?: number;
387
+ /** Total thermal energy delivered for domestic hot water in Watt-hours across all buckets */
388
+ totalHeatGenerationDomesticHotWaterWh?: number;
389
+ }
345
390
  /**
346
391
  * A single entry in the temperature sensor timeseries.
347
392
  * Contains per-sensor average temperature readings for a single time bucket.
@@ -9,7 +9,7 @@ exports.getSdkVersion = getSdkVersion;
9
9
  /**
10
10
  * Current version of the enyo Energy App SDK.
11
11
  */
12
- exports.SDK_VERSION = '0.0.110';
12
+ exports.SDK_VERSION = '0.0.112';
13
13
  /**
14
14
  * Gets the current SDK version.
15
15
  * @returns The semantic version string of the SDK
@@ -5,7 +5,7 @@
5
5
  /**
6
6
  * Current version of the enyo Energy App SDK.
7
7
  */
8
- export declare const SDK_VERSION = "0.0.110";
8
+ export declare const SDK_VERSION = "0.0.112";
9
9
  /**
10
10
  * Gets the current SDK version.
11
11
  * @returns The semantic version string of the SDK
@@ -9,8 +9,10 @@ import { BaseForecast, Forecaster, ForecastConfig } from './forecast-types.js';
9
9
  export type HeatpumpConsumptionForecastResult = BaseForecast<EnyoDataBusHeatpumpConsumptionForecastV1['data']>;
10
10
  /**
11
11
  * Builds a 24-hour heatpump electrical consumption forecast for a single
12
- * heatpump appliance from historical `HeatpumpValuesUpdateV1` data-bus
13
- * messages, with live updates merged in as new readings arrive.
12
+ * heatpump appliance. Historical buckets are sourced from the dedicated
13
+ * `useTimeseries().getHeatpumpPowerTimeseries()` endpoint, and live
14
+ * `HeatpumpValuesUpdateV1` data-bus messages are merged in as new readings
15
+ * arrive.
14
16
  *
15
17
  * Heatpump operation is strongly tied to outdoor temperature (which itself is
16
18
  * weekday-cyclic on average), so the algorithm uses a same-weekday
@@ -51,8 +53,9 @@ export declare class HeatpumpConsumptionForecast implements Forecaster {
51
53
  config?: ForecastConfig;
52
54
  });
53
55
  /**
54
- * Pulls historical `HeatpumpValuesUpdateV1` messages, aggregates them into
55
- * 15-minute slots, and starts listening to live heatpump events. Idempotent.
56
+ * Loads historical heatpump power buckets via
57
+ * `useTimeseries().getHeatpumpPowerTimeseries()` and starts listening to
58
+ * live `HeatpumpValuesUpdateV1` events for ongoing updates. Idempotent.
56
59
  */
57
60
  initialize(): Promise<void>;
58
61
  /**
@@ -3,8 +3,10 @@ import { resolveForecastConfig, } from './forecast-types.js';
3
3
  import { FIFTEEN_MIN_MS, alignForecastToRecentActuals, buildForecastSlots, buildIsoIndex, buildSlotMap, parseHistory, roundDownTo15Minutes, trimHistory, upsertLiveBucket, weightedAverageBySlot, wToWhPer15Min, } from './forecast-utils.js';
4
4
  /**
5
5
  * Builds a 24-hour heatpump electrical consumption forecast for a single
6
- * heatpump appliance from historical `HeatpumpValuesUpdateV1` data-bus
7
- * messages, with live updates merged in as new readings arrive.
6
+ * heatpump appliance. Historical buckets are sourced from the dedicated
7
+ * `useTimeseries().getHeatpumpPowerTimeseries()` endpoint, and live
8
+ * `HeatpumpValuesUpdateV1` data-bus messages are merged in as new readings
9
+ * arrive.
8
10
  *
9
11
  * Heatpump operation is strongly tied to outdoor temperature (which itself is
10
12
  * weekday-cyclic on average), so the algorithm uses a same-weekday
@@ -47,8 +49,9 @@ export class HeatpumpConsumptionForecast {
47
49
  this.source = options.source;
48
50
  }
49
51
  /**
50
- * Pulls historical `HeatpumpValuesUpdateV1` messages, aggregates them into
51
- * 15-minute slots, and starts listening to live heatpump events. Idempotent.
52
+ * Loads historical heatpump power buckets via
53
+ * `useTimeseries().getHeatpumpPowerTimeseries()` and starts listening to
54
+ * live `HeatpumpValuesUpdateV1` events for ongoing updates. Idempotent.
52
55
  */
53
56
  async initialize() {
54
57
  if (this.initialized)
@@ -56,35 +59,20 @@ export class HeatpumpConsumptionForecast {
56
59
  this.initialized = true;
57
60
  const now = Date.now();
58
61
  const startMs = now - this.config.historyDays * 24 * 60 * 60 * 1000;
59
- const response = await this.app.useTimeseries().queryDataBusMessages({
60
- applianceId: this.applianceId,
62
+ const response = await this.app.useTimeseries().getHeatpumpPowerTimeseries({
61
63
  startDateIso: new Date(startMs).toISOString(),
62
64
  endDateIso: new Date(now).toISOString(),
63
- messageTypes: [EnyoDataBusMessageEnum.HeatpumpValuesUpdateV1],
64
- limit: 100000,
65
+ applianceIds: [this.applianceId],
66
+ resolution: this.config.resolution,
65
67
  });
66
- const slotAggregator = new Map();
67
- for (const raw of response.messages) {
68
- const message = raw;
69
- const power = message.data.values.powerW;
70
- if (typeof power !== 'number' || Number.isNaN(power))
71
- continue;
72
- const slotMs = roundDownTo15Minutes(new Date(message.timestampIso).getTime());
73
- const existing = slotAggregator.get(slotMs);
74
- if (existing) {
75
- existing.sum += power;
76
- existing.count += 1;
77
- }
78
- else {
79
- slotAggregator.set(slotMs, { sum: power, count: 1 });
80
- }
81
- }
82
- const slots = Array.from(slotAggregator.keys()).sort((a, b) => a - b);
83
- for (const slotMs of slots) {
84
- const agg = slotAggregator.get(slotMs);
68
+ for (const entry of response.entries) {
85
69
  this.history.push({
86
- timestampIso: new Date(slotMs).toISOString(),
87
- values: { powerW: agg.sum / agg.count, samples: agg.count },
70
+ timestampIso: entry.timestampIso,
71
+ values: {
72
+ powerW: entry.heatpumpPowerW,
73
+ powerWh: entry.heatpumpPowerWh,
74
+ samples: 1,
75
+ },
88
76
  });
89
77
  }
90
78
  this.listenerId = this.app.useDataBus().listenForMessages([EnyoDataBusMessageEnum.HeatpumpValuesUpdateV1], (msg) => this.onHeatpumpValues(msg));
@@ -176,10 +164,12 @@ export class HeatpumpConsumptionForecast {
176
164
  if (this.currentSlotMs === undefined || this.currentSlotSamples === 0)
177
165
  return;
178
166
  const avg = this.currentSlotPowerSum / this.currentSlotSamples;
179
- upsertLiveBucket(this.history, this.currentSlotMs, { powerW: avg, samples: this.currentSlotSamples }, (existing, incoming) => {
167
+ upsertLiveBucket(this.history, this.currentSlotMs, { powerW: avg, powerWh: wToWhPer15Min(avg), samples: this.currentSlotSamples }, (existing, incoming) => {
180
168
  const total = existing.samples + incoming.samples;
169
+ const mergedPowerW = (existing.powerW * existing.samples + incoming.powerW * incoming.samples) / total;
181
170
  return {
182
- powerW: (existing.powerW * existing.samples + incoming.powerW * incoming.samples) / total,
171
+ powerW: mergedPowerW,
172
+ powerWh: wToWhPer15Min(mergedPowerW),
183
173
  samples: total,
184
174
  };
185
175
  });
@@ -1,4 +1,4 @@
1
- import { DataBusMessageQueryRequest, DataBusMessageQueryResponse, PvProductionTimeseriesRequest, PvProductionTimeseriesResponse, BatterySocTimeseriesRequest, BatterySocTimeseriesResponse, BatteryPowerTimeseriesRequest, BatteryPowerTimeseriesResponse, MeterValuesTimeseriesRequest, MeterValuesTimeseriesResponse, GridPowerTimeseriesRequest, GridPowerTimeseriesResponse, HomeConsumptionTimeseriesRequest, HomeConsumptionTimeseriesResponse, HeatpumpTemperatureTimeseriesRequest, HeatpumpTemperatureTimeseriesResponse, TemperatureSensorTimeseriesRequest, TemperatureSensorTimeseriesResponse, AirConditioningPowerTimeseriesRequest, AirConditioningPowerTimeseriesResponse, AirConditioningTemperatureTimeseriesRequest, AirConditioningTemperatureTimeseriesResponse } from "../types/enyo-timeseries.js";
1
+ import { DataBusMessageQueryRequest, DataBusMessageQueryResponse, PvProductionTimeseriesRequest, PvProductionTimeseriesResponse, BatterySocTimeseriesRequest, BatterySocTimeseriesResponse, BatteryPowerTimeseriesRequest, BatteryPowerTimeseriesResponse, MeterValuesTimeseriesRequest, MeterValuesTimeseriesResponse, GridPowerTimeseriesRequest, GridPowerTimeseriesResponse, HomeConsumptionTimeseriesRequest, HomeConsumptionTimeseriesResponse, HeatpumpTemperatureTimeseriesRequest, HeatpumpTemperatureTimeseriesResponse, HeatpumpPowerTimeseriesRequest, HeatpumpPowerTimeseriesResponse, TemperatureSensorTimeseriesRequest, TemperatureSensorTimeseriesResponse, AirConditioningPowerTimeseriesRequest, AirConditioningPowerTimeseriesResponse, AirConditioningTemperatureTimeseriesRequest, AirConditioningTemperatureTimeseriesResponse } from "../types/enyo-timeseries.js";
2
2
  /**
3
3
  * Interface for querying historical energy data with configurable bucket granularity.
4
4
  * Provides methods to retrieve aggregated timeseries data for various energy metrics
@@ -159,6 +159,33 @@ export interface EnergyAppTimeseries {
159
159
  * ```
160
160
  */
161
161
  getHeatpumpTemperatureTimeseries(request: HeatpumpTemperatureTimeseriesRequest): Promise<HeatpumpTemperatureTimeseriesResponse>;
162
+ /**
163
+ * Retrieves heatpump power timeseries data aggregated in time buckets.
164
+ * Returns time-weighted average electrical power (W) and cumulative
165
+ * electrical energy (Wh) for each bucket. When the heatpump reports them,
166
+ * also returns the split between space heating and domestic hot water for
167
+ * both electrical consumption and thermal energy delivered.
168
+ *
169
+ * Split fields are optional because not all heatpumps report meter values
170
+ * for the heating / domestic-hot-water categories.
171
+ *
172
+ * @param request - The query parameters including date range and optional appliance filter
173
+ * @returns Promise resolving to heatpump power entries with total electrical consumption
174
+ *
175
+ * @example
176
+ * ```typescript
177
+ * const response = await timeseries.getHeatpumpPowerTimeseries({
178
+ * startDateIso: '2024-01-01T00:00:00Z',
179
+ * endDateIso: '2024-01-02T00:00:00Z'
180
+ * });
181
+ * console.log(`Total heatpump consumption: ${response.totalHeatpumpPowerWh} Wh`);
182
+ * if (response.totalHeatGenerationHeatingWh && response.totalPowerConsumptionHeatingWh) {
183
+ * const cop = response.totalHeatGenerationHeatingWh / response.totalPowerConsumptionHeatingWh;
184
+ * console.log(`Heating COP: ${cop.toFixed(2)}`);
185
+ * }
186
+ * ```
187
+ */
188
+ getHeatpumpPowerTimeseries(request: HeatpumpPowerTimeseriesRequest): Promise<HeatpumpPowerTimeseriesResponse>;
162
189
  /**
163
190
  * Retrieves temperature sensor timeseries data aggregated in time buckets.
164
191
  * Returns per-sensor average temperature readings for each bucket, along with
@@ -25,6 +25,19 @@ export declare enum EnyoApplianceStateEnum {
25
25
  Offline = "offline",
26
26
  ConfigurationRequired = "configuration-required"
27
27
  }
28
+ /**
29
+ * Health status of an appliance. Orthogonal to {@link EnyoApplianceStateEnum},
30
+ * which describes connectivity. `Healthy` means the appliance is operating
31
+ * normally; `Faulted` means it has reported an internal error and may need
32
+ * attention. Vendor- or protocol-specific details should be conveyed via
33
+ * accompanying error codes.
34
+ */
35
+ export declare enum EnyoApplianceStatusEnum {
36
+ /** Appliance is operating normally */
37
+ Healthy = "healthy",
38
+ /** Appliance has reported an internal fault */
39
+ Faulted = "faulted"
40
+ }
28
41
  export interface EnyoApplianceNetworkMetadata {
29
42
  /** If the appliance is connected via cellular network, you can put the imsi here*/
30
43
  imsi?: string;
@@ -55,6 +68,8 @@ export interface EnyoApplianceMetadata {
55
68
  hostname?: string;
56
69
  ipAddress?: string;
57
70
  state?: EnyoApplianceStateEnum;
71
+ /** Health status of the appliance (e.g. healthy or faulted) */
72
+ status?: EnyoApplianceStatusEnum;
58
73
  network?: EnyoApplianceNetworkMetadata;
59
74
  modbus?: EnyoApplianceModbusMetadata;
60
75
  /** Optional MQTT configuration */
@@ -15,6 +15,20 @@ export var EnyoApplianceStateEnum;
15
15
  EnyoApplianceStateEnum["Offline"] = "offline";
16
16
  EnyoApplianceStateEnum["ConfigurationRequired"] = "configuration-required";
17
17
  })(EnyoApplianceStateEnum || (EnyoApplianceStateEnum = {}));
18
+ /**
19
+ * Health status of an appliance. Orthogonal to {@link EnyoApplianceStateEnum},
20
+ * which describes connectivity. `Healthy` means the appliance is operating
21
+ * normally; `Faulted` means it has reported an internal error and may need
22
+ * attention. Vendor- or protocol-specific details should be conveyed via
23
+ * accompanying error codes.
24
+ */
25
+ export var EnyoApplianceStatusEnum;
26
+ (function (EnyoApplianceStatusEnum) {
27
+ /** Appliance is operating normally */
28
+ EnyoApplianceStatusEnum["Healthy"] = "healthy";
29
+ /** Appliance has reported an internal fault */
30
+ EnyoApplianceStatusEnum["Faulted"] = "faulted";
31
+ })(EnyoApplianceStatusEnum || (EnyoApplianceStatusEnum = {}));
18
32
  export var EnyoApplianceConnectionType;
19
33
  (function (EnyoApplianceConnectionType) {
20
34
  EnyoApplianceConnectionType["Connector"] = "Connector";
@@ -1,4 +1,4 @@
1
- import { EnyoApplianceStateEnum, EnyoApplianceTypeEnum } from "./enyo-appliance.js";
1
+ import { EnyoApplianceStateEnum, EnyoApplianceStatusEnum, EnyoApplianceTypeEnum } from "./enyo-appliance.js";
2
2
  import { EnyoSourceEnum } from "./enyo-source.enum.js";
3
3
  import { EnyoOcppRelativeSchedule } from "./enyo-ocpp.js";
4
4
  import { EnyoChargerApplianceStatusEnum } from "./enyo-charger-appliance.js";
@@ -152,6 +152,7 @@ export declare enum EnyoDataBusMessageEnum {
152
152
  MeterValuesUpdateV1 = "MeterValuesUpdateV1",
153
153
  BatteryValuesUpdateV1 = "BatteryValuesUpdateV1",
154
154
  ApplianceFlexibilityAnnouncementV1 = "ApplianceFlexibilityAnnouncementV1",
155
+ ApplianceStateUpdateV1 = "ApplianceStateUpdateV1",
155
156
  HeatpumpValuesUpdateV1 = "HeatpumpValuesUpdateV1",
156
157
  ChargingStartedV1 = "ChargingStartedV1",
157
158
  ChargingMeterValuesUpdateV1 = "ChargingMeterValuesUpdateV1",
@@ -276,6 +277,8 @@ export interface EnyoDataBusInverterValuesV1String {
276
277
  powerW?: number;
277
278
  current?: number;
278
279
  state?: EnyoStringStateEnum;
280
+ /** Cumulative energy meter reading for this DC string in Watt hours */
281
+ meterValueWh?: number;
279
282
  }
280
283
  export interface EnyoDataBusInverterValuesV1 extends EnyoDataBusMessage {
281
284
  type: 'message';
@@ -303,6 +306,8 @@ export interface EnyoDataBusInverterValuesV1 extends EnyoDataBusMessage {
303
306
  dcPowerW?: number;
304
307
  /** Active power Limitation of the Inverter */
305
308
  activePowerLimitationW?: number;
309
+ /** Cumulative AC energy meter reading of the inverter in Watt hours */
310
+ meterValueWh?: number;
306
311
  /** DC String values. Please only provide active strings */
307
312
  strings?: EnyoDataBusInverterValuesV1String[];
308
313
  };
@@ -320,6 +325,31 @@ export interface EnyoDataBusApplianceFlexibilityAnnouncementV1 extends EnyoDataB
320
325
  };
321
326
  };
322
327
  }
328
+ /**
329
+ * Message sent when an appliance's connectivity state and/or health status
330
+ * changes. At least one of `state` or `status` should be set; both may be
331
+ * provided together if they change in the same event. `errorCodes` carries
332
+ * vendor- or protocol-specific codes that explain a transition into a
333
+ * `faulted` status.
334
+ */
335
+ export interface EnyoDataBusApplianceStateUpdateV1 extends EnyoDataBusMessage {
336
+ type: 'message';
337
+ message: EnyoDataBusMessageEnum.ApplianceStateUpdateV1;
338
+ /** ID of the appliance whose state and/or status changed */
339
+ applianceId: string;
340
+ data: {
341
+ /** New connectivity state of the appliance, if it changed */
342
+ state?: EnyoApplianceStateEnum;
343
+ /** New health status of the appliance, if it changed */
344
+ status?: EnyoApplianceStatusEnum;
345
+ /**
346
+ * Optional vendor- or protocol-specific error codes that explain the
347
+ * current status. Typically populated when transitioning into
348
+ * `faulted`; omitted or empty when there is nothing to report.
349
+ */
350
+ errorCodes?: string[];
351
+ };
352
+ }
323
353
  export interface EnyoDataBusChargingStartedV1 extends EnyoDataBusMessage {
324
354
  type: 'message';
325
355
  message: EnyoDataBusMessageEnum.ChargingStartedV1;
@@ -110,6 +110,7 @@ export var EnyoDataBusMessageEnum;
110
110
  EnyoDataBusMessageEnum["MeterValuesUpdateV1"] = "MeterValuesUpdateV1";
111
111
  EnyoDataBusMessageEnum["BatteryValuesUpdateV1"] = "BatteryValuesUpdateV1";
112
112
  EnyoDataBusMessageEnum["ApplianceFlexibilityAnnouncementV1"] = "ApplianceFlexibilityAnnouncementV1";
113
+ EnyoDataBusMessageEnum["ApplianceStateUpdateV1"] = "ApplianceStateUpdateV1";
113
114
  EnyoDataBusMessageEnum["HeatpumpValuesUpdateV1"] = "HeatpumpValuesUpdateV1";
114
115
  EnyoDataBusMessageEnum["ChargingStartedV1"] = "ChargingStartedV1";
115
116
  EnyoDataBusMessageEnum["ChargingMeterValuesUpdateV1"] = "ChargingMeterValuesUpdateV1";
@@ -342,6 +342,51 @@ export interface HeatpumpTemperatureTimeseriesResponse extends TimeseriesRespons
342
342
  /** Average buffer tank temperature in degrees Celsius across all buckets */
343
343
  averageBufferTankTemperatureC?: number;
344
344
  }
345
+ /**
346
+ * A single entry in the heatpump power timeseries.
347
+ * Contains electrical power and energy values, optional split between space
348
+ * heating and domestic hot water consumption, and optional thermal energy
349
+ * delivered (heat generation) for the same split, for a single time bucket.
350
+ */
351
+ export interface HeatpumpPowerTimeseriesEntry extends TimeseriesEntryBase {
352
+ /** Time-weighted average heatpump electrical power consumption in Watts for this bucket */
353
+ heatpumpPowerW: number;
354
+ /** Cumulative heatpump electrical energy consumption in Watt-hours for this bucket */
355
+ heatpumpPowerWh: number;
356
+ /** Electrical energy consumed for space heating in Watt-hours for this bucket */
357
+ powerConsumptionHeatingWh?: number;
358
+ /** Electrical energy consumed for domestic hot water in Watt-hours for this bucket */
359
+ powerConsumptionDomesticHotWaterWh?: number;
360
+ /** Thermal energy delivered for space heating in Watt-hours for this bucket */
361
+ heatGenerationHeatingWh?: number;
362
+ /** Thermal energy delivered for domestic hot water in Watt-hours for this bucket */
363
+ heatGenerationDomesticHotWaterWh?: number;
364
+ }
365
+ /**
366
+ * Request parameters for querying heatpump power timeseries data.
367
+ */
368
+ export interface HeatpumpPowerTimeseriesRequest extends TimeseriesRequestBase {
369
+ }
370
+ /**
371
+ * Response containing heatpump power timeseries data.
372
+ * Aggregated totals for the heating / domestic-hot-water splits and for
373
+ * heat generation are optional because not all heatpumps report meter values
374
+ * for every category.
375
+ */
376
+ export interface HeatpumpPowerTimeseriesResponse extends TimeseriesResponseBase {
377
+ /** Array of heatpump power entries, one per time bucket */
378
+ entries: HeatpumpPowerTimeseriesEntry[];
379
+ /** Total heatpump electrical energy consumption in Watt-hours across all buckets */
380
+ totalHeatpumpPowerWh: number;
381
+ /** Total electrical energy consumed for space heating in Watt-hours across all buckets */
382
+ totalPowerConsumptionHeatingWh?: number;
383
+ /** Total electrical energy consumed for domestic hot water in Watt-hours across all buckets */
384
+ totalPowerConsumptionDomesticHotWaterWh?: number;
385
+ /** Total thermal energy delivered for space heating in Watt-hours across all buckets */
386
+ totalHeatGenerationHeatingWh?: number;
387
+ /** Total thermal energy delivered for domestic hot water in Watt-hours across all buckets */
388
+ totalHeatGenerationDomesticHotWaterWh?: number;
389
+ }
345
390
  /**
346
391
  * A single entry in the temperature sensor timeseries.
347
392
  * Contains per-sensor average temperature readings for a single time bucket.
package/dist/version.d.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  /**
6
6
  * Current version of the enyo Energy App SDK.
7
7
  */
8
- export declare const SDK_VERSION = "0.0.110";
8
+ export declare const SDK_VERSION = "0.0.112";
9
9
  /**
10
10
  * Gets the current SDK version.
11
11
  * @returns The semantic version string of the SDK
package/dist/version.js CHANGED
@@ -5,7 +5,7 @@
5
5
  /**
6
6
  * Current version of the enyo Energy App SDK.
7
7
  */
8
- export const SDK_VERSION = '0.0.110';
8
+ export const SDK_VERSION = '0.0.112';
9
9
  /**
10
10
  * Gets the current SDK version.
11
11
  * @returns The semantic version string of the SDK
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@enyo-energy/energy-app-sdk",
3
- "version": "0.0.110",
3
+ "version": "0.0.112",
4
4
  "description": "enyo Energy App SDK",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",