@enyo-energy/energy-app-sdk 1.20.0 → 1.22.0

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.
Files changed (36) hide show
  1. package/README.md +81 -0
  2. package/dist/cjs/energy-app-package-definition.cjs +19 -1
  3. package/dist/cjs/energy-app-package-definition.d.cts +37 -0
  4. package/dist/cjs/energy-app-permission.type.cjs +2 -0
  5. package/dist/cjs/energy-app-permission.type.d.cts +2 -0
  6. package/dist/cjs/energy-app.cjs +9 -0
  7. package/dist/cjs/energy-app.d.cts +8 -0
  8. package/dist/cjs/enyo-energy-app-sdk.d.cts +3 -0
  9. package/dist/cjs/implementations/firmware/firmware-validators.cjs +16 -2
  10. package/dist/cjs/index.cjs +2 -0
  11. package/dist/cjs/index.d.cts +2 -0
  12. package/dist/cjs/packages/energy-app-modbus-rtu.d.cts +31 -2
  13. package/dist/cjs/packages/energy-app-weather-history.cjs +2 -0
  14. package/dist/cjs/packages/energy-app-weather-history.d.cts +138 -0
  15. package/dist/cjs/types/enyo-weather-history.cjs +43 -0
  16. package/dist/cjs/types/enyo-weather-history.d.cts +284 -0
  17. package/dist/cjs/version.cjs +1 -1
  18. package/dist/cjs/version.d.cts +1 -1
  19. package/dist/energy-app-package-definition.d.ts +37 -0
  20. package/dist/energy-app-package-definition.js +18 -0
  21. package/dist/energy-app-permission.type.d.ts +2 -0
  22. package/dist/energy-app-permission.type.js +2 -0
  23. package/dist/energy-app.d.ts +8 -0
  24. package/dist/energy-app.js +9 -0
  25. package/dist/enyo-energy-app-sdk.d.ts +3 -0
  26. package/dist/implementations/firmware/firmware-validators.js +16 -2
  27. package/dist/index.d.ts +2 -0
  28. package/dist/index.js +2 -0
  29. package/dist/packages/energy-app-modbus-rtu.d.ts +31 -2
  30. package/dist/packages/energy-app-weather-history.d.ts +138 -0
  31. package/dist/packages/energy-app-weather-history.js +1 -0
  32. package/dist/types/enyo-weather-history.d.ts +284 -0
  33. package/dist/types/enyo-weather-history.js +40 -0
  34. package/dist/version.d.ts +1 -1
  35. package/dist/version.js +1 -1
  36. package/package.json +1 -1
package/README.md CHANGED
@@ -375,6 +375,7 @@ Energy Apps use a granular permissions system to control access to system resour
375
375
  - **`EnergyManager`**: Run as the active energy manager
376
376
  - **`EnergyManagerInfo`**: Read information about the active energy manager
377
377
  - **`WeatherForecastRegister`** / **`WeatherForecastUse`**: Publish / consume weather forecasts
378
+ - **`WeatherHistoryRegister`** / **`WeatherHistoryUse`**: Publish / consume observed (past) weather data
378
379
  - **`PvForecastRegister`** / **`PvForecastUse`**: Publish / consume PV forecasts
379
380
  - **`DynamicPriceForecastRegister`** / **`DynamicPriceForecastUse`**: Publish / consume dynamic-price forecasts
380
381
  - **`PvSystemRegister`** / **`PvSystemUse`**: Register / read PV system configuration
