@enyo-energy/energy-app-sdk 0.0.187 → 0.0.189

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 (53) hide show
  1. package/dist/cjs/energy-app-permission.type.cjs +1 -0
  2. package/dist/cjs/energy-app-permission.type.d.cts +1 -0
  3. package/dist/cjs/energy-app.cjs +16 -0
  4. package/dist/cjs/energy-app.d.cts +15 -0
  5. package/dist/cjs/enyo-energy-app-sdk.d.cts +3 -0
  6. package/dist/cjs/implementations/modbus/define-modbus-server-register.cjs +85 -0
  7. package/dist/cjs/implementations/modbus/define-modbus-server-register.d.cts +71 -0
  8. package/dist/cjs/implementations/modbus/modbus-server-validators.cjs +161 -0
  9. package/dist/cjs/implementations/modbus/modbus-server-validators.d.cts +73 -0
  10. package/dist/cjs/implementations/onboarding-v2/define-onboarding-guide-v2.cjs +45 -0
  11. package/dist/cjs/implementations/onboarding-v2/define-onboarding-guide-v2.d.cts +36 -1
  12. package/dist/cjs/implementations/onboarding-v2/onboarding-v2-validators.cjs +115 -3
  13. package/dist/cjs/index.cjs +4 -0
  14. package/dist/cjs/index.d.cts +4 -0
  15. package/dist/cjs/packages/energy-app-modbus-server.cjs +14 -0
  16. package/dist/cjs/packages/energy-app-modbus-server.d.cts +122 -0
  17. package/dist/cjs/types/enyo-air-conditioning-appliance.cjs +6 -0
  18. package/dist/cjs/types/enyo-air-conditioning-appliance.d.cts +7 -1
  19. package/dist/cjs/types/enyo-heating-rod-appliance.cjs +10 -0
  20. package/dist/cjs/types/enyo-heating-rod-appliance.d.cts +11 -1
  21. package/dist/cjs/types/enyo-modbus-server.cjs +149 -0
  22. package/dist/cjs/types/enyo-modbus-server.d.cts +346 -0
  23. package/dist/cjs/types/enyo-onboarding-v2.cjs +17 -2
  24. package/dist/cjs/types/enyo-onboarding-v2.d.cts +142 -6
  25. package/dist/cjs/version.cjs +1 -1
  26. package/dist/cjs/version.d.cts +1 -1
  27. package/dist/energy-app-permission.type.d.ts +1 -0
  28. package/dist/energy-app-permission.type.js +1 -0
  29. package/dist/energy-app.d.ts +15 -0
  30. package/dist/energy-app.js +16 -0
  31. package/dist/enyo-energy-app-sdk.d.ts +3 -0
  32. package/dist/implementations/modbus/define-modbus-server-register.d.ts +71 -0
  33. package/dist/implementations/modbus/define-modbus-server-register.js +82 -0
  34. package/dist/implementations/modbus/modbus-server-validators.d.ts +73 -0
  35. package/dist/implementations/modbus/modbus-server-validators.js +154 -0
  36. package/dist/implementations/onboarding-v2/define-onboarding-guide-v2.d.ts +36 -1
  37. package/dist/implementations/onboarding-v2/define-onboarding-guide-v2.js +45 -0
  38. package/dist/implementations/onboarding-v2/onboarding-v2-validators.js +116 -4
  39. package/dist/index.d.ts +4 -0
  40. package/dist/index.js +4 -0
  41. package/dist/packages/energy-app-modbus-server.d.ts +122 -0
  42. package/dist/packages/energy-app-modbus-server.js +13 -0
  43. package/dist/types/enyo-air-conditioning-appliance.d.ts +7 -1
  44. package/dist/types/enyo-air-conditioning-appliance.js +6 -0
  45. package/dist/types/enyo-heating-rod-appliance.d.ts +11 -1
  46. package/dist/types/enyo-heating-rod-appliance.js +10 -0
  47. package/dist/types/enyo-modbus-server.d.ts +346 -0
  48. package/dist/types/enyo-modbus-server.js +144 -0
  49. package/dist/types/enyo-onboarding-v2.d.ts +142 -6
  50. package/dist/types/enyo-onboarding-v2.js +16 -1
  51. package/dist/version.d.ts +1 -1
  52. package/dist/version.js +1 -1
  53. package/package.json +1 -1
@@ -27,6 +27,7 @@ var EnergyAppPermissionTypeEnum;
27
27
  EnergyAppPermissionTypeEnum["EnergyManager"] = "EnergyManager";
