@enyo-energy/energy-app-sdk 0.0.131 → 0.0.132
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.
- package/README.md +163 -0
- package/dist/cjs/energy-app-permission.type.cjs +2 -0
- package/dist/cjs/energy-app-permission.type.d.cts +3 -1
- package/dist/cjs/energy-app.cjs +10 -0
- package/dist/cjs/energy-app.d.cts +9 -0
- package/dist/cjs/enyo-energy-app-sdk.d.cts +3 -0
- package/dist/cjs/implementations/network-devices/network-access-guard.cjs +239 -0
- package/dist/cjs/implementations/network-devices/network-access-guard.d.cts +165 -0
- package/dist/cjs/implementations/network-devices/network-device-manager.cjs +416 -0
- package/dist/cjs/implementations/network-devices/network-device-manager.d.cts +266 -0
- package/dist/cjs/index.cjs +3 -0
- package/dist/cjs/index.d.cts +3 -0
- package/dist/cjs/packages/energy-app-dynamic-price-forecast.cjs +2 -0
- package/dist/cjs/packages/energy-app-dynamic-price-forecast.d.cts +141 -0
- package/dist/cjs/types/enyo-battery-appliance.d.cts +7 -0
- package/dist/cjs/types/enyo-forecasting.d.cts +83 -0
- package/dist/cjs/types/enyo-inverter-appliance.d.cts +2 -0
- package/dist/cjs/version.cjs +1 -1
- package/dist/cjs/version.d.cts +1 -1
- package/dist/energy-app-permission.type.d.ts +3 -1
- package/dist/energy-app-permission.type.js +2 -0
- package/dist/energy-app.d.ts +9 -0
- package/dist/energy-app.js +10 -0
- package/dist/enyo-energy-app-sdk.d.ts +3 -0
- package/dist/implementations/network-devices/network-access-guard.d.ts +165 -0
- package/dist/implementations/network-devices/network-access-guard.js +235 -0
- package/dist/implementations/network-devices/network-device-manager.d.ts +266 -0
- package/dist/implementations/network-devices/network-device-manager.js +412 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/packages/energy-app-dynamic-price-forecast.d.ts +141 -0
- package/dist/packages/energy-app-dynamic-price-forecast.js +1 -0
- package/dist/types/enyo-battery-appliance.d.ts +7 -0
- package/dist/types/enyo-forecasting.d.ts +83 -0
- package/dist/types/enyo-inverter-appliance.d.ts +2 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,6 +23,10 @@ The official TypeScript SDK for building Energy Apps on the enyo platform. Creat
|
|
|
23
23
|
- [User Features](#user-features)
|
|
24
24
|
- [App Intelligence](#app-intelligence)
|
|
25
25
|
- [Advanced Modbus Integration](#advanced-modbus-integration)
|
|
26
|
+
- [Network Devices & Access Recovery](#network-devices--access-recovery)
|
|
27
|
+
- [NetworkAccessGuard](#networkaccessguard)
|
|
28
|
+
- [NetworkDeviceManager](#networkdevicemanager)
|
|
29
|
+
- [Startup pattern](#startup-pattern)
|
|
26
30
|
- [Device Integrations](#device-integrations)
|
|
27
31
|
- [IntegrationEnergyApp (Base Class)](#integrationenergyapp-base-class)
|
|
28
32
|
- [HeatpumpIntegrationEnergyApp](#heatpumpintegrationenergyapp)
|
|
@@ -906,6 +910,165 @@ The Modbus implementation follows a clean, modular architecture:
|
|
|
906
910
|
|
|
907
911
|
This modular design ensures maintainability, testability, and extensibility for future enhancements.
|
|
908
912
|
|
|
913
|
+
## Network Devices & Access Recovery
|
|
914
|
+
|
|
915
|
+
Packages that talk to local hardware over TCP (Modbus, SunSpec, EEBUS over SHIP, REST) must deal with two failure modes the `useNetworkDevices()` API exposes only at a low level:
|
|
916
|
+
|
|
917
|
+
1. **Network-access-denied errors** — `EnyoNetworkDevice.accessStatus` is device-wide (`granted | denied | pending`). It does **not** carry per-port grants. A device can report `'granted'` while your package never received (or has since lost) access to its Modbus port, and the first symptom is the runtime error `[NET] Network access denied: Host '...:502' is not in the allowed list.` from a poll cycle.
|
|
918
|
+
2. **User-driven access transitions** — the user revokes or re-grants access via the UI; the SDK fires `listenForDeviceAccessChange`, and packages need to disconnect / reconnect accordingly.
|
|
919
|
+
|
|
920
|
+
The SDK ships two classes that encapsulate this lifecycle so packages don't reinvent it: a low-level **`NetworkAccessGuard`** for access-denied recovery, and a higher-level **`NetworkDeviceManager`** that wires the guard together with all the network-device listeners and the package's `ApplianceManager`.
|
|
921
|
+
|
|
922
|
+
### NetworkAccessGuard
|
|
923
|
+
|
|
924
|
+
`NetworkAccessGuard` recovers from access-denied errors raised by the SDK's network layer. Construct one per package with the ports it needs and a restored-callback that reconnects whatever client was reading from the device.
|
|
925
|
+
|
|
926
|
+
```typescript
|
|
927
|
+
import { NetworkAccessGuard } from '@enyo-energy/energy-app-sdk';
|
|
928
|
+
|
|
929
|
+
const accessGuard = new NetworkAccessGuard(energyApp, {
|
|
930
|
+
ports: [502],
|
|
931
|
+
onAccessRestored: async (networkDeviceId) => {
|
|
932
|
+
await myModbusPool.reconnect(networkDeviceId);
|
|
933
|
+
},
|
|
934
|
+
});
|
|
935
|
+
|
|
936
|
+
// Precondition before a Modbus connect:
|
|
937
|
+
if (!(await accessGuard.ensureAccess(networkDevice.id))) {
|
|
938
|
+
console.warn(`Modbus port access not granted for ${networkDevice.hostname}`);
|
|
939
|
+
return;
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
// Wrap any Modbus read so an access-denied error triggers recovery:
|
|
943
|
+
const registers = await accessGuard.withAccessGuard(networkDevice.id, () =>
|
|
944
|
+
modbusClient.readHoldingRegisters(40000, 4),
|
|
945
|
+
);
|
|
946
|
+
```
|
|
947
|
+
|
|
948
|
+
Recovery lifecycle:
|
|
949
|
+
|
|
950
|
+
1. A read fails inside `withAccessGuard`. The guard detects the access-denied error via `NetworkAccessGuard.isAccessDeniedError(error)`, re-throws it to the caller (so the current poll cycle fails fast), and kicks off recovery in the background.
|
|
951
|
+
2. The guard calls `requestDeviceAccess(deviceId, ports)`. If the SDK answers `'granted'` synchronously (the port was just missing from the allow-list and no user prompt is needed), the `onAccessRestored` handler fires immediately.
|
|
952
|
+
3. Otherwise the device stays in a pending set and the `listenForDeviceAccessChange` registration fires the handler when the SDK reports the device flipped to `'granted'`.
|
|
953
|
+
|
|
954
|
+
Re-entrancy: repeated `recoverAccess(...)` calls for the same device while a recovery is already in flight are coalesced — the handler runs exactly once per restoration.
|
|
955
|
+
|
|
956
|
+
The guard exposes:
|
|
957
|
+
|
|
958
|
+
| Method | Purpose |
|
|
959
|
+
| --- | --- |
|
|
960
|
+
| `static isAccessDeniedError(error)` | Recognise the SDK's access-denied error string |
|
|
961
|
+
| `ensureAccess(deviceId)` | Idempotent port-allow-list request before a connect |
|
|
962
|
+
| `withAccessGuard(deviceId, action)` | Wrap any async TCP call — recovers on access-denied |
|
|
963
|
+
| `recoverAccess(deviceId)` | Explicit recovery trigger after catching an access-denied error |
|
|
964
|
+
| `onAccessRestored(handler)` / `onAccessDenied(handler)` | Register additional handlers at runtime; returns a disposer |
|
|
965
|
+
| `isRecovering(deviceId)` | Introspect whether a recovery is in flight |
|
|
966
|
+
| `dispose()` | Tear down the SDK listener |
|
|
967
|
+
|
|
968
|
+
### NetworkDeviceManager
|
|
969
|
+
|
|
970
|
+
`NetworkDeviceManager` is the recommended entry point for any package that owns appliances backed by NetworkDevices. It bundles a `NetworkAccessGuard` with the three NetworkDevice-related SDK listeners (`listenForDeviceAccessChange`, `listenForDetectedDevice`, `listenForNetworkDeviceRemoved`) and resolves every event into **per-appliance callbacks** by joining against the package's `ApplianceManager`.
|
|
971
|
+
|
|
972
|
+
```typescript
|
|
973
|
+
import {
|
|
974
|
+
ApplianceManager,
|
|
975
|
+
EnergyApp,
|
|
976
|
+
NetworkDeviceManager,
|
|
977
|
+
} from '@enyo-energy/energy-app-sdk';
|
|
978
|
+
|
|
979
|
+
const energyApp = new EnergyApp();
|
|
980
|
+
const applianceManager = await ApplianceManager.initialize(energyApp);
|
|
981
|
+
|
|
982
|
+
const networkManager = await NetworkDeviceManager.initialize(
|
|
983
|
+
energyApp,
|
|
984
|
+
applianceManager,
|
|
985
|
+
{
|
|
986
|
+
ports: [502],
|
|
987
|
+
autoToggleApplianceState: true,
|
|
988
|
+
onApplianceAccessRestored: async ({ appliance, networkDeviceId }) => {
|
|
989
|
+
// Re-establish a Modbus session and restart the polling loop for this appliance.
|
|
990
|
+
await myModbusPool.reconnect(networkDeviceId);
|
|
991
|
+
},
|
|
992
|
+
onApplianceAccessRevoked: async ({ appliance, networkDeviceId }) => {
|
|
993
|
+
// User revoked access in the UI — tear down the connection.
|
|
994
|
+
await myModbusPool.disconnect(networkDeviceId);
|
|
995
|
+
},
|
|
996
|
+
onApplianceAccessDenied: async ({ appliance, networkDeviceId }) => {
|
|
997
|
+
// An access-denied error was just observed at runtime — mark the
|
|
998
|
+
// appliance offline. `autoToggleApplianceState: true` already does
|
|
999
|
+
// this; the handler is here for any custom side-effects.
|
|
1000
|
+
},
|
|
1001
|
+
onApplianceNetworkDeviceRemoved: async ({ appliance, networkDeviceId }) => {
|
|
1002
|
+
await myModbusPool.disconnect(networkDeviceId);
|
|
1003
|
+
},
|
|
1004
|
+
onNetworkDeviceDetected: async (devices) => {
|
|
1005
|
+
// New device found — classify + connect.
|
|
1006
|
+
for (const device of devices) {
|
|
1007
|
+
await classifyAndConnect(device);
|
|
1008
|
+
}
|
|
1009
|
+
},
|
|
1010
|
+
onNetworkDeviceAccessChanged: async (deviceId, status) => {
|
|
1011
|
+
// Optional: raw access-status passthrough, fires even for devices
|
|
1012
|
+
// the package has no appliances on yet. Useful for first-time
|
|
1013
|
+
// onboarding where a 'granted' transition needs to drive a discovery
|
|
1014
|
+
// pass before any appliance exists.
|
|
1015
|
+
},
|
|
1016
|
+
},
|
|
1017
|
+
);
|
|
1018
|
+
|
|
1019
|
+
// Every Modbus read inside the poll loop:
|
|
1020
|
+
await networkManager.withAccessGuard(networkDeviceId, () =>
|
|
1021
|
+
modbusClient.readHoldingRegisters(40000, 4),
|
|
1022
|
+
);
|
|
1023
|
+
```
|
|
1024
|
+
|
|
1025
|
+
What the manager handles for you:
|
|
1026
|
+
|
|
1027
|
+
- **Access-denied recovery** — `withAccessGuard` / `ensureAccess` delegate to the bundled `NetworkAccessGuard`.
|
|
1028
|
+
- **User-driven transitions** — on `listenForDeviceAccessChange`, the manager dispatches `onApplianceAccessRestored` on `'granted'` and `onApplianceAccessRevoked` on `'denied'` / `'pending'`, resolving each transition into the per-appliance events your reconnect/disconnect code needs.
|
|
1029
|
+
- **Listener dedup** — the manager registers its access-change listener *before* the guard's, so a `'granted'` event during a recovery cycle dispatches only once (the manager observes `isRecovering(deviceId) === true` and skips, letting the guard's own restored callback win).
|
|
1030
|
+
- **Device removal** — on `listenForNetworkDeviceRemoved`, the manager fires `onApplianceNetworkDeviceRemoved` per affected appliance and clears its cache.
|
|
1031
|
+
- **Optional auto-state toggle** — with `autoToggleApplianceState: true`, the manager flips affected appliances to `EnyoApplianceStateEnum.Offline` on denial / revocation / removal, and back to `EnyoApplianceStateEnum.Connected` on restoration, via `applianceManager.updateApplianceState(...)`.
|
|
1032
|
+
|
|
1033
|
+
Every handler is also registerable at runtime via `manager.onApplianceAccessRestored(fn)` / `onApplianceAccessDenied(fn)` / `onApplianceAccessRevoked(fn)` / `onApplianceNetworkDeviceRemoved(fn)` / `onNetworkDeviceDetected(fn)` / `onNetworkDeviceAccessChanged(fn)`, each returning a disposer.
|
|
1034
|
+
|
|
1035
|
+
### Startup pattern
|
|
1036
|
+
|
|
1037
|
+
The SDK's `listenForDeviceAccessChange` only fires on *transitions* — devices that are already `'granted'` from a previous session won't trigger it. Recommended startup flow for a package that supports both first-onboarding and warm restarts:
|
|
1038
|
+
|
|
1039
|
+
```typescript
|
|
1040
|
+
client.register(async () => {
|
|
1041
|
+
const applianceManager = await ApplianceManager.initialize(client);
|
|
1042
|
+
const networkManager = await NetworkDeviceManager.initialize(
|
|
1043
|
+
client,
|
|
1044
|
+
applianceManager,
|
|
1045
|
+
{
|
|
1046
|
+
ports: [502],
|
|
1047
|
+
onApplianceAccessRestored: ({ networkDeviceId }) => connectDevice(networkDeviceId),
|
|
1048
|
+
onApplianceAccessRevoked: ({ networkDeviceId }) => disconnectDevice(networkDeviceId),
|
|
1049
|
+
onNetworkDeviceDetected: async (devices) => {
|
|
1050
|
+
for (const device of devices) await connectDevice(device.id);
|
|
1051
|
+
},
|
|
1052
|
+
},
|
|
1053
|
+
);
|
|
1054
|
+
|
|
1055
|
+
// Warm-restart: reconnect to devices that already have access.
|
|
1056
|
+
const granted = await client.useNetworkDevices().getDevices({ accessStatus: 'granted' });
|
|
1057
|
+
for (const device of granted) {
|
|
1058
|
+
await connectDevice(device.id);
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
client.updateEnergyAppState(EnergyAppStateEnum.Running);
|
|
1062
|
+
});
|
|
1063
|
+
|
|
1064
|
+
async function connectDevice(networkDeviceId: string) {
|
|
1065
|
+
if (!(await networkManager.ensureAccess(networkDeviceId))) return;
|
|
1066
|
+
// ...classify, open modbus client, register appliances...
|
|
1067
|
+
}
|
|
1068
|
+
```
|
|
1069
|
+
|
|
1070
|
+
This pattern matches the wiring used by real Sungrow / Fronius energy-app packages: one `NetworkDeviceManager` per package, `ensureAccess` before every connect, `withAccessGuard` around every poll, and a single `getDevices({ accessStatus: 'granted' })` pass at startup to cover the warm-restart case.
|
|
1071
|
+
|
|
909
1072
|
## Device Integrations
|
|
910
1073
|
|
|
911
1074
|
Device Integrations are the high-level building blocks for apps that **drive a real device** — a heatpump, EV wallbox, PV inverter, battery storage system, or air-conditioning unit. Each integration class hides the data-bus plumbing for its appliance type so you only implement the business logic that physically controls the device.
|
|
@@ -31,6 +31,8 @@ var EnergyAppPermissionTypeEnum;
|
|
|
31
31
|
EnergyAppPermissionTypeEnum["WeatherForecastUse"] = "WeatherForecastUse";
|
|
32
32
|
EnergyAppPermissionTypeEnum["PvForecastRegister"] = "PvForecastRegister";
|
|
33
33
|
EnergyAppPermissionTypeEnum["PvForecastUse"] = "PvForecastUse";
|
|
34
|
+
EnergyAppPermissionTypeEnum["DynamicPriceForecastRegister"] = "DynamicPriceForecastRegister";
|
|
35
|
+
EnergyAppPermissionTypeEnum["DynamicPriceForecastUse"] = "DynamicPriceForecastUse";
|
|
34
36
|
EnergyAppPermissionTypeEnum["PvSystemRegister"] = "PvSystemRegister";
|
|
35
37
|
EnergyAppPermissionTypeEnum["PvSystemUse"] = "PvSystemUse";
|
|
36
38
|
EnergyAppPermissionTypeEnum["InverterControlCommands"] = "InverterControlCommands";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type EnergyAppPermissionType = 'RestrictedInternetAccess' | 'NetworkDeviceDiscovery' | 'NetworkDeviceSearch' | 'NetworkDeviceAccess' | 'Modbus' | 'Storage' | 'Appliance' | 'AllAppliances' | 'SendDataBusValues' | 'SubscribeDataBus' | 'SendDataBusCommands' | 'OcppServer' | 'ChargingCard' | 'Vehicle' | 'Charge' | 'SecretManager' | 'LocationZipCode' | 'LocationCoordinates' | 'Timeseries' | 'EnergyManagerInfo' | 'ElectricityTariff' | 'WeatherForecastRegister' | 'WeatherForecastUse' | 'PvForecastRegister' | 'PvForecastUse' | 'PvSystemRegister' | 'PvSystemUse' | 'InverterControlCommands' | 'BatteryControlCommands' | 'ChargerControlCommands' | 'ModbusRtu' | 'EnergyPrices' | 'EnergyManager' | 'EebusDeviceManagement' | 'EebusDataAccess' | 'EebusControl' | 'Mqtt' | 'Bluetooth' | 'Wifi' | 'ChildProcess' | 'Udp';
|
|
1
|
+
export type EnergyAppPermissionType = 'RestrictedInternetAccess' | 'NetworkDeviceDiscovery' | 'NetworkDeviceSearch' | 'NetworkDeviceAccess' | 'Modbus' | 'Storage' | 'Appliance' | 'AllAppliances' | 'SendDataBusValues' | 'SubscribeDataBus' | 'SendDataBusCommands' | 'OcppServer' | 'ChargingCard' | 'Vehicle' | 'Charge' | 'SecretManager' | 'LocationZipCode' | 'LocationCoordinates' | 'Timeseries' | 'EnergyManagerInfo' | 'ElectricityTariff' | 'WeatherForecastRegister' | 'WeatherForecastUse' | 'PvForecastRegister' | 'PvForecastUse' | 'DynamicPriceForecastRegister' | 'DynamicPriceForecastUse' | 'PvSystemRegister' | 'PvSystemUse' | 'InverterControlCommands' | 'BatteryControlCommands' | 'ChargerControlCommands' | 'ModbusRtu' | 'EnergyPrices' | 'EnergyManager' | 'EebusDeviceManagement' | 'EebusDataAccess' | 'EebusControl' | 'Mqtt' | 'Bluetooth' | 'Wifi' | 'ChildProcess' | 'Udp';
|
|
2
2
|
export declare enum EnergyAppPermissionTypeEnum {
|
|
3
3
|
RestrictedInternetAccess = "RestrictedInternetAccess",
|
|
4
4
|
NetworkDeviceDiscovery = "NetworkDeviceDiscovery",
|
|
@@ -28,6 +28,8 @@ export declare enum EnergyAppPermissionTypeEnum {
|
|
|
28
28
|
WeatherForecastUse = "WeatherForecastUse",
|
|
29
29
|
PvForecastRegister = "PvForecastRegister",
|
|
30
30
|
PvForecastUse = "PvForecastUse",
|
|
31
|
+
DynamicPriceForecastRegister = "DynamicPriceForecastRegister",
|
|
32
|
+
DynamicPriceForecastUse = "DynamicPriceForecastUse",
|
|
31
33
|
PvSystemRegister = "PvSystemRegister",
|
|
32
34
|
PvSystemUse = "PvSystemUse",
|
|
33
35
|
InverterControlCommands = "InverterControlCommands",
|
package/dist/cjs/energy-app.cjs
CHANGED
|
@@ -167,6 +167,16 @@ class EnergyApp {
|
|
|
167
167
|
usePvForecasting() {
|
|
168
168
|
return this.energyAppSdk.usePvForecasting();
|
|
169
169
|
}
|
|
170
|
+
/**
|
|
171
|
+
* Gets the Dynamic Price Forecast API for publishing and consuming
|
|
172
|
+
* forward-looking electricity price forecasts (e.g. day-ahead spot
|
|
173
|
+
* prices). The data is forecast only — see
|
|
174
|
+
* {@link EnergyAppDynamicPriceForecast} for the full contract.
|
|
175
|
+
* @returns The Dynamic Price Forecast API instance
|
|
176
|
+
*/
|
|
177
|
+
useDynamicPriceForecast() {
|
|
178
|
+
return this.energyAppSdk.useDynamicPriceForecast();
|
|
179
|
+
}
|
|
170
180
|
/**
|
|
171
181
|
* Gets the PV System API for managing PV system registrations and configurations.
|
|
172
182
|
* Provides methods to register, retrieve, update, and remove PV systems
|
|
@@ -22,6 +22,7 @@ import { EnergyAppEnergyManager } from "./packages/energy-app-energy-manager.cjs
|
|
|
22
22
|
import { EnergyAppElectricityTariff } from "./packages/energy-app-electricity-tariff.cjs";
|
|
23
23
|
import { EnergyAppWeatherForecasting } from "./packages/energy-app-weather-forecasting.cjs";
|
|
24
24
|
import { EnergyAppPvForecasting } from "./packages/energy-app-pv-forecasting.cjs";
|
|
25
|
+
import { EnergyAppDynamicPriceForecast } from "./packages/energy-app-dynamic-price-forecast.cjs";
|
|
25
26
|
import { EnergyAppPvSystem } from "./packages/energy-app-pv-system.cjs";
|
|
26
27
|
import { EnergyAppSequenceGenerator } from "./packages/energy-app-sequence-generator.cjs";
|
|
27
28
|
import { EnergyAppModbusRtu } from "./packages/energy-app-modbus-rtu.cjs";
|
|
@@ -125,6 +126,14 @@ export declare class EnergyApp implements EnyoEnergyAppSdk {
|
|
|
125
126
|
* @returns The PV Forecasting API instance
|
|
126
127
|
*/
|
|
127
128
|
usePvForecasting(): EnergyAppPvForecasting;
|
|
129
|
+
/**
|
|
130
|
+
* Gets the Dynamic Price Forecast API for publishing and consuming
|
|
131
|
+
* forward-looking electricity price forecasts (e.g. day-ahead spot
|
|
132
|
+
* prices). The data is forecast only — see
|
|
133
|
+
* {@link EnergyAppDynamicPriceForecast} for the full contract.
|
|
134
|
+
* @returns The Dynamic Price Forecast API instance
|
|
135
|
+
*/
|
|
136
|
+
useDynamicPriceForecast(): EnergyAppDynamicPriceForecast;
|
|
128
137
|
/**
|
|
129
138
|
* Gets the PV System API for managing PV system registrations and configurations.
|
|
130
139
|
* Provides methods to register, retrieve, update, and remove PV systems
|
|
@@ -21,6 +21,7 @@ import { EnergyAppEnergyManager } from "./packages/energy-app-energy-manager.cjs
|
|
|
21
21
|
import { EnergyAppElectricityTariff } from "./packages/energy-app-electricity-tariff.cjs";
|
|
22
22
|
import { EnergyAppWeatherForecasting } from "./packages/energy-app-weather-forecasting.cjs";
|
|
23
23
|
import { EnergyAppPvForecasting } from "./packages/energy-app-pv-forecasting.cjs";
|
|
24
|
+
import { EnergyAppDynamicPriceForecast } from "./packages/energy-app-dynamic-price-forecast.cjs";
|
|
24
25
|
import { EnergyAppPvSystem } from "./packages/energy-app-pv-system.cjs";
|
|
25
26
|
import { EnergyAppSequenceGenerator } from "./packages/energy-app-sequence-generator.cjs";
|
|
26
27
|
import { EnergyAppModbusRtu } from "./packages/energy-app-modbus-rtu.cjs";
|
|
@@ -102,6 +103,8 @@ export interface EnyoEnergyAppSdk {
|
|
|
102
103
|
useWeatherForecasting: () => EnergyAppWeatherForecasting;
|
|
103
104
|
/** Get the PV Forecasting API for managing PV forecast providers and retrieving PV forecasts */
|
|
104
105
|
usePvForecasting: () => EnergyAppPvForecasting;
|
|
106
|
+
/** Get the Dynamic Price Forecast API for publishing and consuming forward-looking electricity price forecasts */
|
|
107
|
+
useDynamicPriceForecast: () => EnergyAppDynamicPriceForecast;
|
|
105
108
|
/** Get the PV System API for managing PV system registrations and configurations */
|
|
106
109
|
usePvSystem: () => EnergyAppPvSystem;
|
|
107
110
|
/** Get the Sequence Generator API for generating unique sequential numbers per named sequence */
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NetworkAccessGuard = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Recovers from "Network access denied" failures raised by the SDK's
|
|
6
|
+
* network layer when a Modbus (or other TCP) call hits a host whose
|
|
7
|
+
* port is not in the package's allowed list.
|
|
8
|
+
*
|
|
9
|
+
* Why this exists: `EnyoNetworkDevice.accessStatus` is device-wide
|
|
10
|
+
* (`granted | denied | pending`) — it does NOT carry per-port grants.
|
|
11
|
+
* A device can report `'granted'` while our package never received
|
|
12
|
+
* (or has since lost) access to its Modbus port, and the first
|
|
13
|
+
* symptom is a runtime `[NET] Network access denied: Host '...:502'
|
|
14
|
+
* is not in the allowed list. Allowed origins: [], Allowed network
|
|
15
|
+
* devices: []` error from a poll cycle. The guard provides one place
|
|
16
|
+
* to detect that error, re-request access, and dispatch a reconnect
|
|
17
|
+
* once access returns.
|
|
18
|
+
*
|
|
19
|
+
* Typical wiring:
|
|
20
|
+
* 1. Construct one guard per package with the ports it needs.
|
|
21
|
+
* 2. On every Modbus call site, either:
|
|
22
|
+
* - wrap the call in {@link withAccessGuard}, which recovers in
|
|
23
|
+
* the background on access-denied errors and re-throws, or
|
|
24
|
+
* - catch the error, check {@link isAccessDeniedError}, and call
|
|
25
|
+
* {@link recoverAccess} explicitly.
|
|
26
|
+
* 3. Register an `onAccessRestored` handler (via the config or
|
|
27
|
+
* {@link onAccessRestored}) that performs the reconnect.
|
|
28
|
+
*
|
|
29
|
+
* Re-entrancy: repeated {@link recoverAccess} calls for the same
|
|
30
|
+
* device while a recovery is already in flight are coalesced — the
|
|
31
|
+
* restored handlers run exactly once per restoration.
|
|
32
|
+
*
|
|
33
|
+
* Precondition use: {@link ensureAccess} exposes the same idempotent
|
|
34
|
+
* `requestDeviceAccess` call so callers can use a single API for both
|
|
35
|
+
* "before a connect, make sure we have access" and "we just lost
|
|
36
|
+
* access, recover it".
|
|
37
|
+
*/
|
|
38
|
+
class NetworkAccessGuard {
|
|
39
|
+
energyApp;
|
|
40
|
+
/** Devices we've re-requested access for and are awaiting a 'granted' signal on. */
|
|
41
|
+
pending = new Set();
|
|
42
|
+
/** Listener IDs registered against the network-devices service. */
|
|
43
|
+
listenerIds = [];
|
|
44
|
+
restoredHandlers = new Set();
|
|
45
|
+
deniedHandlers = new Set();
|
|
46
|
+
ports;
|
|
47
|
+
enableLogging;
|
|
48
|
+
disposed = false;
|
|
49
|
+
/**
|
|
50
|
+
* Recognise the SDK's network-access-denied error so callers don't
|
|
51
|
+
* have to re-implement the substring check. Matches both variants
|
|
52
|
+
* observed in production logs.
|
|
53
|
+
* @param error The error to inspect (any value is accepted)
|
|
54
|
+
* @returns `true` if the error message looks like an access-denied error
|
|
55
|
+
*/
|
|
56
|
+
static isAccessDeniedError(error) {
|
|
57
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
58
|
+
return /Network access denied|not in the allowed list/i.test(message);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Constructs a guard and immediately subscribes to the SDK's
|
|
62
|
+
* device-access-change events.
|
|
63
|
+
* @param energyApp The {@link EnergyApp} instance used to call the network-devices API
|
|
64
|
+
* @param config Required ports plus optional initial handlers and logging flag
|
|
65
|
+
*/
|
|
66
|
+
constructor(energyApp, config) {
|
|
67
|
+
this.energyApp = energyApp;
|
|
68
|
+
this.ports = config.ports;
|
|
69
|
+
this.enableLogging = config.enableLogging ?? true;
|
|
70
|
+
if (config.onAccessRestored) {
|
|
71
|
+
this.restoredHandlers.add(config.onAccessRestored);
|
|
72
|
+
}
|
|
73
|
+
if (config.onAccessDenied) {
|
|
74
|
+
this.deniedHandlers.add(config.onAccessDenied);
|
|
75
|
+
}
|
|
76
|
+
const listenerId = this.energyApp.useNetworkDevices().listenForDeviceAccessChange((deviceId, status) => this.handleAccessChange(deviceId, status));
|
|
77
|
+
this.listenerIds.push(listenerId);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Registers an additional handler to be invoked whenever access
|
|
81
|
+
* is restored for any NetworkDevice the guard is recovering.
|
|
82
|
+
* @param handler The handler to register
|
|
83
|
+
* @returns A disposer that removes the handler again
|
|
84
|
+
*/
|
|
85
|
+
onAccessRestored(handler) {
|
|
86
|
+
this.restoredHandlers.add(handler);
|
|
87
|
+
return () => this.restoredHandlers.delete(handler);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Registers an additional handler to be invoked whenever a
|
|
91
|
+
* recovery cycle starts (i.e. an access-denied error was just
|
|
92
|
+
* observed). Useful for marking dependent appliances offline.
|
|
93
|
+
* @param handler The handler to register
|
|
94
|
+
* @returns A disposer that removes the handler again
|
|
95
|
+
*/
|
|
96
|
+
onAccessDenied(handler) {
|
|
97
|
+
this.deniedHandlers.add(handler);
|
|
98
|
+
return () => this.deniedHandlers.delete(handler);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Idempotently request access to the configured ports for the
|
|
102
|
+
* given NetworkDevice. Safe to call as a precondition before any
|
|
103
|
+
* Modbus operation.
|
|
104
|
+
* @param networkDeviceId The NetworkDevice to request access on
|
|
105
|
+
* @returns `true` when the SDK reports access is granted, `false` otherwise
|
|
106
|
+
*/
|
|
107
|
+
async ensureAccess(networkDeviceId) {
|
|
108
|
+
try {
|
|
109
|
+
const result = await this.energyApp
|
|
110
|
+
.useNetworkDevices()
|
|
111
|
+
.requestDeviceAccess(networkDeviceId, this.ports);
|
|
112
|
+
return result.status === 'granted';
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
if (this.enableLogging) {
|
|
116
|
+
console.warn(`[NetworkAccessGuard] requestDeviceAccess for ${networkDeviceId} failed: ${String(error)}`);
|
|
117
|
+
}
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Wraps an async action so that an access-denied error
|
|
123
|
+
* automatically triggers a background recovery. The original
|
|
124
|
+
* error is re-thrown so the caller can fail-fast on the failed
|
|
125
|
+
* read; the next read after access is restored will succeed.
|
|
126
|
+
*
|
|
127
|
+
* Use this on Modbus call sites for the simplest integration:
|
|
128
|
+
* ```ts
|
|
129
|
+
* await guard.withAccessGuard(networkDeviceId, () =>
|
|
130
|
+
* modbusClient.readHoldingRegisters(...));
|
|
131
|
+
* ```
|
|
132
|
+
* @param networkDeviceId The NetworkDevice the action targets
|
|
133
|
+
* @param action The async operation to perform
|
|
134
|
+
* @returns The resolved value of `action`
|
|
135
|
+
*/
|
|
136
|
+
async withAccessGuard(networkDeviceId, action) {
|
|
137
|
+
try {
|
|
138
|
+
return await action();
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
if (NetworkAccessGuard.isAccessDeniedError(error)) {
|
|
142
|
+
void this.recoverAccess(networkDeviceId);
|
|
143
|
+
}
|
|
144
|
+
throw error;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Drive recovery from an access-denied error: notify denied
|
|
149
|
+
* handlers, re-request access, and fire the restored handlers
|
|
150
|
+
* once access is granted. The restored handlers run at most once
|
|
151
|
+
* per recovery, regardless of whether the grant is observed
|
|
152
|
+
* synchronously (from the request response) or asynchronously
|
|
153
|
+
* (from the access-change listener).
|
|
154
|
+
* @param networkDeviceId The NetworkDevice whose access needs to be recovered
|
|
155
|
+
*/
|
|
156
|
+
async recoverAccess(networkDeviceId) {
|
|
157
|
+
if (this.disposed)
|
|
158
|
+
return;
|
|
159
|
+
if (this.pending.has(networkDeviceId)) {
|
|
160
|
+
if (this.enableLogging) {
|
|
161
|
+
console.debug(`[NetworkAccessGuard] recovery already in flight for ${networkDeviceId}`);
|
|
162
|
+
}
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
this.pending.add(networkDeviceId);
|
|
166
|
+
if (this.enableLogging) {
|
|
167
|
+
console.log(`[NetworkAccessGuard] re-requesting access for ${networkDeviceId} after access-denied`);
|
|
168
|
+
}
|
|
169
|
+
await this.fireDenied(networkDeviceId);
|
|
170
|
+
const granted = await this.ensureAccess(networkDeviceId);
|
|
171
|
+
if (!granted) {
|
|
172
|
+
// Leave the device in `pending` so the listener can fire the
|
|
173
|
+
// handlers when the user eventually accepts the request.
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
// Synchronous grant — the listener won't observe a transition,
|
|
177
|
+
// so fire the handlers ourselves and clear the pending mark.
|
|
178
|
+
this.pending.delete(networkDeviceId);
|
|
179
|
+
await this.fireRestored(networkDeviceId);
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Returns `true` if a recovery cycle is currently in flight for
|
|
183
|
+
* the given NetworkDevice. Mainly useful for tests and
|
|
184
|
+
* introspection.
|
|
185
|
+
*/
|
|
186
|
+
isRecovering(networkDeviceId) {
|
|
187
|
+
return this.pending.has(networkDeviceId);
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Tears down the SDK listener and clears all handlers. After
|
|
191
|
+
* calling this the guard should no longer be used.
|
|
192
|
+
*/
|
|
193
|
+
dispose() {
|
|
194
|
+
if (this.disposed)
|
|
195
|
+
return;
|
|
196
|
+
this.disposed = true;
|
|
197
|
+
const service = this.energyApp.useNetworkDevices();
|
|
198
|
+
for (const id of this.listenerIds) {
|
|
199
|
+
service.removeListener(id);
|
|
200
|
+
}
|
|
201
|
+
this.listenerIds.length = 0;
|
|
202
|
+
this.restoredHandlers.clear();
|
|
203
|
+
this.deniedHandlers.clear();
|
|
204
|
+
this.pending.clear();
|
|
205
|
+
}
|
|
206
|
+
async handleAccessChange(networkDeviceId, status) {
|
|
207
|
+
if (status !== 'granted')
|
|
208
|
+
return;
|
|
209
|
+
if (!this.pending.has(networkDeviceId))
|
|
210
|
+
return;
|
|
211
|
+
this.pending.delete(networkDeviceId);
|
|
212
|
+
await this.fireRestored(networkDeviceId);
|
|
213
|
+
}
|
|
214
|
+
async fireRestored(networkDeviceId) {
|
|
215
|
+
for (const handler of this.restoredHandlers) {
|
|
216
|
+
try {
|
|
217
|
+
await handler(networkDeviceId);
|
|
218
|
+
}
|
|
219
|
+
catch (error) {
|
|
220
|
+
if (this.enableLogging) {
|
|
221
|
+
console.warn(`[NetworkAccessGuard] access-restored handler for ${networkDeviceId} failed: ${String(error)}`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
async fireDenied(networkDeviceId) {
|
|
227
|
+
for (const handler of this.deniedHandlers) {
|
|
228
|
+
try {
|
|
229
|
+
await handler(networkDeviceId);
|
|
230
|
+
}
|
|
231
|
+
catch (error) {
|
|
232
|
+
if (this.enableLogging) {
|
|
233
|
+
console.warn(`[NetworkAccessGuard] access-denied handler for ${networkDeviceId} failed: ${String(error)}`);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
exports.NetworkAccessGuard = NetworkAccessGuard;
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import type { EnergyApp } from "../../energy-app.cjs";
|
|
2
|
+
/**
|
|
3
|
+
* Callback fired when access to a NetworkDevice's required ports
|
|
4
|
+
* becomes granted — either synchronously in response to a re-request
|
|
5
|
+
* or asynchronously via the SDK's `listenForDeviceAccessChange`
|
|
6
|
+
* listener.
|
|
7
|
+
*/
|
|
8
|
+
export type AccessRestoredHandler = (networkDeviceId: string) => void | Promise<void>;
|
|
9
|
+
/**
|
|
10
|
+
* Callback fired when {@link NetworkAccessGuard.recoverAccess} is
|
|
11
|
+
* invoked because the caller has just observed an access-denied
|
|
12
|
+
* error. The handler runs at most once per recovery cycle, before
|
|
13
|
+
* the guard re-requests access.
|
|
14
|
+
*/
|
|
15
|
+
export type AccessDeniedHandler = (networkDeviceId: string) => void | Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Construction options for {@link NetworkAccessGuard}.
|
|
18
|
+
*/
|
|
19
|
+
export interface NetworkAccessGuardConfig {
|
|
20
|
+
/**
|
|
21
|
+
* TCP ports the package requires on every target NetworkDevice.
|
|
22
|
+
* Forwarded to `requestDeviceAccess(deviceId, ports)` whenever the
|
|
23
|
+
* guard needs to (re-)negotiate access.
|
|
24
|
+
*/
|
|
25
|
+
ports: number[];
|
|
26
|
+
/**
|
|
27
|
+
* Optional handler invoked when access is restored. Multiple
|
|
28
|
+
* handlers can be registered after construction via
|
|
29
|
+
* {@link NetworkAccessGuard.onAccessRestored}.
|
|
30
|
+
*/
|
|
31
|
+
onAccessRestored?: AccessRestoredHandler;
|
|
32
|
+
/**
|
|
33
|
+
* Optional handler invoked the moment {@link NetworkAccessGuard.recoverAccess}
|
|
34
|
+
* is triggered — i.e. when a caller has just observed an
|
|
35
|
+
* access-denied error. Lets consumers mark dependent appliances
|
|
36
|
+
* offline before recovery completes.
|
|
37
|
+
*/
|
|
38
|
+
onAccessDenied?: AccessDeniedHandler;
|
|
39
|
+
/** Toggle internal logging. Defaults to `true`. */
|
|
40
|
+
enableLogging?: boolean;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Recovers from "Network access denied" failures raised by the SDK's
|
|
44
|
+
* network layer when a Modbus (or other TCP) call hits a host whose
|
|
45
|
+
* port is not in the package's allowed list.
|
|
46
|
+
*
|
|
47
|
+
* Why this exists: `EnyoNetworkDevice.accessStatus` is device-wide
|
|
48
|
+
* (`granted | denied | pending`) — it does NOT carry per-port grants.
|
|
49
|
+
* A device can report `'granted'` while our package never received
|
|
50
|
+
* (or has since lost) access to its Modbus port, and the first
|
|
51
|
+
* symptom is a runtime `[NET] Network access denied: Host '...:502'
|
|
52
|
+
* is not in the allowed list. Allowed origins: [], Allowed network
|
|
53
|
+
* devices: []` error from a poll cycle. The guard provides one place
|
|
54
|
+
* to detect that error, re-request access, and dispatch a reconnect
|
|
55
|
+
* once access returns.
|
|
56
|
+
*
|
|
57
|
+
* Typical wiring:
|
|
58
|
+
* 1. Construct one guard per package with the ports it needs.
|
|
59
|
+
* 2. On every Modbus call site, either:
|
|
60
|
+
* - wrap the call in {@link withAccessGuard}, which recovers in
|
|
61
|
+
* the background on access-denied errors and re-throws, or
|
|
62
|
+
* - catch the error, check {@link isAccessDeniedError}, and call
|
|
63
|
+
* {@link recoverAccess} explicitly.
|
|
64
|
+
* 3. Register an `onAccessRestored` handler (via the config or
|
|
65
|
+
* {@link onAccessRestored}) that performs the reconnect.
|
|
66
|
+
*
|
|
67
|
+
* Re-entrancy: repeated {@link recoverAccess} calls for the same
|
|
68
|
+
* device while a recovery is already in flight are coalesced — the
|
|
69
|
+
* restored handlers run exactly once per restoration.
|
|
70
|
+
*
|
|
71
|
+
* Precondition use: {@link ensureAccess} exposes the same idempotent
|
|
72
|
+
* `requestDeviceAccess` call so callers can use a single API for both
|
|
73
|
+
* "before a connect, make sure we have access" and "we just lost
|
|
74
|
+
* access, recover it".
|
|
75
|
+
*/
|
|
76
|
+
export declare class NetworkAccessGuard {
|
|
77
|
+
private readonly energyApp;
|
|
78
|
+
/** Devices we've re-requested access for and are awaiting a 'granted' signal on. */
|
|
79
|
+
private readonly pending;
|
|
80
|
+
/** Listener IDs registered against the network-devices service. */
|
|
81
|
+
private readonly listenerIds;
|
|
82
|
+
private readonly restoredHandlers;
|
|
83
|
+
private readonly deniedHandlers;
|
|
84
|
+
private readonly ports;
|
|
85
|
+
private readonly enableLogging;
|
|
86
|
+
private disposed;
|
|
87
|
+
/**
|
|
88
|
+
* Recognise the SDK's network-access-denied error so callers don't
|
|
89
|
+
* have to re-implement the substring check. Matches both variants
|
|
90
|
+
* observed in production logs.
|
|
91
|
+
* @param error The error to inspect (any value is accepted)
|
|
92
|
+
* @returns `true` if the error message looks like an access-denied error
|
|
93
|
+
*/
|
|
94
|
+
static isAccessDeniedError(error: unknown): boolean;
|
|
95
|
+
/**
|
|
96
|
+
* Constructs a guard and immediately subscribes to the SDK's
|
|
97
|
+
* device-access-change events.
|
|
98
|
+
* @param energyApp The {@link EnergyApp} instance used to call the network-devices API
|
|
99
|
+
* @param config Required ports plus optional initial handlers and logging flag
|
|
100
|
+
*/
|
|
101
|
+
constructor(energyApp: EnergyApp, config: NetworkAccessGuardConfig);
|
|
102
|
+
/**
|
|
103
|
+
* Registers an additional handler to be invoked whenever access
|
|
104
|
+
* is restored for any NetworkDevice the guard is recovering.
|
|
105
|
+
* @param handler The handler to register
|
|
106
|
+
* @returns A disposer that removes the handler again
|
|
107
|
+
*/
|
|
108
|
+
onAccessRestored(handler: AccessRestoredHandler): () => void;
|
|
109
|
+
/**
|
|
110
|
+
* Registers an additional handler to be invoked whenever a
|
|
111
|
+
* recovery cycle starts (i.e. an access-denied error was just
|
|
112
|
+
* observed). Useful for marking dependent appliances offline.
|
|
113
|
+
* @param handler The handler to register
|
|
114
|
+
* @returns A disposer that removes the handler again
|
|
115
|
+
*/
|
|
116
|
+
onAccessDenied(handler: AccessDeniedHandler): () => void;
|
|
117
|
+
/**
|
|
118
|
+
* Idempotently request access to the configured ports for the
|
|
119
|
+
* given NetworkDevice. Safe to call as a precondition before any
|
|
120
|
+
* Modbus operation.
|
|
121
|
+
* @param networkDeviceId The NetworkDevice to request access on
|
|
122
|
+
* @returns `true` when the SDK reports access is granted, `false` otherwise
|
|
123
|
+
*/
|
|
124
|
+
ensureAccess(networkDeviceId: string): Promise<boolean>;
|
|
125
|
+
/**
|
|
126
|
+
* Wraps an async action so that an access-denied error
|
|
127
|
+
* automatically triggers a background recovery. The original
|
|
128
|
+
* error is re-thrown so the caller can fail-fast on the failed
|
|
129
|
+
* read; the next read after access is restored will succeed.
|
|
130
|
+
*
|
|
131
|
+
* Use this on Modbus call sites for the simplest integration:
|
|
132
|
+
* ```ts
|
|
133
|
+
* await guard.withAccessGuard(networkDeviceId, () =>
|
|
134
|
+
* modbusClient.readHoldingRegisters(...));
|
|
135
|
+
* ```
|
|
136
|
+
* @param networkDeviceId The NetworkDevice the action targets
|
|
137
|
+
* @param action The async operation to perform
|
|
138
|
+
* @returns The resolved value of `action`
|
|
139
|
+
*/
|
|
140
|
+
withAccessGuard<T>(networkDeviceId: string, action: () => Promise<T>): Promise<T>;
|
|
141
|
+
/**
|
|
142
|
+
* Drive recovery from an access-denied error: notify denied
|
|
143
|
+
* handlers, re-request access, and fire the restored handlers
|
|
144
|
+
* once access is granted. The restored handlers run at most once
|
|
145
|
+
* per recovery, regardless of whether the grant is observed
|
|
146
|
+
* synchronously (from the request response) or asynchronously
|
|
147
|
+
* (from the access-change listener).
|
|
148
|
+
* @param networkDeviceId The NetworkDevice whose access needs to be recovered
|
|
149
|
+
*/
|
|
150
|
+
recoverAccess(networkDeviceId: string): Promise<void>;
|
|
151
|
+
/**
|
|
152
|
+
* Returns `true` if a recovery cycle is currently in flight for
|
|
153
|
+
* the given NetworkDevice. Mainly useful for tests and
|
|
154
|
+
* introspection.
|
|
155
|
+
*/
|
|
156
|
+
isRecovering(networkDeviceId: string): boolean;
|
|
157
|
+
/**
|
|
158
|
+
* Tears down the SDK listener and clears all handlers. After
|
|
159
|
+
* calling this the guard should no longer be used.
|
|
160
|
+
*/
|
|
161
|
+
dispose(): void;
|
|
162
|
+
private handleAccessChange;
|
|
163
|
+
private fireRestored;
|
|
164
|
+
private fireDenied;
|
|
165
|
+
}
|