@@ -1769,6 +1770,86 @@ const byCoords = await weather.getWeatherForecastByCoordinates('wx-prod', 48.13,
1769
1770
 
1770
1771
  Publishers need `WeatherForecastRegister`; consumers need `WeatherForecastUse`.
1771
1772
 
1773
+ #### `useWeatherHistory(): EnergyAppWeatherHistory`
1774
+
1775
+ The backward-looking counterpart. Where forecasting says what the weather *will* do, this says what it
1776
+ *did* between two timestamps — temperature, wind, cloud cover and solar irradiance. That is what you
1777
+ need to correlate measured consumption with the weather that drove it, build heating-degree-day
1778
+ statistics, check measured PV production against the irradiance that was actually available, or train
1779
+ a consumption model on outdoor conditions.
1780
+
1781
+ ```typescript
1782
+ const weatherHistory = energyApp.useWeatherHistory();
1783
+
1784
+ await weatherHistory.registerHistory({
1785
+ historyId: 'dwd-archive',
1786
+ name: 'DWD Climate Archive',
1787
+ vendor: 'Deutscher Wetterdienst',
1788
+ availableHistoryDays: 730,
1789
+ availableMeasures: [
1790
+ WeatherHistoryMeasureEnum.OutdoorTemperature,
1791
+ WeatherHistoryMeasureEnum.WindSpeed,
1792
+ WeatherHistoryMeasureEnum.GlobalHorizontalIrradiance,
1793
+ ],
1794
+ });
1795
+
1796
+ const temperatures = await weatherHistory.getOutdoorTemperature('dwd-archive', {
1797
+ fromIso: '2026-01-15T00:00:00Z',
1798
+ untilIso: '2026-01-16T00:00:00Z',
1799
+ resolution: WeatherHistoryResolutionEnum.OneHour, // optional, defaults to the provider's
1800
+ });
1801
+
1802
+ temperatures.averageCelsius; // time-weighted mean over the interval
1803
+ temperatures.minCelsius;
1804
+ temperatures.maxCelsius;
1805
+ temperatures.readings; // [{ timestampIso, outdoorTemperatureCelsius }, …]
1806
+ ```
1807
+
1808
+ The interval is half-open — `fromIso` included, `untilIso` excluded — so consecutive intervals line
1809
+ up without double-counting the boundary. A `resolution` finer than the provider's is filled by
1810
+ linear interpolation, a coarser one aggregated as a time-weighted average.
1811
+
1812
+ Reaching further back than the archive goes is **not** an error: you get the covered part plus
1813
+ `coveredFromIso` / `coveredUntilIso` telling you where the data actually began and ended, and an
1814
+ interval the provider holds nothing for returns empty `readings` with no aggregates.
1815
+
1816
+ No location is passed: the registered provider resolves the device's own location through
1817
+ `useLocation()`, so a consumer only states the interval it wants.
1818
+
1819
+ For anything beyond temperature — irradiance, wind, cloud cover — use `getWeatherHistory`, which
1820
+ returns every requested measure per bucket:
1821
+
1822
+ ```typescript
1823
+ const history = await weatherHistory.getWeatherHistory('open-meteo-archive', {
1824
+ fromIso: '2026-01-15T00:00:00Z',
1825
+ untilIso: '2026-01-16T00:00:00Z',
1826
+ resolution: WeatherHistoryResolutionEnum.OneHour,
1827
+ measures: [ // optional, defaults to everything held
1828
+ WeatherHistoryMeasureEnum.GlobalHorizontalIrradiance,
1829
+ WeatherHistoryMeasureEnum.WindSpeed,
1830
+ WeatherHistoryMeasureEnum.CloudArea,
1831
+ ],
1832
+ });
1833
+
1834
+ history.measures; // what actually came back
1835
+ history.readings; // [{ timestampIso, globalHorizontalIrradiance, windSpeedMs, … }, …]
1836
+ history.statistics?.[WeatherHistoryMeasureEnum.WindSpeed]?.averageValue;
1837
+ ```
1838
+
1839
+ Field names and units mirror `WeatherForecastEntry` one to one — `outdoorTemperatureCelsius`,
1840
+ `windSpeedMs`, `cloudAreaPercent` (0–100), `symbol`, and `globalHorizontalIrradiance` /
1841
+ `directNormalIrradiance` / `diffuseHorizontalIrradiance` in W/m² — so an observed series and a
1842
+ forecast series concatenate into one timeline without translation. Every measure on a reading is
1843
+ optional: you get what the provider holds and you asked for, and a measure it does not hold is simply
1844
+ absent rather than an error.
1845
+
1846
+ Aggregates per measure live in `statistics`, keyed by `WeatherHistoryMeasureEnum`. `averageValue` is
1847
+ time-weighted; for the irradiance measures it is a mean power density in W/m², so multiply by the
1848
+ covered duration in hours to get received energy in Wh/m². `symbol` is categorical and therefore
1849
+ never appears in `statistics`.
1850
+
1851
+ Publishers need `WeatherHistoryRegister`; consumers need `WeatherHistoryUse`.
1852
+
1772
1853
  #### `usePvForecasting(): EnergyAppPvForecasting`
1773
1854
 
1774
1855
  Same shape as weather forecasting, but for PV production.
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.EnergyAppPackageFirmwareModeEnum = exports.EnergyAppPackageCategory = void 0;
3
+ exports.EnergyAppPackageFirmwareModeEnum = exports.EnergyAppPackageCompatibilityStatus = exports.EnergyAppPackageCategory = void 0;
4
4
  exports.defineEnergyAppPackage = defineEnergyAppPackage;
5
5
  const version_js_1 = require("./version.cjs");
6
6
  var EnergyAppPackageCategory;
@@ -27,6 +27,24 @@ var EnergyAppPackageCategory;
27
27
  EnergyAppPackageCategory["Vehicle"] = "vehicle";
28
28
  EnergyAppPackageCategory["Other"] = "other";
29
29
  })(EnergyAppPackageCategory || (exports.EnergyAppPackageCategory = EnergyAppPackageCategory = {}));