28
28
  EnergyAppPermissionTypeEnum["ElectricityTariff"] = "ElectricityTariff";
29
29
  EnergyAppPermissionTypeEnum["ModbusRtu"] = "ModbusRtu";
30
+ EnergyAppPermissionTypeEnum["ModbusServer"] = "ModbusServer";
30
31
  EnergyAppPermissionTypeEnum["EnergyPrices"] = "EnergyPrices";
31
32
  EnergyAppPermissionTypeEnum["WeatherForecastRegister"] = "WeatherForecastRegister";
32
33
  EnergyAppPermissionTypeEnum["WeatherForecastUse"] = "WeatherForecastUse";
@@ -24,6 +24,7 @@ export declare enum EnergyAppPermissionTypeEnum {
24
24
  EnergyManager = "EnergyManager",
25
25
  ElectricityTariff = "ElectricityTariff",
26
26
  ModbusRtu = "ModbusRtu",
27
+ ModbusServer = "ModbusServer",
27
28
  EnergyPrices = "EnergyPrices",
28
29
  WeatherForecastRegister = "WeatherForecastRegister",
29
30
  WeatherForecastUse = "WeatherForecastUse",
@@ -212,6 +212,22 @@ class EnergyApp {
212
212
  useModbusRtu() {
213
213
  return this.energyAppSdk.useModbusRtu();
214
214
  }
215
+ /**
216
+ * Gets the Modbus server API for serving your app's data to Modbus clients.
217
+ *
218
+ * The inverse of {@link EnergyApp.useModbus}: the hub itself answers Modbus
219
+ * requests, and this app supplies the values behind individual registers by
220
+ * registering them with read/write handlers. The listener is host-owned and
221
+ * shared with every other installed app, so address ranges are
222
+ * first-come-first-served across apps.
223
+ *
224
+ * Requires the `ModbusServer` permission.
225
+ *
226
+ * @returns The Modbus server API instance
227
+ */
228
+ useModbusServer() {
229
+ return this.energyAppSdk.useModbusServer();
230
+ }
215
231
  /**
216
232
  * Gets the EEbus API for SHIP/SPINE device communication.
217
233
  *
@@ -26,6 +26,7 @@ import { EnergyAppDynamicPriceForecast } from "./packages/energy-app-dynamic-pri
26
26
  import { EnergyAppPvSystem } from "./packages/energy-app-pv-system.cjs";
27
27
  import { EnergyAppSequenceGenerator } from "./packages/energy-app-sequence-generator.cjs";
28
28
  import { EnergyAppModbusRtu } from "./packages/energy-app-modbus-rtu.cjs";
29
+ import { EnergyAppModbusServer } from "./packages/energy-app-modbus-server.cjs";
29
30
  import { EnergyAppEebus } from "./packages/energy-app-eebus.cjs";
30
31
  import { EnergyAppMqtt } from "./packages/energy-app-mqtt.cjs";
31
32
  import { EnergyAppBluetooth } from "./packages/energy-app-bluetooth.cjs";
@@ -171,6 +172,20 @@ export declare class EnergyApp implements EnyoEnergyAppSdk {
171
172
  * @returns The Modbus RTU API instance
172
173
  */
173
174
  useModbusRtu(): EnergyAppModbusRtu;
175
+ /**
176
+ * Gets the Modbus server API for serving your app's data to Modbus clients.
177
+ *
178
+ * The inverse of {@link EnergyApp.useModbus}: the hub itself answers Modbus
179
+ * requests, and this app supplies the values behind individual registers by
180
+ * registering them with read/write handlers. The listener is host-owned and
181
+ * shared with every other installed app, so address ranges are
182
+ * first-come-first-served across apps.
183
+ *
184
+ * Requires the `ModbusServer` permission.
185
+ *
186
+ * @returns The Modbus server API instance
187
+ */
188
+ useModbusServer(): EnergyAppModbusServer;
174
189
  /**
175
190
  * Gets the EEbus API for SHIP/SPINE device communication.
176
191
  *
@@ -25,6 +25,7 @@ import { EnergyAppDynamicPriceForecast } from "./packages/energy-app-dynamic-pri
25
25
  import { EnergyAppPvSystem } from "./packages/energy-app-pv-system.cjs";
26
26
  import { EnergyAppSequenceGenerator } from "./packages/energy-app-sequence-generator.cjs";
27
27
  import { EnergyAppModbusRtu } from "./packages/energy-app-modbus-rtu.cjs";
28
+ import { EnergyAppModbusServer } from "./packages/energy-app-modbus-server.cjs";
28
29
  import { EnergyAppEebus } from "./packages/energy-app-eebus.cjs";
29
30
  import { EnergyAppMqtt } from "./packages/energy-app-mqtt.cjs";
30
31
  import { EnergyAppBluetooth } from "./packages/energy-app-bluetooth.cjs";
@@ -123,6 +124,8 @@ export interface EnyoEnergyAppSdk {
123
124
  useSequenceGenerator: () => EnergyAppSequenceGenerator;
124
125
  /** Get the Modbus RTU serial communication API */
125
126
  useModbusRtu: () => EnergyAppModbusRtu;
127
+ /** Get the Modbus server API for serving registers to Modbus clients */
128
+ useModbusServer: () => EnergyAppModbusServer;
126
129
  /** Get the EEbus SHIP/SPINE communication API for device pairing, data access, and power management */
127
130
  useEebus: () => EnergyAppEebus;
128
131
  /** Get the MQTT communication API for connecting to internal or external MQTT brokers */
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ /**
3
+ * Ergonomic authoring helpers for {@link EnyoModbusServerRegistration}s.
4
+ *
5
+ * These exist for one concrete reason: type inference. A registration's handler
6
+ * signatures depend on its `dataType` — a `float32` register reads a `number`, a
7
+ * `string` register reads a `string` — but that link only holds when TypeScript
8
+ * can *infer* the data type from the literal. Annotating an object literal as
9
+ * {@link EnyoModbusServerRegistration} defeats that: the generic falls back to
10
+ * the full data-type union and every handler widens to `string | number`.
11
+ *
12
+ * Building registers through these factories keeps the link intact, so a handler
13
+ * returning the wrong shape is a compile error rather than a runtime decode
14
+ * failure. They also fill in `space`, which is the one field that is pure
15
+ * boilerplate.
16
+ *
17
+ * Mirrors the SDK's `onboardingV2Block` pattern.
18
+ */
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.modbusServerRegister = void 0;
21
+ const enyo_modbus_server_js_1 = require("../../types/enyo-modbus-server.cjs");
22
+ /**
23
+ * Typed factories for each Modbus address space. Each fills in `space` and
24
+ * returns the corresponding registration, inferring the word data type from the
25
+ * supplied `dataType` so handler values stay exact.
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * const registers = [
30
+ * modbusServerRegister.holding({
31
+ * address: 40071,
32
+ * key: 'active-power-w',
33
+ * name: [{language: 'en', value: 'Active power'}],
34
+ * unit: 'W',
35
+ * dataType: 'float32',
36
+ * onRead: () => currentPowerW, // must be a number
37
+ * onWrite: (value) => applyLimit(value),
38
+ * }),
39
+ * modbusServerRegister.input({
40
+ * address: 40080,
41
+ * key: 'serial',
42
+ * name: [{language: 'en', value: 'Serial number'}],
43
+ * dataType: 'string',
44
+ * quantity: 8,
45
+ * onRead: () => deviceSerial, // must be a string
46
+ * }),
47
+ * ];
48
+ * ```
49
+ */
50
+ exports.modbusServerRegister = {
51
+ /**
52
+ * A read/write single-bit coil.
53
+ * @param registration - Everything but `space`; omit `onWrite` for a read-only coil.
54
+ */
55
+ coil: (registration) => ({
56
+ ...registration,
57
+ space: enyo_modbus_server_js_1.EnyoModbusServerRegisterSpace.Coil,
58
+ }),
59
+ /**
60
+ * A read-only single-bit discrete input.
61
+ * @param registration - Everything but `space`.
62
+ */
63
+ discreteInput: (registration) => ({
64
+ ...registration,
65
+ space: enyo_modbus_server_js_1.EnyoModbusServerRegisterSpace.DiscreteInput,
66
+ }),
67
+ /**
68
+ * A read/write 16-bit holding register.
69
+ * @param registration - Everything but `space`. The `dataType` fixes the
70
+ * handler value types; omit `onWrite` for a read-only holding register.
71
+ */
72
+ holding: (registration) => ({
73
+ ...registration,
74
+ space: enyo_modbus_server_js_1.EnyoModbusServerRegisterSpace.Holding,
75
+ }),
76
+ /**
77
+ * A read-only 16-bit input register.
78
+ * @param registration - Everything but `space`. The `dataType` fixes the
79
+ * handler value type.
80
+ */
81
+ input: (registration) => ({
82
+ ...registration,
83
+ space: enyo_modbus_server_js_1.EnyoModbusServerRegisterSpace.Input,
84
+ }),
85
+ };
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Ergonomic authoring helpers for {@link EnyoModbusServerRegistration}s.
3
+ *
4
+ * These exist for one concrete reason: type inference. A registration's handler
5
+ * signatures depend on its `dataType` — a `float32` register reads a `number`, a
6
+ * `string` register reads a `string` — but that link only holds when TypeScript
7
+ * can *infer* the data type from the literal. Annotating an object literal as
8
+ * {@link EnyoModbusServerRegistration} defeats that: the generic falls back to
9
+ * the full data-type union and every handler widens to `string | number`.
10
+ *
11
+ * Building registers through these factories keeps the link intact, so a handler
12
+ * returning the wrong shape is a compile error rather than a runtime decode
13
+ * failure. They also fill in `space`, which is the one field that is pure
14
+ * boilerplate.
15
+ *
16
+ * Mirrors the SDK's `onboardingV2Block` pattern.
17
+ */
18
+ import { type EnyoModbusServerCoilRegistration, type EnyoModbusServerDiscreteInputRegistration, type EnyoModbusServerHoldingRegistration, type EnyoModbusServerInputRegistration } from '../../types/enyo-modbus-server.cjs';
19
+ import type { EnergyAppModbusDataType } from './interfaces.cjs';
20
+ /**
21
+ * Typed factories for each Modbus address space. Each fills in `space` and
22
+ * returns the corresponding registration, inferring the word data type from the
23
+ * supplied `dataType` so handler values stay exact.
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * const registers = [
28
+ * modbusServerRegister.holding({
29
+ * address: 40071,
30
+ * key: 'active-power-w',
31
+ * name: [{language: 'en', value: 'Active power'}],
32
+ * unit: 'W',
33
+ * dataType: 'float32',
34
+ * onRead: () => currentPowerW, // must be a number
35
+ * onWrite: (value) => applyLimit(value),
36
+ * }),
37
+ * modbusServerRegister.input({
38
+ * address: 40080,
39
+ * key: 'serial',
40
+ * name: [{language: 'en', value: 'Serial number'}],
41
+ * dataType: 'string',
42
+ * quantity: 8,
43
+ * onRead: () => deviceSerial, // must be a string
44
+ * }),
45
+ * ];
46
+ * ```
47
+ */
48
+ export declare const modbusServerRegister: {
49
+ /**
50
+ * A read/write single-bit coil.
51
+ * @param registration - Everything but `space`; omit `onWrite` for a read-only coil.
52
+ */
53
+ coil: (registration: Omit<EnyoModbusServerCoilRegistration, "space">) => EnyoModbusServerCoilRegistration;
54
+ /**
55
+ * A read-only single-bit discrete input.
56
+ * @param registration - Everything but `space`.
57
+ */
58
+ discreteInput: (registration: Omit<EnyoModbusServerDiscreteInputRegistration, "space">) => EnyoModbusServerDiscreteInputRegistration;
59
+ /**
60
+ * A read/write 16-bit holding register.
61
+ * @param registration - Everything but `space`. The `dataType` fixes the
62
+ * handler value types; omit `onWrite` for a read-only holding register.
63
+ */
64
+ holding: <D extends EnergyAppModbusDataType>(registration: Omit<EnyoModbusServerHoldingRegistration<D>, "space">) => EnyoModbusServerHoldingRegistration<D>;
65
+ /**
66
+ * A read-only 16-bit input register.
67
+ * @param registration - Everything but `space`. The `dataType` fixes the
68
+ * handler value type.
69
+ */
70
+ input: <D extends EnergyAppModbusDataType>(registration: Omit<EnyoModbusServerInputRegistration<D>, "space">) => EnyoModbusServerInputRegistration<D>;
71
+ };
@@ -0,0 +1,161 @@
1
+ "use strict";
2
+ /**
3
+ * Client-side validator for {@link EnyoModbusServerRegistration} lists.
4
+ *
5
+ * The host rejects a conflicting registration at call time by throwing
6
+ * {@link EnyoModbusServerRegistrationConflictError}, which is the authoritative
7
+ * check but a poor way to find out your own register map overlaps itself. This
8
+ * validator catches everything that is knowable without the host — self-overlap,
9
+ * duplicate keys, missing metadata — so an app can fail fast at startup with all
10
+ * the problems at once instead of one throw at a time.
11
+ *
12
+ * It cannot see other apps' registrations, so a clean result here does **not**
13
+ * guarantee registration will succeed.
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.ModbusServerValidationError = void 0;
17
+ exports.modbusServerRegisterSpan = modbusServerRegisterSpan;
18
+ exports.validateModbusServerRegistrations = validateModbusServerRegistrations;
19
+ exports.assertValidModbusServerRegistrations = assertValidModbusServerRegistrations;
20
+ const enyo_modbus_server_js_1 = require("../../types/enyo-modbus-server.cjs");
21
+ /**
22
+ * Thrown by {@link assertValidModbusServerRegistrations} when a register map
23
+ * fails validation. The message lists every blocking error.
24
+ */
25
+ class ModbusServerValidationError extends Error {
26
+ /** The individual blocking errors that caused the failure. */
27
+ errors;
28
+ /**
29
+ * @param errors - The blocking validation errors.
30
+ */
31
+ constructor(errors) {
32
+ super(`Invalid Modbus server register map:\n- ${errors.join('\n- ')}`);
33
+ this.name = 'ModbusServerValidationError';
34
+ this.errors = errors;
35
+ }
36
+ }
37
+ exports.ModbusServerValidationError = ModbusServerValidationError;
38
+ /** Word counts for the data types whose span is fixed. */
39
+ const WORDS_BY_DATA_TYPE = {
40
+ uint16: 1,
41
+ int16: 1,
42
+ acc16: 1,
43
+ uint32: 2,
44
+ int32: 2,
45
+ float32: 2,
46
+ acc32: 2,
47
+ };
48
+ /** The two spaces addressed in bits rather than 16-bit words. */
49
+ const BIT_SPACES = new Set([
50
+ enyo_modbus_server_js_1.EnyoModbusServerRegisterSpace.Coil,
51
+ enyo_modbus_server_js_1.EnyoModbusServerRegisterSpace.DiscreteInput,
52
+ ]);
53
+ /**
54
+ * How many words (or bits) a registration occupies, starting at its `address`.
55
+ *
56
+ * Bit spaces always occupy one. Word spaces derive their span from `dataType`,
57
+ * except `'string'`, which needs an explicit `quantity` — when that is missing
58
+ * the span is unknowable and this returns `undefined` so the caller can report
59
+ * it rather than guess.
60
+ *
61
+ * @param registration - The registration to measure.
62
+ * @returns The span, or `undefined` when it cannot be derived.
63
+ */
64
+ function modbusServerRegisterSpan(registration) {
65
+ if (BIT_SPACES.has(registration.space))
66
+ return 1;
67
+ const { dataType, quantity } = registration;
68
+ if (dataType === 'string') {
69
+ return quantity && quantity > 0 ? quantity : undefined;
70
+ }
71
+ return WORDS_BY_DATA_TYPE[dataType];
72
+ }
73
+ /**
74
+ * Checks a register map for problems that are knowable without the host.
75
+ *
76
+ * Reports as errors: overlapping ranges within the same space, duplicate `key`s,
77
+ * a missing or empty `name`, a `'string'` register without `quantity`, an
78
+ * unknown `dataType`, and a negative address. Reports as warnings: `scale` on a
79
+ * string register (which the host ignores) and a `unit` on a coil or discrete
80
+ * input (a bit has no unit).
81
+ *
82
+ * Two registrations only conflict within the same space — holding 40071 and
83
+ * input 40071 are different addresses and both are fine.
84
+ *
85
+ * @param registrations - The register map to check.
86
+ * @returns The {@link ModbusServerValidationResult}.
87
+ */
88
+ function validateModbusServerRegistrations(registrations) {
89
+ const errors = [];
90
+ const warnings = [];
91
+ if (!registrations?.length) {
92
+ return { ok: true, errors, warnings };
93
+ }
94
+ const keys = new Set();
95
+ /** Claimed ranges per space, so the overlap check never crosses spaces. */
96
+ const claimed = new Map();
97
+ for (const [i, registration] of registrations.entries()) {
98
+ const at = `registrations[${i}] (${registration.key || '?'})`;
99
+ if (!registration.key?.trim()) {
100
+ errors.push(`${at}: key is required.`);
101
+ }
102
+ else if (keys.has(registration.key)) {
103
+ errors.push(`${at}: duplicate key "${registration.key}".`);
104
+ }
105
+ else {
106
+ keys.add(registration.key);
107
+ }
108
+ if (!registration.name?.length) {
109
+ errors.push(`${at}: name is required — it is what an installer sees in the register map.`);
110
+ }
111
+ if (!Number.isInteger(registration.address) || registration.address < 0) {
112
+ errors.push(`${at}: address must be a non-negative integer.`);
113
+ continue;
114
+ }
115
+ if (BIT_SPACES.has(registration.space) && registration.unit) {
116
+ warnings.push(`${at}: unit "${registration.unit}" on a ${registration.space} is meaningless — a bit has no unit.`);
117
+ }
118
+ const span = modbusServerRegisterSpan(registration);
119
+ if (span === undefined) {
120
+ const { dataType } = registration;
121
+ if (dataType === 'string') {
122
+ errors.push(`${at}: dataType "string" needs a positive quantity — its length cannot be derived.`);
123
+ }
124
+ else {
125
+ errors.push(`${at}: unknown dataType "${dataType}".`);
126
+ }
127
+ continue;
128
+ }
129
+ if (!BIT_SPACES.has(registration.space)) {
130
+ const { dataType, scale } = registration;
131
+ if (dataType === 'string' && scale !== undefined) {
132
+ warnings.push(`${at}: scale is ignored on a string register.`);
133
+ }
134
+ }
135
+ const from = registration.address;
136
+ const to = from + span - 1;
137
+ const inSpace = claimed.get(registration.space) ?? [];
138
+ const clash = inSpace.find((r) => from <= r.to && to >= r.from);
139
+ if (clash) {
140
+ errors.push(`${at}: ${registration.space} range ${from}..${to} overlaps ${clash.at} (${clash.from}..${clash.to}).`);
141
+ }
142
+ inSpace.push({ from, to, at });
143
+ claimed.set(registration.space, inSpace);
144
+ }
145
+ return { ok: errors.length === 0, errors, warnings };
146
+ }
147
+ /**
148
+ * Like {@link validateModbusServerRegistrations}, but throws
149
+ * {@link ModbusServerValidationError} when there are blocking errors. Warnings
150
+ * never throw; the validated list is returned on success for chaining.
151
+ *
152
+ * @param registrations - The register map to check.
153
+ * @returns The same list when it has no blocking errors.
154
+ * @throws {ModbusServerValidationError} When validation produces any error.
155
+ */
156
+ function assertValidModbusServerRegistrations(registrations) {
157
+ const { ok, errors } = validateModbusServerRegistrations(registrations);
158
+ if (!ok)
159
+ throw new ModbusServerValidationError(errors);
160
+ return registrations;
161
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Client-side validator for {@link EnyoModbusServerRegistration} lists.
3
+ *
4
+ * The host rejects a conflicting registration at call time by throwing
5
+ * {@link EnyoModbusServerRegistrationConflictError}, which is the authoritative
6
+ * check but a poor way to find out your own register map overlaps itself. This
7
+ * validator catches everything that is knowable without the host — self-overlap,
8
+ * duplicate keys, missing metadata — so an app can fail fast at startup with all
9
+ * the problems at once instead of one throw at a time.
10
+ *
11
+ * It cannot see other apps' registrations, so a clean result here does **not**
12
+ * guarantee registration will succeed.
13
+ */
14
+ import { type EnyoModbusServerRegistration } from '../../types/enyo-modbus-server.cjs';
15
+ /** The outcome of validating a list of registrations. */
16
+ export interface ModbusServerValidationResult {
17
+ /** True when there are no blocking `errors` (warnings are still allowed). */
18
+ ok: boolean;
19
+ /** Blocking problems — registration will fail or serve wrong data. */
20
+ errors: string[];
21
+ /** Advisory problems — allowed, but usually worth fixing. */
22
+ warnings: string[];
23
+ }
24
+ /**
25
+ * Thrown by {@link assertValidModbusServerRegistrations} when a register map
26
+ * fails validation. The message lists every blocking error.
27
+ */
28
+ export declare class ModbusServerValidationError extends Error {
29
+ /** The individual blocking errors that caused the failure. */
30
+ readonly errors: string[];
31
+ /**
32
+ * @param errors - The blocking validation errors.
33
+ */
34
+ constructor(errors: string[]);
35
+ }
36
+ /**
37
+ * How many words (or bits) a registration occupies, starting at its `address`.
38
+ *
39
+ * Bit spaces always occupy one. Word spaces derive their span from `dataType`,
40
+ * except `'string'`, which needs an explicit `quantity` — when that is missing
41
+ * the span is unknowable and this returns `undefined` so the caller can report
42
+ * it rather than guess.
43
+ *
44
+ * @param registration - The registration to measure.
45
+ * @returns The span, or `undefined` when it cannot be derived.
46
+ */
47
+ export declare function modbusServerRegisterSpan(registration: EnyoModbusServerRegistration): number | undefined;
48
+ /**
49
+ * Checks a register map for problems that are knowable without the host.
50
+ *
51
+ * Reports as errors: overlapping ranges within the same space, duplicate `key`s,
52
+ * a missing or empty `name`, a `'string'` register without `quantity`, an
53
+ * unknown `dataType`, and a negative address. Reports as warnings: `scale` on a
54
+ * string register (which the host ignores) and a `unit` on a coil or discrete
55
+ * input (a bit has no unit).
56
+ *
57
+ * Two registrations only conflict within the same space — holding 40071 and
58
+ * input 40071 are different addresses and both are fine.
59
+ *
60
+ * @param registrations - The register map to check.
61
+ * @returns The {@link ModbusServerValidationResult}.
62
+ */
63
+ export declare function validateModbusServerRegistrations(registrations: EnyoModbusServerRegistration[]): ModbusServerValidationResult;
64
+ /**
65
+ * Like {@link validateModbusServerRegistrations}, but throws
66
+ * {@link ModbusServerValidationError} when there are blocking errors. Warnings
67
+ * never throw; the validated list is returned on success for chaining.
68
+ *
69
+ * @param registrations - The register map to check.
70
+ * @returns The same list when it has no blocking errors.
71
+ * @throws {ModbusServerValidationError} When validation produces any error.
72
+ */
73
+ export declare function assertValidModbusServerRegistrations(registrations: EnyoModbusServerRegistration[]): EnyoModbusServerRegistration[];
@@ -133,6 +133,51 @@ exports.onboardingV2Block = {
133
133
  outcomes,
134
134
  deviceSelection,
135
135
  }),
136
+ /**
137
+ * A link block: a fixed URL the installer opens or copies.
138
+ *
139
+ * Passive content — it produces no routing handle, so a step whose only
140
+ * non-content block is a link still routes through `continue`.
141
+ *
142
+ * @param id - Stable block id, unique within the guide.
143
+ * @param url - Absolute `http(s)` URL. Other schemes are rejected by the validator.
144
+ * @param label - Translated link text (de/en).
145
+ * @param opts - Optional translated `description` and `copyable` (defaults to `true`).
146
+ */
147
+ link: (id, url, label, opts) => ({
148
+ id,
149
+ type: enyo_onboarding_v2_js_1.EnyoOnboardingV2BlockType.Link,
150
+ url,
151
+ label,
152
+ description: opts?.description,
153
+ copyable: opts?.copyable ?? true,
154
+ }),
155
+ /**
156
+ * An input block: the installer types a value and the host checks it,
157
+ * producing the branch.
158
+ *
159
+ * For {@link EnyoOnboardingV2InputValueType.IpAddress} the host runs this
160
+ * app's registered device-test handler against the typed address; see
161
+ * {@link EnyoOnboardingV2InputBlock} for how a verdict picks an outcome.
162
+ * Route the outcomes with {@link onOutcomeV2} — there is no separate helper.
163
+ *
164
+ * @param id - Stable block id, unique within the guide.
165
+ * @param valueType - What is asked for (`Text` | `IpAddress` | `Number`).
166
+ * @param label - Translated field label (de/en).
167
+ * @param submitLabel - Translated submit button text (de/en).
168
+ * @param outcomes - The possible verdicts; each is a routing handle.
169
+ * @param opts - Optional translated `placeholder` and `help`.
170
+ */
171
+ input: (id, valueType, label, submitLabel, outcomes, opts) => ({
172
+ id,
173
+ type: enyo_onboarding_v2_js_1.EnyoOnboardingV2BlockType.Input,
174
+ valueType,
175
+ label,
176
+ submitLabel,
177
+ outcomes,
178
+ placeholder: opts?.placeholder,
179
+ help: opts?.help,
180
+ }),
136
181
  };
137
182
  // ---------------------------------------------------------------------------
138
183
  // Target factories
@@ -11,7 +11,7 @@
11
11
  */
12
12
  import type { EnyoOnboardingTranslatedContent } from '../../types/enyo-onboarding.cjs';
13
13
  import { EnyoOnboardingV2ActionKind, EnyoOnboardingV2ChoiceLayout, EnyoOnboardingV2DeviceSelection } from '../../types/enyo-onboarding-v2.cjs';
14
- import type { EnyoOnboardingV2ActionOutcome, EnyoOnboardingV2Block, EnyoOnboardingV2ChoiceOption, EnyoOnboardingV2DynamicKind, EnyoOnboardingV2Guide, EnyoOnboardingV2HintVariant, EnyoOnboardingV2PauseReason, EnyoOnboardingV2StartVariant, EnyoOnboardingV2Target, EnyoOnboardingV2Transition } from '../../types/enyo-onboarding-v2.cjs';
14
+ import type { EnyoOnboardingV2ActionOutcome, EnyoOnboardingV2Block, EnyoOnboardingV2ChoiceOption, EnyoOnboardingV2DynamicKind, EnyoOnboardingV2Guide, EnyoOnboardingV2HintVariant, EnyoOnboardingV2InputOutcome, EnyoOnboardingV2InputValueType, EnyoOnboardingV2PauseReason, EnyoOnboardingV2StartVariant, EnyoOnboardingV2Target, EnyoOnboardingV2Transition } from '../../types/enyo-onboarding-v2.cjs';
15
15
  /**
16
16
  * Identity helper that type-checks a v2 guide literal at definition time
17
17
  * (mirrors `defineEnergyAppPackage`). Prefer this over a bare object literal so
@@ -98,6 +98,41 @@ export declare const onboardingV2Block: {
98
98
  * @param deviceSelection - Which devices to test (defaults to `Detected`).
99
99
  */
100
100
  deviceTest: (id: string, label: EnyoOnboardingTranslatedContent[], outcomes: EnyoOnboardingV2ActionOutcome[], deviceSelection?: EnyoOnboardingV2DeviceSelection) => EnyoOnboardingV2Block;
101
+ /**
102
+ * A link block: a fixed URL the installer opens or copies.
103
+ *
104
+ * Passive content — it produces no routing handle, so a step whose only
105
+ * non-content block is a link still routes through `continue`.
106
+ *
107
+ * @param id - Stable block id, unique within the guide.
108
+ * @param url - Absolute `http(s)` URL. Other schemes are rejected by the validator.
109
+ * @param label - Translated link text (de/en).
110
+ * @param opts - Optional translated `description` and `copyable` (defaults to `true`).
111
+ */
112
+ link: (id: string, url: string, label: EnyoOnboardingTranslatedContent[], opts?: {
113
+ description?: EnyoOnboardingTranslatedContent[];
114
+ copyable?: boolean;
115
+ }) => EnyoOnboardingV2Block;
116
+ /**
117
+ * An input block: the installer types a value and the host checks it,
118
+ * producing the branch.
119
+ *
120
+ * For {@link EnyoOnboardingV2InputValueType.IpAddress} the host runs this
121
+ * app's registered device-test handler against the typed address; see
122
+ * {@link EnyoOnboardingV2InputBlock} for how a verdict picks an outcome.
123
+ * Route the outcomes with {@link onOutcomeV2} — there is no separate helper.
124
+ *
125
+ * @param id - Stable block id, unique within the guide.
126
+ * @param valueType - What is asked for (`Text` | `IpAddress` | `Number`).
127
+ * @param label - Translated field label (de/en).
128
+ * @param submitLabel - Translated submit button text (de/en).
129
+ * @param outcomes - The possible verdicts; each is a routing handle.
130
+ * @param opts - Optional translated `placeholder` and `help`.
131
+ */
132
+ input: (id: string, valueType: EnyoOnboardingV2InputValueType, label: EnyoOnboardingTranslatedContent[], submitLabel: EnyoOnboardingTranslatedContent[], outcomes: EnyoOnboardingV2InputOutcome[], opts?: {
133
+ placeholder?: EnyoOnboardingTranslatedContent[];
134
+ help?: EnyoOnboardingTranslatedContent[];
135
+ }) => EnyoOnboardingV2Block;
101
136
  };
102
137
  /** Typed factories for each transition {@link EnyoOnboardingV2Target}. */
103
138
  export declare const onboardingV2Target: {