homebridge-plugin-klares4 2.0.1-beta0 → 2.0.1-beta2
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/CHANGELOG.md +20 -1
- package/README.md +2 -0
- package/config.schema.json +108 -1
- package/dist/ksa/cache-service.d.ts +11 -0
- package/dist/ksa/cache-service.d.ts.map +1 -0
- package/dist/ksa/cache-service.js +73 -0
- package/dist/ksa/cache-service.js.map +1 -0
- package/dist/ksa/derive.d.ts +3 -0
- package/dist/ksa/derive.d.ts.map +1 -0
- package/dist/ksa/derive.js +191 -0
- package/dist/ksa/derive.js.map +1 -0
- package/dist/ksa/parser.d.ts +4 -0
- package/dist/ksa/parser.d.ts.map +1 -0
- package/dist/ksa/parser.js +112 -0
- package/dist/ksa/parser.js.map +1 -0
- package/dist/ksa/types.d.ts +85 -0
- package/dist/ksa/types.d.ts.map +1 -0
- package/dist/ksa/types.js +3 -0
- package/dist/ksa/types.js.map +1 -0
- package/dist/platform/config-file-service.d.ts +1 -0
- package/dist/platform/config-file-service.d.ts.map +1 -1
- package/dist/platform/config-file-service.js +12 -4
- package/dist/platform/config-file-service.js.map +1 -1
- package/dist/platform/index.d.ts +1 -0
- package/dist/platform/index.d.ts.map +1 -1
- package/dist/platform/index.js +4 -0
- package/dist/platform/index.js.map +1 -1
- package/dist/platform/ksa-import-service.d.ts +15 -0
- package/dist/platform/ksa-import-service.d.ts.map +1 -0
- package/dist/platform/ksa-import-service.js +149 -0
- package/dist/platform/ksa-import-service.js.map +1 -0
- package/dist/platform/types.d.ts +2 -1
- package/dist/platform/types.d.ts.map +1 -1
- package/dist/types/websocket.types.d.ts +47 -0
- package/dist/types/websocket.types.d.ts.map +1 -1
- package/dist/websocket-client/command-service.d.ts +1 -0
- package/dist/websocket-client/command-service.d.ts.map +1 -1
- package/dist/websocket-client/command-service.js +22 -1
- package/dist/websocket-client/command-service.js.map +1 -1
- package/dist/websocket-client/domus-thermostat-mapping-runtime.d.ts.map +1 -1
- package/dist/websocket-client/domus-thermostat-mapping-runtime.js +22 -2
- package/dist/websocket-client/domus-thermostat-mapping-runtime.js.map +1 -1
- package/dist/websocket-client/index.js +1 -1
- package/dist/websocket-client/index.js.map +1 -1
- package/dist/websocket-client/message-service.d.ts +1 -0
- package/dist/websocket-client/message-service.d.ts.map +1 -1
- package/dist/websocket-client/message-service.js +35 -0
- package/dist/websocket-client/message-service.js.map +1 -1
- package/dist/websocket-client/state.d.ts +2 -2
- package/dist/websocket-client/state.d.ts.map +1 -1
- package/dist/websocket-client/state.js +26 -1
- package/dist/websocket-client/state.js.map +1 -1
- package/dist/websocket-client/thermostat-command-id-resolver.d.ts +3 -1
- package/dist/websocket-client/thermostat-command-id-resolver.d.ts.map +1 -1
- package/dist/websocket-client/thermostat-command-id-resolver.js +14 -5
- package/dist/websocket-client/thermostat-command-id-resolver.js.map +1 -1
- package/dist/websocket-client/thermostat-config-sync.js +13 -2
- package/dist/websocket-client/thermostat-config-sync.js.map +1 -1
- package/dist/websocket-client/thermostat-status-updater.d.ts +1 -0
- package/dist/websocket-client/thermostat-status-updater.d.ts.map +1 -1
- package/dist/websocket-client/thermostat-status-updater.js +24 -3
- package/dist/websocket-client/thermostat-status-updater.js.map +1 -1
- package/dist/websocket-client/types.d.ts +12 -2
- package/dist/websocket-client/types.d.ts.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,11 +7,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
-
## [2.0.1-
|
|
10
|
+
## [2.0.1-beta2] - 2026-03-13
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- New KSA import workflow in plugin config (`ksaImport`) to parse central backup metadata and derive deterministic routing data.
|
|
15
|
+
- Sanitized KSA cache persisted in Homebridge storage (`klares4-ksa-cache.json`) with thermostat/output/sensor/room mappings only (no raw backup persistence).
|
|
16
|
+
- Automatic derivation from KSA for:
|
|
17
|
+
- `domusThermostat.manualCommandPairs` (`output -> CFG/STATUS thermostat ID`)
|
|
18
|
+
- `domusThermostat.manualPairs` (`output -> Domus sensor`)
|
|
19
|
+
- optional `roomMapping` and optional `customNames`.
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- Runtime thermostat routing now preloads program mappings from KSA cache when realtime `PRG_THERMOSTATS` is unavailable on panel firmware.
|
|
24
|
+
- Degraded command resolver now evaluates mapped Domus sensor IDs before raw output fallback, improving compatibility in non-PRG environments.
|
|
25
|
+
- Initial websocket state can bootstrap thermostat program maps from cached KSA data to avoid startup desynchronization.
|
|
26
|
+
|
|
27
|
+
## [2.0.1-beta1] - 2026-03-13
|
|
11
28
|
|
|
12
29
|
### Fixed
|
|
13
30
|
|
|
14
31
|
- DOMUS thermostats now use `STATUS_TEMPERATURES` as the authoritative source for realtime mode, setpoint, current temperature and HVAC activity.
|
|
32
|
+
- DOMUS thermostat routing now uses `PRG_THERMOSTATS` over WSS to resolve the real relation between output, thermostat config ID and Domus sensor.
|
|
33
|
+
- Fixed swapped command routing on installations where `CFG_THERMOSTATS.ID` does not match the Domus sensor ID, including the real-world `Matrimoniale`/`Bagno` inversion.
|
|
15
34
|
- Removed the automatic legacy `WRITE/THERMOSTAT` fallback: thermostat writes now use only the observed `WRITE_CFG/CFG_THERMOSTATS` protocol.
|
|
16
35
|
- `STATUS_SYSTEM` fallback no longer overwrites mapped Domus/realtime thermostat data while fresher room or thermostat telemetry is available.
|
|
17
36
|
|
package/README.md
CHANGED
|
@@ -109,6 +109,7 @@ The plugin can be fully configured via the Homebridge UI graphical interface. Re
|
|
|
109
109
|
| `allowInsecureTls` | boolean | false | Disable TLS certificate validation (trusted LAN only) |
|
|
110
110
|
| `logLevel` | number | 1 | 0=minimal, 1=normal, 2=debug |
|
|
111
111
|
| `domusThermostat` | object | enabled/freshness defaults | DOMUS thermostat mapping, manual overrides, freshness fallback |
|
|
112
|
+
| `ksaImport` | object | disabled | Import KSA backup metadata for thermostat routing, room mapping and optional config apply |
|
|
112
113
|
| `debug` | boolean | false | Detailed logging |
|
|
113
114
|
| `excludeZones` | string[] | [] | Zones to exclude |
|
|
114
115
|
| `excludeOutputs` | string[] | [] | Outputs to exclude |
|
|
@@ -401,6 +402,7 @@ Il plugin puo essere configurato completamente tramite l'interfaccia grafica di
|
|
|
401
402
|
| `heartbeatInterval` | number | 30000 | Intervallo heartbeat (ms) |
|
|
402
403
|
| `logLevel` | number | 1 | 0=minimal, 1=normal, 2=debug |
|
|
403
404
|
| `domusThermostat` | object | default attivi | Mapping termostati DOMUS, override manuali e fallback freshness |
|
|
405
|
+
| `ksaImport` | object | disabilitato | Import metadata da backup KSA per routing termostati, room mapping e apply opzionale |
|
|
404
406
|
| `debug` | boolean | false | Logging dettagliato |
|
|
405
407
|
| `excludeZones` | string[] | [] | Zone da escludere |
|
|
406
408
|
| `excludeOutputs` | string[] | [] | Output da escludere |
|
package/config.schema.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"singular": true,
|
|
5
5
|
"customUi": false,
|
|
6
6
|
"headerDisplay": "<div style='margin-bottom:12px;'><h3 style='margin:0 0 6px 0;'>Klares4 - Ksenia Lares4 per Homebridge</h3><p style='margin:0 0 12px 0;'>Plugin Homebridge per controllo locale della centrale Ksenia Lares4.</p><div style='padding:10px 12px;border:1px solid currentColor;border-radius:8px;'><strong>Prerequisiti:</strong><ul style='margin:8px 0 0 18px;padding:0;'><li>Centrale raggiungibile in rete locale</li><li>Interfaccia WebSocket abilitata</li><li>PIN utente valido (4-8 cifre)</li></ul></div></div>",
|
|
7
|
-
"footerDisplay": "<div style='margin-top:16px;padding-top:12px;border-top:1px solid currentColor;'><p style='margin:0 0 8px 0;'>Versione 2.0.1-
|
|
7
|
+
"footerDisplay": "<div style='margin-top:16px;padding-top:12px;border-top:1px solid currentColor;'><p style='margin:0 0 8px 0;'>Versione 2.0.1-beta2 • Sviluppato da Paolo Trivisonno</p><p style='margin:0;'><a href='https://github.com/paolo-trivi/homebridge-plugin-klares4' target='_blank' rel='noopener noreferrer'>Documentazione</a> | <a href='https://github.com/paolo-trivi/homebridge-plugin-klares4/issues' target='_blank' rel='noopener noreferrer'>Segnala Bug</a> | <a href='https://github.com/sponsors/paolo-trivi' target='_blank' rel='noopener noreferrer'>Supporta</a></p></div>",
|
|
8
8
|
"schema": {
|
|
9
9
|
"type": "object",
|
|
10
10
|
"properties": {
|
|
@@ -300,6 +300,58 @@
|
|
|
300
300
|
}
|
|
301
301
|
}
|
|
302
302
|
},
|
|
303
|
+
"ksaImport": {
|
|
304
|
+
"type": "object",
|
|
305
|
+
"default": {
|
|
306
|
+
"enabled": false,
|
|
307
|
+
"applyAtStartup": false,
|
|
308
|
+
"applyDomusMappings": true,
|
|
309
|
+
"applyRoomMapping": true,
|
|
310
|
+
"applyCustomNames": false,
|
|
311
|
+
"applyExclusionSuggestions": false
|
|
312
|
+
},
|
|
313
|
+
"properties": {
|
|
314
|
+
"enabled": {
|
|
315
|
+
"title": "Abilita Import KSA",
|
|
316
|
+
"type": "boolean",
|
|
317
|
+
"default": false,
|
|
318
|
+
"description": "Carica il file backup .ksa per generare mapping e metadata strutturali"
|
|
319
|
+
},
|
|
320
|
+
"filePath": {
|
|
321
|
+
"title": "Percorso File .ksa",
|
|
322
|
+
"type": "string",
|
|
323
|
+
"placeholder": "/var/lib/homebridge/prg-xxxx.ksa",
|
|
324
|
+
"description": "Percorso assoluto del file backup .ksa esportato dalla centrale"
|
|
325
|
+
},
|
|
326
|
+
"applyAtStartup": {
|
|
327
|
+
"title": "Applica su config al riavvio",
|
|
328
|
+
"type": "boolean",
|
|
329
|
+
"default": false,
|
|
330
|
+
"description": "Preview + apply: se attivo, applica su config.json e poi si resetta a false"
|
|
331
|
+
},
|
|
332
|
+
"applyDomusMappings": {
|
|
333
|
+
"title": "Applica mapping Domus termostati",
|
|
334
|
+
"type": "boolean",
|
|
335
|
+
"default": true
|
|
336
|
+
},
|
|
337
|
+
"applyRoomMapping": {
|
|
338
|
+
"title": "Applica room mapping",
|
|
339
|
+
"type": "boolean",
|
|
340
|
+
"default": true
|
|
341
|
+
},
|
|
342
|
+
"applyCustomNames": {
|
|
343
|
+
"title": "Applica nomi personalizzati",
|
|
344
|
+
"type": "boolean",
|
|
345
|
+
"default": false
|
|
346
|
+
},
|
|
347
|
+
"applyExclusionSuggestions": {
|
|
348
|
+
"title": "Applica suggerimenti esclusioni",
|
|
349
|
+
"type": "boolean",
|
|
350
|
+
"default": false,
|
|
351
|
+
"description": "Non consigliato di default: applica liste esclusione suggerite dal parser"
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
},
|
|
303
355
|
"mqtt": {
|
|
304
356
|
"type": "object",
|
|
305
357
|
"properties": {
|
|
@@ -619,6 +671,61 @@
|
|
|
619
671
|
}
|
|
620
672
|
]
|
|
621
673
|
},
|
|
674
|
+
{
|
|
675
|
+
"type": "fieldset",
|
|
676
|
+
"title": "Import KSA",
|
|
677
|
+
"expandable": false,
|
|
678
|
+
"items": [
|
|
679
|
+
{
|
|
680
|
+
"key": "ksaImport.enabled",
|
|
681
|
+
"title": "Import KSA Attivo"
|
|
682
|
+
},
|
|
683
|
+
{
|
|
684
|
+
"key": "ksaImport.filePath",
|
|
685
|
+
"title": "Percorso File .ksa",
|
|
686
|
+
"condition": {
|
|
687
|
+
"functionBody": "return model.ksaImport && model.ksaImport.enabled === true;"
|
|
688
|
+
}
|
|
689
|
+
},
|
|
690
|
+
{
|
|
691
|
+
"key": "ksaImport.applyAtStartup",
|
|
692
|
+
"title": "Apply alla Config al Prossimo Avvio",
|
|
693
|
+
"description": "Esegue preview nei log e applica i blocchi selezionati, poi torna automaticamente OFF",
|
|
694
|
+
"condition": {
|
|
695
|
+
"functionBody": "return model.ksaImport && model.ksaImport.enabled === true;"
|
|
696
|
+
}
|
|
697
|
+
},
|
|
698
|
+
{
|
|
699
|
+
"type": "flex",
|
|
700
|
+
"flex-flow": "row wrap",
|
|
701
|
+
"condition": {
|
|
702
|
+
"functionBody": "return model.ksaImport && model.ksaImport.enabled === true;"
|
|
703
|
+
},
|
|
704
|
+
"items": [
|
|
705
|
+
{
|
|
706
|
+
"key": "ksaImport.applyDomusMappings",
|
|
707
|
+
"title": "Domus Mapping",
|
|
708
|
+
"flex": "1 1 200px"
|
|
709
|
+
},
|
|
710
|
+
{
|
|
711
|
+
"key": "ksaImport.applyRoomMapping",
|
|
712
|
+
"title": "Room Mapping",
|
|
713
|
+
"flex": "1 1 200px"
|
|
714
|
+
},
|
|
715
|
+
{
|
|
716
|
+
"key": "ksaImport.applyCustomNames",
|
|
717
|
+
"title": "Custom Names",
|
|
718
|
+
"flex": "1 1 200px"
|
|
719
|
+
},
|
|
720
|
+
{
|
|
721
|
+
"key": "ksaImport.applyExclusionSuggestions",
|
|
722
|
+
"title": "Exclusions",
|
|
723
|
+
"flex": "1 1 200px"
|
|
724
|
+
}
|
|
725
|
+
]
|
|
726
|
+
}
|
|
727
|
+
]
|
|
728
|
+
},
|
|
622
729
|
{
|
|
623
730
|
"type": "fieldset",
|
|
624
731
|
"title": "Scenari",
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Logger } from 'homebridge';
|
|
2
|
+
import type { KsaSanitizedCache } from '../types';
|
|
3
|
+
export declare class KsaCacheService {
|
|
4
|
+
private readonly log;
|
|
5
|
+
private readonly cachePath;
|
|
6
|
+
constructor(storagePath: string, log: Logger);
|
|
7
|
+
get path(): string;
|
|
8
|
+
save(cache: KsaSanitizedCache): Promise<void>;
|
|
9
|
+
load(): Promise<KsaSanitizedCache | undefined>;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=cache-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache-service.d.ts","sourceRoot":"","sources":["../../src/ksa/cache-service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAIlD,qBAAa,eAAe;IAGS,OAAO,CAAC,QAAQ,CAAC,GAAG;IAFrD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;gBAEvB,WAAW,EAAE,MAAM,EAAmB,GAAG,EAAE,MAAM;IAI7D,IAAW,IAAI,IAAI,MAAM,CAExB;IAEY,IAAI,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7C,IAAI,IAAI,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC;CAmB9D"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.KsaCacheService = void 0;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const CACHE_FILE_NAME = 'klares4-ksa-cache.json';
|
|
40
|
+
class KsaCacheService {
|
|
41
|
+
constructor(storagePath, log) {
|
|
42
|
+
this.log = log;
|
|
43
|
+
this.cachePath = path.join(storagePath, CACHE_FILE_NAME);
|
|
44
|
+
}
|
|
45
|
+
get path() {
|
|
46
|
+
return this.cachePath;
|
|
47
|
+
}
|
|
48
|
+
async save(cache) {
|
|
49
|
+
await fs.promises.writeFile(this.cachePath, JSON.stringify(cache, null, 2), 'utf8');
|
|
50
|
+
}
|
|
51
|
+
async load() {
|
|
52
|
+
try {
|
|
53
|
+
const raw = await fs.promises.readFile(this.cachePath, 'utf8');
|
|
54
|
+
const parsed = JSON.parse(raw);
|
|
55
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
if (!Array.isArray(parsed.thermostatPrograms)) {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
return parsed;
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
65
|
+
if (!message.includes('ENOENT')) {
|
|
66
|
+
this.log.warn(`Unable to load KSA cache: ${message}`);
|
|
67
|
+
}
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
exports.KsaCacheService = KsaCacheService;
|
|
73
|
+
//# sourceMappingURL=cache-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache-service.js","sourceRoot":"","sources":["../../src/ksa/cache-service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,uCAAyB;AACzB,2CAA6B;AAI7B,MAAM,eAAe,GAAG,wBAAwB,CAAC;AAEjD,MAAa,eAAe;IAGxB,YAAY,WAAmB,EAAmB,GAAW;QAAX,QAAG,GAAH,GAAG,CAAQ;QACzD,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;IAC7D,CAAC;IAED,IAAW,IAAI;QACX,OAAO,IAAI,CAAC,SAAS,CAAC;IAC1B,CAAC;IAEM,KAAK,CAAC,IAAI,CAAC,KAAwB;QACtC,MAAM,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IACxF,CAAC;IAEM,KAAK,CAAC,IAAI;QACb,IAAI,CAAC;YACD,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;YAC/D,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAsB,CAAC;YACpD,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;gBACxC,OAAO,SAAS,CAAC;YACrB,CAAC;YACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,kBAAkB,CAAC,EAAE,CAAC;gBAC5C,OAAO,SAAS,CAAC;YACrB,CAAC;YACD,OAAO,MAAM,CAAC;QAClB,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACtB,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC9B,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,6BAA6B,OAAO,EAAE,CAAC,CAAC;YAC1D,CAAC;YACD,OAAO,SAAS,CAAC;QACrB,CAAC;IACL,CAAC;CACJ;AAlCD,0CAkCC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"derive.d.ts","sourceRoot":"","sources":["../../src/ksa/derive.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAoB,eAAe,EAAgB,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEjG,wBAAgB,qBAAqB,CACjC,OAAO,EAAE,gBAAgB,EACzB,cAAc,EAAE,MAAM,GAAG,SAAS,EAClC,QAAQ,EAAE,MAAM,GACjB,eAAe,CAgBjB"}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deriveKsaImportResult = deriveKsaImportResult;
|
|
4
|
+
const crypto_1 = require("crypto");
|
|
5
|
+
const device_state_projector_1 = require("../websocket/device-state-projector");
|
|
6
|
+
function deriveKsaImportResult(program, sourceFilePath, rawBytes) {
|
|
7
|
+
const cache = buildSanitizedCache(program, sourceFilePath, rawBytes);
|
|
8
|
+
const derivedConfig = buildDerivedConfig(program, cache);
|
|
9
|
+
return {
|
|
10
|
+
summary: {
|
|
11
|
+
outputs: program.outputs.length,
|
|
12
|
+
zones: program.zones.length,
|
|
13
|
+
scenarios: program.scenarios.length,
|
|
14
|
+
sensors: program.busHas.length,
|
|
15
|
+
thermostats: program.thermostats.length,
|
|
16
|
+
rooms: program.rooms.length,
|
|
17
|
+
maps: program.maps.length,
|
|
18
|
+
},
|
|
19
|
+
cache,
|
|
20
|
+
derivedConfig,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function buildSanitizedCache(program, sourceFilePath, rawBytes) {
|
|
24
|
+
const thermostatProgramIdByOutputId = {};
|
|
25
|
+
const domusSensorIdByThermostatProgramId = {};
|
|
26
|
+
const thermostatPrograms = program.thermostats
|
|
27
|
+
.map((thermostat) => {
|
|
28
|
+
const thermostatProgramId = asId(thermostat.ID);
|
|
29
|
+
if (!thermostatProgramId) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
const heatingOutputId = normalizeNullableId(thermostat.HEATING_OUT);
|
|
33
|
+
const coolingOutputId = normalizeNullableId(thermostat.COOLING_OUT);
|
|
34
|
+
const domusSensorId = normalizeNullableId(thermostat.PERIPH?.PID);
|
|
35
|
+
if (heatingOutputId) {
|
|
36
|
+
thermostatProgramIdByOutputId[heatingOutputId] = thermostatProgramId;
|
|
37
|
+
}
|
|
38
|
+
if (coolingOutputId) {
|
|
39
|
+
thermostatProgramIdByOutputId[coolingOutputId] = thermostatProgramId;
|
|
40
|
+
}
|
|
41
|
+
if (domusSensorId) {
|
|
42
|
+
domusSensorIdByThermostatProgramId[thermostatProgramId] = domusSensorId;
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
id: thermostatProgramId,
|
|
46
|
+
description: normalizeLabel(thermostat.DES) ?? undefined,
|
|
47
|
+
heatingOutputId: heatingOutputId ?? undefined,
|
|
48
|
+
coolingOutputId: coolingOutputId ?? undefined,
|
|
49
|
+
domusSensorId: domusSensorId ?? undefined,
|
|
50
|
+
};
|
|
51
|
+
})
|
|
52
|
+
.filter((entry) => entry !== null);
|
|
53
|
+
return {
|
|
54
|
+
sourceFilePath,
|
|
55
|
+
sourceFileHash: (0, crypto_1.createHash)('sha256').update(rawBytes).digest('hex'),
|
|
56
|
+
parsedAt: new Date().toISOString(),
|
|
57
|
+
thermostatPrograms,
|
|
58
|
+
thermostatProgramIdByOutputId,
|
|
59
|
+
domusSensorIdByThermostatProgramId,
|
|
60
|
+
outputNamesById: toNameRecord(program.outputs),
|
|
61
|
+
zoneNamesById: toNameRecord(program.zones),
|
|
62
|
+
scenarioNamesById: toNameRecord(program.scenarios),
|
|
63
|
+
domusSensorNamesById: toNameRecord(program.busHas.map((sensor) => ({
|
|
64
|
+
ID: sensor.ID,
|
|
65
|
+
DES: sensor.DOMUS?.DES ?? `Sensor ${sensor.ID}`,
|
|
66
|
+
}))),
|
|
67
|
+
roomNameById: toNameRecord(program.rooms),
|
|
68
|
+
roomDeviceRefs: toRoomDeviceRefs(program.maps),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function buildDerivedConfig(program, cache) {
|
|
72
|
+
const outputById = new Map(program.outputs.map((output) => [String(output.ID), output]));
|
|
73
|
+
const roomNameById = cache.roomNameById;
|
|
74
|
+
const roomDevices = new Map();
|
|
75
|
+
for (const map of cache.roomDeviceRefs) {
|
|
76
|
+
const roomName = roomNameById[map.roomId];
|
|
77
|
+
if (!roomName) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
const resolvedDeviceIds = resolveRoomMapToDeviceIds(map, outputById);
|
|
81
|
+
if (resolvedDeviceIds.length === 0) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const bucket = roomDevices.get(roomName) ?? new Set();
|
|
85
|
+
for (const deviceId of resolvedDeviceIds) {
|
|
86
|
+
bucket.add(deviceId);
|
|
87
|
+
}
|
|
88
|
+
roomDevices.set(roomName, bucket);
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
domusThermostat: {
|
|
92
|
+
manualPairs: Object.entries(cache.thermostatProgramIdByOutputId)
|
|
93
|
+
.map(([outputId, thermostatProgramId]) => ({
|
|
94
|
+
thermostatOutputId: outputId,
|
|
95
|
+
domusSensorId: cache.domusSensorIdByThermostatProgramId[thermostatProgramId],
|
|
96
|
+
}))
|
|
97
|
+
.filter((pair) => Boolean(pair.domusSensorId)),
|
|
98
|
+
manualCommandPairs: Object.entries(cache.thermostatProgramIdByOutputId)
|
|
99
|
+
.map(([outputId, thermostatProgramId]) => ({
|
|
100
|
+
thermostatOutputId: outputId,
|
|
101
|
+
commandThermostatId: thermostatProgramId,
|
|
102
|
+
})),
|
|
103
|
+
},
|
|
104
|
+
roomMapping: {
|
|
105
|
+
enabled: true,
|
|
106
|
+
rooms: [...roomDevices.entries()].map(([roomName, devices]) => ({
|
|
107
|
+
roomName,
|
|
108
|
+
devices: [...devices].map((deviceId) => ({ deviceId })),
|
|
109
|
+
})),
|
|
110
|
+
},
|
|
111
|
+
customNames: {
|
|
112
|
+
outputs: cache.outputNamesById,
|
|
113
|
+
zones: cache.zoneNamesById,
|
|
114
|
+
sensors: cache.domusSensorNamesById,
|
|
115
|
+
scenarios: cache.scenarioNamesById,
|
|
116
|
+
},
|
|
117
|
+
suggestedExclusions: {
|
|
118
|
+
outputs: [],
|
|
119
|
+
zones: [],
|
|
120
|
+
sensors: [],
|
|
121
|
+
scenarios: [],
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function resolveRoomMapToDeviceIds(map, outputById) {
|
|
126
|
+
if (map.objectType === 'prgOutputs') {
|
|
127
|
+
const output = outputById.get(map.objectId);
|
|
128
|
+
if (!output) {
|
|
129
|
+
return [];
|
|
130
|
+
}
|
|
131
|
+
const outputType = (0, device_state_projector_1.determineOutputType)(output.CAT ?? '', output.MOD);
|
|
132
|
+
return [`${outputType}_${map.objectId}`];
|
|
133
|
+
}
|
|
134
|
+
if (map.objectType === 'prgZones') {
|
|
135
|
+
return [`zone_${map.objectId}`];
|
|
136
|
+
}
|
|
137
|
+
if (map.objectType === 'prgScenarios') {
|
|
138
|
+
return [`scenario_${map.objectId}`];
|
|
139
|
+
}
|
|
140
|
+
if (map.objectType === 'prgBusHas') {
|
|
141
|
+
return [`sensor_temp_${map.objectId}`, `sensor_hum_${map.objectId}`, `sensor_light_${map.objectId}`];
|
|
142
|
+
}
|
|
143
|
+
return [];
|
|
144
|
+
}
|
|
145
|
+
function toNameRecord(items) {
|
|
146
|
+
const record = {};
|
|
147
|
+
for (const item of items) {
|
|
148
|
+
const id = asId(item.ID);
|
|
149
|
+
if (!id) {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
const name = normalizeLabel(item.DES);
|
|
153
|
+
if (name) {
|
|
154
|
+
record[id] = name;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return record;
|
|
158
|
+
}
|
|
159
|
+
function toRoomDeviceRefs(maps) {
|
|
160
|
+
return maps
|
|
161
|
+
.map((map) => ({
|
|
162
|
+
roomId: asId(map.ROOM),
|
|
163
|
+
objectType: normalizeLabel(map.OT) ?? '',
|
|
164
|
+
objectId: asId(map.OID),
|
|
165
|
+
}))
|
|
166
|
+
.filter((map) => Boolean(map.roomId) && Boolean(map.objectType) && Boolean(map.objectId));
|
|
167
|
+
}
|
|
168
|
+
function asId(value) {
|
|
169
|
+
if (typeof value === 'string') {
|
|
170
|
+
return value.trim();
|
|
171
|
+
}
|
|
172
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
173
|
+
return Math.trunc(value).toString();
|
|
174
|
+
}
|
|
175
|
+
return '';
|
|
176
|
+
}
|
|
177
|
+
function normalizeNullableId(value) {
|
|
178
|
+
const normalized = asId(value);
|
|
179
|
+
if (!normalized || normalized.toUpperCase() === 'NU' || normalized.toUpperCase() === 'NA') {
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
return normalized;
|
|
183
|
+
}
|
|
184
|
+
function normalizeLabel(value) {
|
|
185
|
+
if (typeof value !== 'string') {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
const normalized = value.trim();
|
|
189
|
+
return normalized ? normalized : null;
|
|
190
|
+
}
|
|
191
|
+
//# sourceMappingURL=derive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"derive.js","sourceRoot":"","sources":["../../src/ksa/derive.ts"],"names":[],"mappings":";;AAKA,sDAoBC;AAzBD,mCAAoC;AAEpC,gFAA0E;AAG1E,SAAgB,qBAAqB,CACjC,OAAyB,EACzB,cAAkC,EAClC,QAAgB;IAEhB,MAAM,KAAK,GAAG,mBAAmB,CAAC,OAAO,EAAE,cAAc,EAAE,QAAQ,CAAC,CAAC;IACrE,MAAM,aAAa,GAAG,kBAAkB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACzD,OAAO;QACH,OAAO,EAAE;YACL,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM;YAC/B,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,MAAM;YAC3B,SAAS,EAAE,OAAO,CAAC,SAAS,CAAC,MAAM;YACnC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM;YAC9B,WAAW,EAAE,OAAO,CAAC,WAAW,CAAC,MAAM;YACvC,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,MAAM;YAC3B,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,MAAM;SAC5B;QACD,KAAK;QACL,aAAa;KAChB,CAAC;AACN,CAAC;AAED,SAAS,mBAAmB,CAAC,OAAyB,EAAE,cAAkC,EAAE,QAAgB;IACxG,MAAM,6BAA6B,GAA2B,EAAE,CAAC;IACjE,MAAM,kCAAkC,GAA2B,EAAE,CAAC;IACtE,MAAM,kBAAkB,GAAG,OAAO,CAAC,WAAW;SACzC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE;QAChB,MAAM,mBAAmB,GAAG,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QAChD,IAAI,CAAC,mBAAmB,EAAE,CAAC;YACvB,OAAO,IAAI,CAAC;QAChB,CAAC;QACD,MAAM,eAAe,GAAG,mBAAmB,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;QACpE,MAAM,eAAe,GAAG,mBAAmB,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;QACpE,MAAM,aAAa,GAAG,mBAAmB,CAAC,UAAU,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAClE,IAAI,eAAe,EAAE,CAAC;YAClB,6BAA6B,CAAC,eAAe,CAAC,GAAG,mBAAmB,CAAC;QACzE,CAAC;QACD,IAAI,eAAe,EAAE,CAAC;YAClB,6BAA6B,CAAC,eAAe,CAAC,GAAG,mBAAmB,CAAC;QACzE,CAAC;QACD,IAAI,aAAa,EAAE,CAAC;YAChB,kCAAkC,CAAC,mBAAmB,CAAC,GAAG,aAAa,CAAC;QAC5E,CAAC;QACD,OAAO;YACH,EAAE,EAAE,mBAAmB;YACvB,WAAW,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,SAAS;YACxD,eAAe,EAAE,eAAe,IAAI,SAAS;YAC7C,eAAe,EAAE,eAAe,IAAI,SAAS;YAC7C,aAAa,EAAE,aAAa,IAAI,SAAS;SAC5C,CAAC;IACN,CAAC,CAAC;SACD,MAAM,CAAC,CAAC,KAAK,EAAsC,EAAE,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC;IAE3E,OAAO;QACH,cAAc;QACd,cAAc,EAAE,IAAA,mBAAU,EAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;QACnE,QAAQ,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QAClC,kBAAkB;QAClB,6BAA6B;QAC7B,kCAAkC;QAClC,eAAe,EAAE,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC;QAC9C,aAAa,EAAE,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC;QAC1C,iBAAiB,EAAE,YAAY,CAAC,OAAO,CAAC,SAAS,CAAC;QAClD,oBAAoB,EAAE,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;YAC/D,EAAE,EAAE,MAAM,CAAC,EAAE;YACb,GAAG,EAAE,MAAM,CAAC,KAAK,EAAE,GAAG,IAAI,UAAU,MAAM,CAAC,EAAE,EAAE;SAClD,CAAC,CAAC,CAAC;QACJ,YAAY,EAAE,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC;QACzC,cAAc,EAAE,gBAAgB,CAAC,OAAO,CAAC,IAAI,CAAC;KACjD,CAAC;AACN,CAAC;AAED,SAAS,kBAAkB,CAAC,OAAyB,EAAE,KAAwB;IAC3E,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IACzF,MAAM,YAAY,GAAG,KAAK,CAAC,YAAY,CAAC;IACxC,MAAM,WAAW,GAAG,IAAI,GAAG,EAAuB,CAAC;IAEnD,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,cAAc,EAAE,CAAC;QACrC,MAAM,QAAQ,GAAG,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC1C,IAAI,CAAC,QAAQ,EAAE,CAAC;YACZ,SAAS;QACb,CAAC;QACD,MAAM,iBAAiB,GAAG,yBAAyB,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;QACrE,IAAI,iBAAiB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACjC,SAAS;QACb,CAAC;QACD,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,IAAI,GAAG,EAAU,CAAC;QAC9D,KAAK,MAAM,QAAQ,IAAI,iBAAiB,EAAE,CAAC;YACvC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACzB,CAAC;QACD,WAAW,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACtC,CAAC;IAED,OAAO;QACH,eAAe,EAAE;YACb,WAAW,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,6BAA6B,CAAC;iBAC3D,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,mBAAmB,CAAC,EAAE,EAAE,CAAC,CAAC;gBACvC,kBAAkB,EAAE,QAAQ;gBAC5B,aAAa,EAAE,KAAK,CAAC,kCAAkC,CAAC,mBAAmB,CAAC;aAC/E,CAAC,CAAC;iBACF,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YAClD,kBAAkB,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,6BAA6B,CAAC;iBAClE,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,mBAAmB,CAAC,EAAE,EAAE,CAAC,CAAC;gBACvC,kBAAkB,EAAE,QAAQ;gBAC5B,mBAAmB,EAAE,mBAAmB;aAC3C,CAAC,CAAC;SACV;QACD,WAAW,EAAE;YACT,OAAO,EAAE,IAAI;YACb,KAAK,EAAE,CAAC,GAAG,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC5D,QAAQ;gBACR,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC;aAC1D,CAAC,CAAC;SACN;QACD,WAAW,EAAE;YACT,OAAO,EAAE,KAAK,CAAC,eAAe;YAC9B,KAAK,EAAE,KAAK,CAAC,aAAa;YAC1B,OAAO,EAAE,KAAK,CAAC,oBAAoB;YACnC,SAAS,EAAE,KAAK,CAAC,iBAAiB;SACrC;QACD,mBAAmB,EAAE;YACjB,OAAO,EAAE,EAAE;YACX,KAAK,EAAE,EAAE;YACT,OAAO,EAAE,EAAE;YACX,SAAS,EAAE,EAAE;SAChB;KACJ,CAAC;AACN,CAAC;AAED,SAAS,yBAAyB,CAAC,GAA6C,EAAE,UAAuD;IACrI,IAAI,GAAG,CAAC,UAAU,KAAK,YAAY,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC5C,IAAI,CAAC,MAAM,EAAE,CAAC;YACV,OAAO,EAAE,CAAC;QACd,CAAC;QACD,MAAM,UAAU,GAAG,IAAA,4CAAmB,EAAC,MAAM,CAAC,GAAG,IAAI,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;QACrE,OAAO,CAAC,GAAG,UAAU,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC7C,CAAC;IACD,IAAI,GAAG,CAAC,UAAU,KAAK,UAAU,EAAE,CAAC;QAChC,OAAO,CAAC,QAAQ,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;IACpC,CAAC;IACD,IAAI,GAAG,CAAC,UAAU,KAAK,cAAc,EAAE,CAAC;QACpC,OAAO,CAAC,YAAY,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;IACxC,CAAC;IACD,IAAI,GAAG,CAAC,UAAU,KAAK,WAAW,EAAE,CAAC;QACjC,OAAO,CAAC,eAAe,GAAG,CAAC,QAAQ,EAAE,EAAE,cAAc,GAAG,CAAC,QAAQ,EAAE,EAAE,gBAAgB,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;IACzG,CAAC;IACD,OAAO,EAAE,CAAC;AACd,CAAC;AAED,SAAS,YAAY,CAAC,KAA0C;IAC5D,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACvB,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACzB,IAAI,CAAC,EAAE,EAAE,CAAC;YACN,SAAS;QACb,CAAC;QACD,MAAM,IAAI,GAAG,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACtC,IAAI,IAAI,EAAE,CAAC;YACP,MAAM,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC;QACtB,CAAC;IACL,CAAC;IACD,OAAO,MAAM,CAAC;AAClB,CAAC;AAED,SAAS,gBAAgB,CAAC,IAAoB;IAC1C,OAAO,IAAI;SACN,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACX,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;QACtB,UAAU,EAAE,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE;QACxC,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;KAC1B,CAAC,CAAC;SACF,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAItF,CAAC;AACX,CAAC;AAED,SAAS,IAAI,CAAC,KAAc;IACxB,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC5B,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC;IACxB,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACtD,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,QAAQ,EAAE,CAAC;IACxC,CAAC;IACD,OAAO,EAAE,CAAC;AACd,CAAC;AAED,SAAS,mBAAmB,CAAC,KAAc;IACvC,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;IAC/B,IAAI,CAAC,UAAU,IAAI,UAAU,CAAC,WAAW,EAAE,KAAK,IAAI,IAAI,UAAU,CAAC,WAAW,EAAE,KAAK,IAAI,EAAE,CAAC;QACxF,OAAO,IAAI,CAAC;IAChB,CAAC;IACD,OAAO,UAAU,CAAC;AACtB,CAAC;AAED,SAAS,cAAc,CAAC,KAAc;IAClC,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC5B,OAAO,IAAI,CAAC;IAChB,CAAC;IACD,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAChC,OAAO,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC;AAC1C,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/ksa/parser.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAMhD,wBAAsB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAGxF;AAED,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,CAYvE"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.readAndParseKsaProgram = readAndParseKsaProgram;
|
|
37
|
+
exports.parseKsaProgramFromBuffer = parseKsaProgramFromBuffer;
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
async function readAndParseKsaProgram(filePath) {
|
|
40
|
+
const raw = await fs.promises.readFile(filePath);
|
|
41
|
+
return parseKsaProgramFromBuffer(raw);
|
|
42
|
+
}
|
|
43
|
+
function parseKsaProgramFromBuffer(raw) {
|
|
44
|
+
const root = parseEmbeddedRoot(raw);
|
|
45
|
+
const data = asObject(root.DATA);
|
|
46
|
+
return {
|
|
47
|
+
outputs: asArray(data.PRG_OUTPUTS),
|
|
48
|
+
zones: asArray(data.PRG_ZONES),
|
|
49
|
+
scenarios: asArray(data.PRG_SCENARIOS),
|
|
50
|
+
busHas: asArray(data.PRG_BUS_HAS),
|
|
51
|
+
thermostats: asArray(data.PRG_THERMOSTATS),
|
|
52
|
+
rooms: asArray(data.PRG_ROOMS),
|
|
53
|
+
maps: asArray(data.PRG_MAPS),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function parseEmbeddedRoot(raw) {
|
|
57
|
+
const start = raw.indexOf(Buffer.from('{"INFO"'));
|
|
58
|
+
if (start < 0) {
|
|
59
|
+
throw new Error('KSA parsing failed: JSON payload marker not found');
|
|
60
|
+
}
|
|
61
|
+
const jsonSlice = raw.subarray(start);
|
|
62
|
+
const end = findBalancedJsonObjectEnd(jsonSlice);
|
|
63
|
+
const payload = jsonSlice.subarray(0, end).toString('utf8');
|
|
64
|
+
return JSON.parse(payload);
|
|
65
|
+
}
|
|
66
|
+
function findBalancedJsonObjectEnd(rawJsonSlice) {
|
|
67
|
+
let depth = 0;
|
|
68
|
+
let inString = false;
|
|
69
|
+
let escaped = false;
|
|
70
|
+
for (let index = 0; index < rawJsonSlice.length; index += 1) {
|
|
71
|
+
const char = String.fromCharCode(rawJsonSlice[index]);
|
|
72
|
+
if (inString) {
|
|
73
|
+
if (escaped) {
|
|
74
|
+
escaped = false;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (char === '\\') {
|
|
78
|
+
escaped = true;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (char === '"') {
|
|
82
|
+
inString = false;
|
|
83
|
+
}
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (char === '"') {
|
|
87
|
+
inString = true;
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (char === '{') {
|
|
91
|
+
depth += 1;
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (char === '}') {
|
|
95
|
+
depth -= 1;
|
|
96
|
+
if (depth === 0) {
|
|
97
|
+
return index + 1;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
throw new Error('KSA parsing failed: unterminated embedded JSON payload');
|
|
102
|
+
}
|
|
103
|
+
function asObject(value) {
|
|
104
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
105
|
+
return {};
|
|
106
|
+
}
|
|
107
|
+
return value;
|
|
108
|
+
}
|
|
109
|
+
function asArray(value) {
|
|
110
|
+
return Array.isArray(value) ? value : [];
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/ksa/parser.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAOA,wDAGC;AAED,8DAYC;AAxBD,uCAAyB;AAOlB,KAAK,UAAU,sBAAsB,CAAC,QAAgB;IACzD,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACjD,OAAO,yBAAyB,CAAC,GAAG,CAAC,CAAC;AAC1C,CAAC;AAED,SAAgB,yBAAyB,CAAC,GAAW;IACjD,MAAM,IAAI,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC;IACpC,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjC,OAAO;QACH,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC;QAClC,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC;QAC9B,SAAS,EAAE,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC;QACtC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC;QACjC,WAAW,EAAE,OAAO,CAAC,IAAI,CAAC,eAAe,CAAC;QAC1C,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC;QAC9B,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC;KAC/B,CAAC;AACN,CAAC;AAED,SAAS,iBAAiB,CAAC,GAAW;IAClC,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;IAClD,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACzE,CAAC;IACD,MAAM,SAAS,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACtC,MAAM,GAAG,GAAG,yBAAyB,CAAC,SAAS,CAAC,CAAC;IACjD,MAAM,OAAO,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC5D,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAkB,CAAC;AAChD,CAAC;AAED,SAAS,yBAAyB,CAAC,YAAoB;IACnD,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,YAAY,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QAC1D,MAAM,IAAI,GAAG,MAAM,CAAC,YAAY,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC;QACtD,IAAI,QAAQ,EAAE,CAAC;YACX,IAAI,OAAO,EAAE,CAAC;gBACV,OAAO,GAAG,KAAK,CAAC;gBAChB,SAAS;YACb,CAAC;YACD,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;gBAChB,OAAO,GAAG,IAAI,CAAC;gBACf,SAAS;YACb,CAAC;YACD,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;gBACf,QAAQ,GAAG,KAAK,CAAC;YACrB,CAAC;YACD,SAAS;QACb,CAAC;QACD,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACf,QAAQ,GAAG,IAAI,CAAC;YAChB,SAAS;QACb,CAAC;QACD,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACf,KAAK,IAAI,CAAC,CAAC;YACX,SAAS;QACb,CAAC;QACD,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACf,KAAK,IAAI,CAAC,CAAC;YACX,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;gBACd,OAAO,KAAK,GAAG,CAAC,CAAC;YACrB,CAAC;QACL,CAAC;IACL,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;AAC9E,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC5B,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC9D,OAAO,EAAE,CAAC;IACd,CAAC;IACD,OAAO,KAAgC,CAAC;AAC5C,CAAC;AAED,SAAS,OAAO,CAAI,KAAc;IAC9B,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAE,KAAa,CAAC,CAAC,CAAC,EAAE,CAAC;AACtD,CAAC"}
|