30
+ /**
31
+ * Whether a declared compatibility entry means "this works" or "this is known
32
+ * not to work".
33
+ *
34
+ * Used by {@link EnergyAppPackageCompatibilityVendor.status} and
35
+ * {@link EnergyAppPackageCompatibilityModel.status} so a package can
36
+ * list a vendor or model it has explicitly tested and found unsupported,
37
+ * instead of silently leaving it out. The enyo Store and onboarding flows can
38
+ * then tell the user "this device is not supported by this app" rather than
39
+ * showing nothing at all.
40
+ */
41
+ var EnergyAppPackageCompatibilityStatus;
42
+ (function (EnergyAppPackageCompatibilityStatus) {
43
+ /** The package supports this vendor or model. */
44
+ EnergyAppPackageCompatibilityStatus["Compatible"] = "compatible";
45
+ /** The package has been verified **not** to work with this vendor or model. */
46
+ EnergyAppPackageCompatibilityStatus["NotCompatible"] = "not-compatible";
47
+ })(EnergyAppPackageCompatibilityStatus || (exports.EnergyAppPackageCompatibilityStatus = EnergyAppPackageCompatibilityStatus = {}));
30
48
  /**
31
49
  * Enum form of {@link EnergyAppPackageFirmwareMode} for use in package
32
50
  * definitions.
@@ -213,6 +213,23 @@ export interface EnergyAppPackagePermission {
213
213
  /** Internal documentation describing what this permission is used for */
214
214
  internalComment: string;
215
215
  }
216
+ /**
217
+ * Whether a declared compatibility entry means "this works" or "this is known
218
+ * not to work".
219
+ *
220
+ * Used by {@link EnergyAppPackageCompatibilityVendor.status} and
221
+ * {@link EnergyAppPackageCompatibilityModel.status} so a package can
222
+ * list a vendor or model it has explicitly tested and found unsupported,
223
+ * instead of silently leaving it out. The enyo Store and onboarding flows can
224
+ * then tell the user "this device is not supported by this app" rather than
225
+ * showing nothing at all.
226
+ */
227
+ export declare enum EnergyAppPackageCompatibilityStatus {
228
+ /** The package supports this vendor or model. */
229
+ Compatible = "compatible",
230
+ /** The package has been verified **not** to work with this vendor or model. */
231
+ NotCompatible = "not-compatible"
232
+ }
216
233
  /**
217
234
  * A specific device model supported by an Energy App package.
218
235
  * the concrete models the package has been verified to work with.
@@ -249,6 +266,16 @@ export interface EnergyAppPackageCompatibilityModel {
249
266
  category?: EnergyAppPackageCategory;
250
267
  /** Optional internal note explaining model-specific caveats or limitations */
251
268
  internalComment?: string;
269
+ /**
270
+ * Whether this model is supported or explicitly unsupported.
271
+ *
272
+ * Defaults to {@link EnergyAppPackageCompatibilityStatus.Compatible} when
273
+ * omitted, so existing definitions keep their meaning. Set it to
274
+ * {@link EnergyAppPackageCompatibilityStatus.NotCompatible} to document a
275
+ * model you have tested and found not to work — use
276
+ * {@link internalComment} to say why.
277
+ */
278
+ status?: EnergyAppPackageCompatibilityStatus;
252
279
  /**
253
280
  * Important capabilities this specific model supports (e.g. charging the
254
281
  * battery from grid, limiting the charge, or forcing a heat pump DHW boost).
@@ -277,6 +304,16 @@ export interface EnergyAppPackageCompatibilityVendor {
277
304
  * worse than listing none.
278
305
  */
279
306
  models: EnergyAppPackageCompatibilityModel[];
307
+ /**
308
+ * Whether this vendor is supported or explicitly unsupported.
309
+ *
310
+ * Defaults to {@link EnergyAppPackageCompatibilityStatus.Compatible} when
311
+ * omitted. Set it to
312
+ * {@link EnergyAppPackageCompatibilityStatus.NotCompatible} to declare a
313
+ * brand the package is known not to work with; individual models may still
314
+ * override it with their own {@link EnergyAppPackageCompatibilityModel.status}.
315
+ */
316
+ status?: EnergyAppPackageCompatibilityStatus;
280
317
  /**
281
318
  * Marks this package as the default Energy App for the vendor when no
282
319
  * concrete model has been selected.
@@ -37,6 +37,8 @@ var EnergyAppPermissionTypeEnum;
37
37
  EnergyAppPermissionTypeEnum["EnergyPrices"] = "EnergyPrices";
38
38
  EnergyAppPermissionTypeEnum["WeatherForecastRegister"] = "WeatherForecastRegister";
39
39
  EnergyAppPermissionTypeEnum["WeatherForecastUse"] = "WeatherForecastUse";
40
+ EnergyAppPermissionTypeEnum["WeatherHistoryRegister"] = "WeatherHistoryRegister";
41
+ EnergyAppPermissionTypeEnum["WeatherHistoryUse"] = "WeatherHistoryUse";
40
42
  EnergyAppPermissionTypeEnum["PvForecastRegister"] = "PvForecastRegister";
41
43
  EnergyAppPermissionTypeEnum["PvForecastUse"] = "PvForecastUse";
42
44
  EnergyAppPermissionTypeEnum["DynamicPriceForecastRegister"] = "DynamicPriceForecastRegister";
@@ -33,6 +33,8 @@ export declare enum EnergyAppPermissionTypeEnum {
33
33
  EnergyPrices = "EnergyPrices",
34
34
  WeatherForecastRegister = "WeatherForecastRegister",
35
35
  WeatherForecastUse = "WeatherForecastUse",
36
+ WeatherHistoryRegister = "WeatherHistoryRegister",
37
+ WeatherHistoryUse = "WeatherHistoryUse",
36
38
  PvForecastRegister = "PvForecastRegister",
37
39
  PvForecastUse = "PvForecastUse",
38
40
  DynamicPriceForecastRegister = "DynamicPriceForecastRegister",
@@ -188,6 +188,15 @@ class EnergyApp {
188
188
  useWeatherForecasting() {
189
189
  return this.energyAppSdk.useWeatherForecasting();
190
190
  }
191
+ /**
192
+ * Gets the Weather History API for managing weather history providers and retrieving observed weather data.
193
+ * Provides methods to register/deregister weather history providers, list available providers,
194
+ * and fetch the outdoor temperature for a past time interval by zip code or coordinates.
195
+ * @returns The Weather History API instance
196
+ */
197
+ useWeatherHistory() {
198
+ return this.energyAppSdk.useWeatherHistory();
199
+ }
191
200
  /**
192
201
  * Gets the PV Forecasting API for managing PV forecast providers and retrieving PV forecasts.
193
202
  * Provides methods to register/deregister PV forecast providers and fetch power production forecasts.
@@ -23,6 +23,7 @@ import { EnyoEnergyAppEnvironment } from "./enyo-energy-app-environment.cjs";
23
23
  import { EnergyAppEnergyManager } from "./packages/energy-app-energy-manager.cjs";
24
24
  import { EnergyAppElectricityTariff } from "./packages/energy-app-electricity-tariff.cjs";
25
25
  import { EnergyAppWeatherForecasting } from "./packages/energy-app-weather-forecasting.cjs";
26
+ import { EnergyAppWeatherHistory } from "./packages/energy-app-weather-history.cjs";
26
27
  import { EnergyAppPvForecasting } from "./packages/energy-app-pv-forecasting.cjs";
27
28
  import { EnergyAppDynamicPriceForecast } from "./packages/energy-app-dynamic-price-forecast.cjs";
28
29
  import { EnergyAppPvSystem } from "./packages/energy-app-pv-system.cjs";
@@ -161,6 +162,13 @@ export declare class EnergyApp implements EnyoEnergyAppSdk {
161
162
  * @returns The Weather Forecasting API instance
162
163
  */
163
164
  useWeatherForecasting(): EnergyAppWeatherForecasting;
165
+ /**
166
+ * Gets the Weather History API for managing weather history providers and retrieving observed weather data.
167
+ * Provides methods to register/deregister weather history providers, list available providers,
168
+ * and fetch the outdoor temperature for a past time interval by zip code or coordinates.
169
+ * @returns The Weather History API instance
170
+ */
171
+ useWeatherHistory(): EnergyAppWeatherHistory;
164
172
  /**
165
173
  * Gets the PV Forecasting API for managing PV forecast providers and retrieving PV forecasts.
166
174
  * Provides methods to register/deregister PV forecast providers and fetch power production forecasts.
@@ -22,6 +22,7 @@ import { EnyoEnergyAppEnvironment } from "./enyo-energy-app-environment.cjs";
22
22
  import { EnergyAppEnergyManager } from "./packages/energy-app-energy-manager.cjs";
23
23
  import { EnergyAppElectricityTariff } from "./packages/energy-app-electricity-tariff.cjs";
24
24
  import { EnergyAppWeatherForecasting } from "./packages/energy-app-weather-forecasting.cjs";
25
+ import { EnergyAppWeatherHistory } from "./packages/energy-app-weather-history.cjs";
25
26
  import { EnergyAppPvForecasting } from "./packages/energy-app-pv-forecasting.cjs";
26
27
  import { EnergyAppDynamicPriceForecast } from "./packages/energy-app-dynamic-price-forecast.cjs";
27
28
  import { EnergyAppPvSystem } from "./packages/energy-app-pv-system.cjs";
@@ -126,6 +127,8 @@ export interface EnyoEnergyAppSdk {
126
127
  useElectricityTariff: () => EnergyAppElectricityTariff;
127
128
  /** Get the Weather Forecasting API for managing weather forecast providers and retrieving weather forecasts */
128
129
  useWeatherForecasting: () => EnergyAppWeatherForecasting;
130
+ /** Get the Weather History API for managing weather history providers and retrieving observed weather for a past interval */
131
+ useWeatherHistory: () => EnergyAppWeatherHistory;
129
132
  /** Get the PV Forecasting API for managing PV forecast providers and retrieving PV forecasts */
130
133
  usePvForecasting: () => EnergyAppPvForecasting;
131
134
  /** Get the Dynamic Price Forecast API for publishing and consuming forward-looking electricity price forecasts */
@@ -24,6 +24,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
24
24
  exports.FirmwareRegistryValidationError = void 0;
25
25
  exports.validateFirmwareRegistry = validateFirmwareRegistry;
26
26
  exports.assertValidFirmwareRegistry = assertValidFirmwareRegistry;
27
+ const energy_app_package_definition_js_1 = require("../../energy-app-package-definition.cjs");
27
28
  const energy_app_permission_type_js_1 = require("../../energy-app-permission.type.cjs");
28
29
  const define_firmware_file_js_1 = require("./define-firmware-file.cjs");
29
30
  /**
@@ -269,11 +270,12 @@ function findDanglingSources(files) {
269
270
  }
270
271
  /**
271
272
  * Reports vendors and models referenced by firmware entries that the package
272
- * does not declare in its `compatibility` list.
273
+ * does not declare in its `compatibility` list, or that it declares as
274
+ * {@link EnergyAppPackageCompatibilityStatus.NotCompatible}.
273
275
  *
274
276
  * @param files - All declared firmware entries.
275
277
  * @param compatibility - The package's declared vendors and models.
276
- * @returns One warning per unknown vendor or model.
278
+ * @returns One warning per unknown or explicitly incompatible vendor or model.
277
279
  */
278
280
  function findUnknownCompatibility(files, compatibility) {
279
281
  const warnings = [];
@@ -281,15 +283,27 @@ function findUnknownCompatibility(files, compatibility) {
281
283
  return warnings;
282
284
  const vendors = new Set(compatibility.map(vendor => vendor.vendorName));
283
285
  const models = new Set(compatibility.flatMap(vendor => vendor.models.map(model => model.modelName)));
286
+ const incompatibleVendors = new Set(compatibility
287
+ .filter(vendor => vendor.status === energy_app_package_definition_js_1.EnergyAppPackageCompatibilityStatus.NotCompatible)
288
+ .map(vendor => vendor.vendorName));
289
+ const incompatibleModels = new Set(compatibility.flatMap(vendor => vendor.models
290
+ .filter(model => model.status === energy_app_package_definition_js_1.EnergyAppPackageCompatibilityStatus.NotCompatible)
291
+ .map(model => model.modelName)));
284
292
  for (const [index, file] of files.entries()) {
285
293
  const at = describe(file, index);
286
294
  if (file.vendorName && !vendors.has(file.vendorName)) {
287
295
  warnings.push(`${at}: vendorName "${file.vendorName}" is not listed in compatibility.`);
288
296
  }
297
+ else if (file.vendorName && incompatibleVendors.has(file.vendorName)) {
298
+ warnings.push(`${at}: vendorName "${file.vendorName}" is declared as not-compatible.`);
299
+ }
289
300
  for (const model of file.modelNames ?? []) {
290
301
  if (!models.has(model)) {
291
302
  warnings.push(`${at}: modelName "${model}" is not listed in compatibility.`);
292
303
  }
304
+ else if (incompatibleModels.has(model)) {
305
+ warnings.push(`${at}: modelName "${model}" is declared as not-compatible.`);
306
+ }
293
307
  }
294
308
  }
295
309
  return warnings;
@@ -49,6 +49,8 @@ __exportStar(require("./packages/energy-app-electricity-tariff.cjs"), exports);
49
49
  __exportStar(require("./types/enyo-pv-forecast.cjs"), exports);
50
50
  __exportStar(require("./types/enyo-forecasting.cjs"), exports);
51
51
  __exportStar(require("./packages/energy-app-weather-forecasting.cjs"), exports);
52
+ __exportStar(require("./types/enyo-weather-history.cjs"), exports);
53
+ __exportStar(require("./packages/energy-app-weather-history.cjs"), exports);
52
54
  __exportStar(require("./packages/energy-app-pv-forecasting.cjs"), exports);
53
55
  __exportStar(require("./packages/energy-app-dynamic-price-forecast.cjs"), exports);
54
56
  __exportStar(require("./types/enyo-pv-system.cjs"), exports);
@@ -33,6 +33,8 @@ export * from './packages/energy-app-electricity-tariff.cjs';
33
33
  export * from './types/enyo-pv-forecast.cjs';
34
34
  export * from './types/enyo-forecasting.cjs';
35
35
  export * from './packages/energy-app-weather-forecasting.cjs';
36
+ export * from './types/enyo-weather-history.cjs';
37
+ export * from './packages/energy-app-weather-history.cjs';
36
38
  export * from './packages/energy-app-pv-forecasting.cjs';
37
39
  export * from './packages/energy-app-dynamic-price-forecast.cjs';
38
40
  export * from './types/enyo-pv-system.cjs';
@@ -28,6 +28,21 @@ export interface ModbusRtuOptions {
28
28
  */
29
29
  waitBetweenMessagesMs?: number;
30
30
  }
31
+ /**
32
+ * Which register bank an RTU read addresses, and therefore which Modbus
33
+ * function code goes on the wire.
34
+ *
35
+ * Holding and input registers are two separate address spaces: a device may
36
+ * answer register 30001 as an input register and hold something entirely
37
+ * different — or nothing at all — at the same address in the holding bank.
38
+ * Reading the wrong bank yields an illegal-data-address exception, not a
39
+ * timeout, so this is a correctness knob rather than a recovery one.
40
+ */
41
+ export type ModbusRtuRegisterType =
42
+ /** Holding registers, read with function code 3 (`mbpoll -t 4`). */
43
+ 'holding'
44
+ /** Input registers, read with function code 4 (`mbpoll -t 3`). */
45
+ | 'input';
31
46
  /**
32
47
  * Request parameters for reading Modbus RTU registers.
33
48
  */
@@ -38,6 +53,14 @@ export interface ModbusRtuReadRegistersRequest {
38
53
  startRegister: number;
39
54
  /** Number of consecutive registers to read */
40
55
  count: number;
56
+ /**
57
+ * Which register bank to read from.
58
+ *
59
+ * `'holding'` issues function code 3 and `'input'` issues function code 4.
60
+ * Defaults to `'holding'` when omitted, which is what every existing caller
61
+ * gets today.
62
+ */
63
+ registerType?: ModbusRtuRegisterType;
41
64
  }
42
65
  /**
43
66
  * Response containing register values read from a Modbus RTU device.
@@ -63,9 +86,15 @@ export interface ModbusRtuWriteRegistersRequest {
63
86
  * Provides methods for reading and writing registers over a serial connection.
64
87
  */
65
88
  export interface EnergyAppModbusRtuInstance {
66
- /** Read register values from the connected Modbus RTU device */
89
+ /**
90
+ * Read register values from the connected Modbus RTU device.
91
+ *
92
+ * Reads holding registers (function code 3) unless the request sets
93
+ * {@link ModbusRtuReadRegistersRequest.registerType} to `'input'`, which
94
+ * reads input registers with function code 4 instead.
95
+ */
67
96
  readRegisters: (request: ModbusRtuReadRegistersRequest) => Promise<ModbusRtuReadRegistersResponse>;
68
- /** Write register values to the connected Modbus RTU device */
97
+ /** Write register values to the connected Modbus RTU device (holding registers only) */
69
98
  writeRegisters: (request: ModbusRtuWriteRegistersRequest) => Promise<void>;
70
99
  }
71
100
  /**
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,138 @@
1
+ import { WeatherHistoryRegistration, WeatherHistoryInfo, OutdoorTemperatureHistoryRequest, OutdoorTemperatureHistory, WeatherHistoryRequest, WeatherHistory } from "../types/enyo-weather-history.cjs";
2
+ /**
3
+ * Interface for managing weather history providers and retrieving observed
4
+ * weather data for a past time interval.
5
+ *
6
+ * This is the backward-looking counterpart to `EnergyAppWeatherForecasting`:
7
+ * where the forecasting API answers "what will the weather do", this one
8
+ * answers "what was the weather between these two timestamps" — temperature,
9
+ * wind, cloud cover and solar irradiance. That is the input an app needs to
10
+ * correlate measured consumption with the weather that drove it, to build
11
+ * heating-degree-day statistics, to check measured PV production against the
12
+ * irradiance that was actually available, or to train a consumption model on
13
+ * outdoor conditions.
14
+ *
15
+ * Two queries are offered: {@link EnergyAppWeatherHistory.getWeatherHistory}
16
+ * returns every requested measure per bucket, while
17
+ * {@link EnergyAppWeatherHistory.getOutdoorTemperature} is the shorthand for
18
+ * the common temperature-only case. Neither takes a location — the registered
19
+ * provider resolves the device's own location via `energyApp.useLocation()`,
20
+ * so a consumer only ever states the interval it cares about.
21
+ *
22
+ * Like the forecasting API, data comes from registered providers; an app
23
+ * either registers one (wrapping an external archive) or consumes one that is
24
+ * already registered, or both.
25
+ */
26
+ export interface EnergyAppWeatherHistory {
27
+ /**
28
+ * Registers a new weather history provider or updates an existing one.
29
+ * Uses upsert logic based on historyId - if a provider with the same ID
30
+ * exists, it will be updated; otherwise, a new provider will be created.
31
+ *
32
+ * @param registration - The history provider registration data
33
+ * @returns Promise that resolves when the provider has been registered
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * await weatherHistory.registerHistory({
38
+ * historyId: 'dwd-archive',
39
+ * name: 'DWD Climate Archive',
40
+ * vendor: 'Deutscher Wetterdienst',
41
+ * availableHistoryDays: 730,
42
+ * availableMeasures: [WeatherHistoryMeasureEnum.OutdoorTemperature]
43
+ * });
44
+ * ```
45
+ */
46
+ registerHistory(registration: WeatherHistoryRegistration): Promise<void>;
47
+ /**
48
+ * Removes a registered weather history provider by its ID.
49
+ * If the provider does not exist, this operation is a no-op.
50
+ *
51
+ * @param historyId - The unique identifier of the history provider to remove
52
+ * @returns Promise that resolves when the provider has been removed
53
+ */
54
+ deregisterHistory(historyId: string): Promise<void>;
55
+ /**
56
+ * Retrieves all registered weather history providers.
57
+ *
58
+ * @returns Promise that resolves to an array of all registered weather history providers
59
+ *
60
+ * @example
61
+ * ```typescript
62
+ * const providers = await weatherHistory.listHistories();
63
+ * providers.forEach(p => console.log(`${p.name} (${p.vendor})`));
64
+ * ```
65
+ */
66
+ listHistories(): Promise<WeatherHistoryInfo[]>;
67
+ /**
68
+ * Fetches the observed outdoor temperature for a time interval.
69
+ *
70
+ * The interval is half-open: `fromIso` is included and `untilIso` is not,
71
+ * so consecutive intervals can be requested back to back without the
72
+ * boundary timestamp being counted twice. Pass `resolution` to get a bucket
73
+ * size other than the provider's own — a finer resolution is filled by
74
+ * linear interpolation, a coarser one aggregated as a time-weighted
75
+ * average.
76
+ *
77
+ * An interval that reaches further back than the provider's archive, or
78
+ * forward past its most recent observation, is not an error: the covered
79
+ * part is returned and `coveredFromIso` / `coveredUntilIso` state where the
80
+ * data actually began and ended. An interval the provider holds no data for
81
+ * at all yields an empty `readings` array.
82
+ *
83
+ * @param historyId - The unique identifier of the history provider to use
84
+ * @param request - The requested interval and resolution
85
+ * @returns Promise that resolves to the observed temperatures for the interval
86
+ *
87
+ * @example
88
+ * ```typescript
89
+ * const temperatures = await weatherHistory.getOutdoorTemperature('dwd-archive', {
90
+ * fromIso: '2026-01-15T00:00:00Z',
91
+ * untilIso: '2026-01-16T00:00:00Z',
92
+ * resolution: WeatherHistoryResolutionEnum.OneHour
93
+ * });
94
+ * console.log(`Average: ${temperatures.averageCelsius}°C`);
95
+ * temperatures.readings.forEach(r =>
96
+ * console.log(`${r.timestampIso}: ${r.outdoorTemperatureCelsius}°C`)
97
+ * );
98
+ * ```
99
+ */
100
+ getOutdoorTemperature(historyId: string, request: OutdoorTemperatureHistoryRequest): Promise<OutdoorTemperatureHistory>;
101
+ /**
102
+ * Fetches the observed weather for a time interval across every requested
103
+ * measure — temperature, wind speed, cloud cover and the three irradiance
104
+ * components.
105
+ *
106
+ * Interval, resolution and coverage behave exactly as described on
107
+ * {@link EnergyAppWeatherHistory.getOutdoorTemperature}. Narrow the result
108
+ * with `measures` when only some quantities are needed — archives are
109
+ * typically billed or rate-limited per quantity. Measures the provider does
110
+ * not hold are simply absent from the readings rather than reported as an
111
+ * error; `measures` on the result states what actually came back.
112
+ *
113
+ * @param historyId - The unique identifier of the history provider to use
114
+ * @param request - The requested interval, resolution and measures
115
+ * @returns Promise that resolves to the observed weather for the interval
116
+ *
117
+ * @example
118
+ * ```typescript
119
+ * const history = await weatherHistory.getWeatherHistory('open-meteo-archive', {
120
+ * fromIso: '2026-01-15T00:00:00Z',
121
+ * untilIso: '2026-01-16T00:00:00Z',
122
+ * resolution: WeatherHistoryResolutionEnum.OneHour,
123
+ * measures: [
124
+ * WeatherHistoryMeasureEnum.GlobalHorizontalIrradiance,
125
+ * WeatherHistoryMeasureEnum.WindSpeed,
126
+ * WeatherHistoryMeasureEnum.CloudArea
127
+ * ]
128
+ * });
129
+ *
130
+ * const ghi = history.statistics?.[WeatherHistoryMeasureEnum.GlobalHorizontalIrradiance];
131
+ * console.log(`Mean irradiance: ${ghi?.averageValue} W/m²`);
132
+ * history.readings.forEach(r =>
133
+ * console.log(`${r.timestampIso}: ${r.globalHorizontalIrradiance} W/m², ${r.windSpeedMs} m/s`)
134
+ * );
135
+ * ```
136
+ */
137
+ getWeatherHistory(historyId: string, request: WeatherHistoryRequest): Promise<WeatherHistory>;
138
+ }
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WeatherHistoryMeasureEnum = exports.WeatherHistoryResolutionEnum = void 0;
4
+ /**
5
+ * Resolution of historical weather data entries.
6
+ *
7
+ * The values mirror {@link ForecastResolutionEnum} so a history series and a
8
+ * forecast series of the same resolution can be concatenated without
9
+ * translating between the two vocabularies.
10
+ */
11
+ var WeatherHistoryResolutionEnum;
12
+ (function (WeatherHistoryResolutionEnum) {
13
+ /** 1-minute intervals */
14
+ WeatherHistoryResolutionEnum["OneMinute"] = "1min";
15
+ /** 15-minute intervals */
16
+ WeatherHistoryResolutionEnum["FifteenMinutes"] = "15min";
17
+ /** 1-hour intervals */
18
+ WeatherHistoryResolutionEnum["OneHour"] = "1hr";
19
+ })(WeatherHistoryResolutionEnum || (exports.WeatherHistoryResolutionEnum = WeatherHistoryResolutionEnum = {}));
20
+ /**
21
+ * A weather quantity a history provider can serve.
22
+ *
23
+ * Used to narrow a request to the measures an app actually needs — archives
24
+ * are usually billed or rate-limited per quantity, so asking for irradiance
25
+ * when only wind is wanted is pure cost.
26
+ */
27
+ var WeatherHistoryMeasureEnum;
28
+ (function (WeatherHistoryMeasureEnum) {
29
+ /** Outdoor air temperature in degrees Celsius */
30
+ WeatherHistoryMeasureEnum["OutdoorTemperature"] = "outdoor-temperature";
31
+ /** Wind speed in meters per second */
32
+ WeatherHistoryMeasureEnum["WindSpeed"] = "wind-speed";
33
+ /** Cloud coverage area in percent */
34
+ WeatherHistoryMeasureEnum["CloudArea"] = "cloud-area";
35
+ /** Global horizontal irradiance in W/m² */
36
+ WeatherHistoryMeasureEnum["GlobalHorizontalIrradiance"] = "global-horizontal-irradiance";
37
+ /** Direct normal irradiance in W/m² */
38
+ WeatherHistoryMeasureEnum["DirectNormalIrradiance"] = "direct-normal-irradiance";
39
+ /** Diffuse horizontal irradiance in W/m² */
40
+ WeatherHistoryMeasureEnum["DiffuseHorizontalIrradiance"] = "diffuse-horizontal-irradiance";
41
+ /** Categorical weather symbol */
42
+ WeatherHistoryMeasureEnum["Symbol"] = "symbol";
43
+ })(WeatherHistoryMeasureEnum || (exports.WeatherHistoryMeasureEnum = WeatherHistoryMeasureEnum = {}));