homebridge-omnilogic-pool 0.1.0-beta.1

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 ADDED
@@ -0,0 +1,72 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.0-beta.1] - 2026-05-11
11
+
12
+ First public beta. Published under the `beta` dist-tag while real-world
13
+ firmware coverage is gathered; install with
14
+ `npm install homebridge-omnilogic-pool@beta`.
15
+
16
+ ### Added
17
+
18
+ - Test suite (`node:test` via `tsx`) covering the XML/SOAP helpers
19
+ (redaction, parser variants, topology builder, telemetry indexing,
20
+ auth-failure detection) and `TokenStore` (file mode, round-trip,
21
+ expiry, wrong-user rejection, corrupted file, atomic write). `npm
22
+ test` runs locally and on CI for every Node version in the matrix.
23
+ - `publish.yml` accepts a `workflow_dispatch` with a `dry-run` boolean
24
+ input so the publish workflow can be exercised end-to-end without
25
+ pushing to npm.
26
+ - Disk-backed token cache (`omnilogic-token.json` in Homebridge's plugin
27
+ persist path) so the plugin doesn't re-authenticate on every Homebridge
28
+ restart. Stored with `0600` file permissions and bound to the configured
29
+ username.
30
+ - `hideEquipmentIds` config option to suppress specific accessories without
31
+ disabling whole categories.
32
+ - `disableLogs` config option for users who want a fully quiet plugin.
33
+ - Discovery is now retried with exponential backoff (5s → 5min) when the
34
+ cloud is unreachable at startup, instead of failing the plugin permanently.
35
+ - Discovered equipment is logged at startup with system IDs so users can
36
+ pick IDs to hide via `hideEquipmentIds`.
37
+ - Bug-report issue template (`.github/ISSUE_TEMPLATE/bug_report.yml`).
38
+
39
+ ### Changed
40
+
41
+ - Extracted pure XML/SOAP helpers from `omnilogic-api.ts` into a new
42
+ `xml-utils.ts` module. `OmniLogicApi` is now ~40% smaller and
43
+ focused on transport + token state; the parsing logic is testable
44
+ in isolation. No behaviour change for consumers.
45
+ - Collapsed five duplicated `try { setX(); refresh } catch { log; throw }`
46
+ blocks across the accessory files into a single `BaseAccessory.runApiSet`
47
+ helper.
48
+ - Pulled the 8-parameter "no schedule" trailing block out of three SET
49
+ methods into a shared `NO_SCHEDULE` constant.
50
+ - ESLint rule `quotes` allows template literals (`avoidEscape` only
51
+ covered double quotes).
52
+ - `engines.node` bumped to `^20.10.0 || ^22 || ^24`. Node 18 is no longer
53
+ supported.
54
+ - Dev tooling: dropped `nodemon` in favour of `tsc --watch` + Node's own
55
+ `--watch` flag, orchestrated by `concurrently`.
56
+ - Publish workflow uses `NPM_CONFIG_PROVENANCE` env var instead of the
57
+ `--provenance` flag (functionally identical).
58
+ - README restructured to lead with troubleshooting and quick-start.
59
+
60
+ ## [0.1.0] - Initial release
61
+
62
+ - TypeScript dynamic platform plugin for Hayward OmniLogic.
63
+ - SOAP/XML client for `HomeAutomationService.asmx` with redacted debug
64
+ logs, nested-XML payload parsing, Status validation on SETs, transparent
65
+ re-login on token expiry.
66
+ - Accessory handlers: Heater (Thermostat), variable-speed Filter (Fan),
67
+ pumps/chlorinator (Switch), ColorLogic lights (Lightbulb), water
68
+ temperature (Temperature Sensor).
69
+ - Per-accessory SET mutex and post-SET telemetry refresh for a responsive
70
+ HomeKit experience.
71
+ - GitHub Actions CI on Node 18/20/22 (later moved to 20/22/24) and
72
+ tag-driven npm publish workflow with provenance.
package/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ Licensed under the Apache License, Version 2.0 (the "License");
6
+ you may not use this file except in compliance with the License.
7
+ You may obtain a copy of the License at
8
+
9
+ http://www.apache.org/licenses/LICENSE-2.0
10
+
11
+ Unless required by applicable law or agreed to in writing, software
12
+ distributed under the License is distributed on an "AS IS" BASIS,
13
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ See the License for the specific language governing permissions and
15
+ limitations under the License.
16
+
17
+ Copyright 2026 kzaky
18
+
19
+ The full text of the Apache License 2.0 is available at
20
+ http://www.apache.org/licenses/LICENSE-2.0
package/README.md ADDED
@@ -0,0 +1,187 @@
1
+ # homebridge-omnilogic-pool
2
+
3
+ A modern [Homebridge](https://homebridge.io) plugin for the
4
+ **Hayward OmniLogic** pool/spa controller.
5
+
6
+ Supports Node 20 / 22 / 24 and Homebridge 1.8+ / 2.0. Replaces the
7
+ unmaintained `homebridge-omnilogic` plugin which only ran on legacy
8
+ Node versions.
9
+
10
+ > **Status:** unofficial, community-built, not endorsed by Hayward.
11
+
12
+ ## Troubleshooting first
13
+
14
+ Most setup problems fall into one of these:
15
+
16
+ | Symptom | Likely cause | Fix |
17
+ | --- | --- | --- |
18
+ | `login failed (Status=…)` | Wrong credentials, or account doesn't have access to the site | Verify in the OmniLogic mobile app, then update `username`/`password` |
19
+ | Plugin loads but no accessories appear | MSP config response was empty | Enable `"debug": true`, restart, attach the redacted `GetMspConfigFile` log section to a GitHub issue |
20
+ | HomeKit shows the wrong on/off state | Telemetry attribute names differ for your firmware | Open an issue with the redacted `GetTelemetryData` response |
21
+ | Heater set rejected (`Status=…`) | Equipment is busy or unavailable on the controller | Wait for the controller to settle and retry |
22
+
23
+ Always run with `"debug": true` when filing a bug. Passwords and
24
+ session tokens are **redacted automatically** before logs are emitted.
25
+
26
+ ## Install
27
+
28
+ ```bash
29
+ npm install -g homebridge-omnilogic-pool
30
+ ```
31
+
32
+ Or via Homebridge UI: search for **Hayward OmniLogic Pool**.
33
+
34
+ ## Quick start (config.json)
35
+
36
+ The minimum viable config:
37
+
38
+ ```json
39
+ {
40
+ "platforms": [
41
+ {
42
+ "platform": "OmniLogicPool",
43
+ "name": "OmniLogic Pool",
44
+ "username": "you@example.com",
45
+ "password": "your-omnilogic-password"
46
+ }
47
+ ]
48
+ }
49
+ ```
50
+
51
+ That's it. The plugin discovers your Backyard, bodies of water, and
52
+ equipment automatically and exposes them as HomeKit accessories.
53
+
54
+ ## Configuration reference
55
+
56
+ | Option | Default | Description |
57
+ | --- | --- | --- |
58
+ | `username` | — | OmniLogic account email (required) |
59
+ | `password` | — | OmniLogic account password (required) |
60
+ | `pollIntervalSeconds` | `30` | Telemetry poll cadence. Min `15`. Values below `30` may trigger Hayward rate limiting. |
61
+ | `temperatureUnits` | `F` | `F` or `C` — affects what HomeKit displays for heater setpoints. |
62
+ | `exposeHeaters` | `true` | Expose heaters as Thermostats. |
63
+ | `exposeLights` | `true` | Expose ColorLogic lights as Lightbulbs. |
64
+ | `exposePumps` | `true` | Expose filter and auxiliary pumps. |
65
+ | `exposeChlorinator` | `true` | Expose the chlorinator as a Switch. |
66
+ | `hideEquipmentIds` | `[]` | OmniLogic system IDs to hide from HomeKit. The IDs are logged at startup. |
67
+ | `debug` | `false` | Verbose SOAP request/response logging (auto-redacted). |
68
+ | `disableLogs` | `false` | Suppress every log line emitted by this plugin. |
69
+
70
+ ## Supported equipment
71
+
72
+ | OmniLogic equipment | HomeKit service | Capabilities |
73
+ | --- | --- | --- |
74
+ | Heater | Thermostat | Off / Heat, target temperature |
75
+ | Variable-speed filter | Fan | On/off + rotation speed (% of max) |
76
+ | Auxiliary pump | Switch | On/off |
77
+ | Chlorinator | Switch | On/off (status only on some firmwares) |
78
+ | ColorLogic light | Lightbulb | On/off (remembers last show) |
79
+ | Body of water (water temp) | Temperature Sensor | Read-only |
80
+
81
+ ## Authentication
82
+
83
+ Credentials are exchanged for a session token on first launch and the
84
+ token is cached at:
85
+
86
+ ```
87
+ <homebridge persist path>/omnilogic-token.json
88
+ ```
89
+
90
+ The cache file is written with `0600` permissions and bound to the
91
+ configured username — changing the username in `config.json`
92
+ invalidates the cache automatically. The token is refreshed
93
+ transparently when it expires or is rejected mid-session.
94
+
95
+ ## Security notes
96
+
97
+ - Your OmniLogic credentials live in plain text in Homebridge's
98
+ `config.json`. That's a Homebridge constraint, not a plugin one —
99
+ protect the file the same way you would for any other Homebridge
100
+ plugin.
101
+ - `"debug": true` is **safe to use**: `Password`, `Token`, `UserID`,
102
+ and `UserId` fields are masked before any log line is emitted.
103
+ Verify before sharing if you've configured custom log sinks.
104
+ - All API traffic uses HTTPS to `haywardomnilogic.com`. Certificate
105
+ validation is enabled by default (axios default).
106
+
107
+ ## Development
108
+
109
+ ```bash
110
+ git clone https://github.com/kzaky/OmniLogic.git
111
+ cd OmniLogic
112
+ npm install
113
+ npm run build # one-shot compile
114
+ npm run lint
115
+ npm run dev # tsc --watch + node --watch homebridge -I -D
116
+ ```
117
+
118
+ Source layout:
119
+
120
+ ```
121
+ src/
122
+ index.ts # Homebridge entrypoint
123
+ platform.ts # Dynamic platform: discovery, polling, log silencing
124
+ omnilogic-api.ts # SOAP/XML client (login, MSP, telemetry, set commands)
125
+ token-store.ts # Disk-backed token cache (mode 0600, atomic writes)
126
+ settings.ts # Constants
127
+ types.ts # Config + accessory context types
128
+ accessories/
129
+ base-accessory.ts # Per-accessory SET mutex + post-SET refresh hook
130
+ heater-accessory.ts # Thermostat
131
+ light-accessory.ts # Lightbulb (ColorLogic)
132
+ switch-accessory.ts # Generic on/off (pumps, chlorinator)
133
+ filter-pump-accessory.ts # Variable-speed Fan
134
+ temperature-sensor-accessory.ts
135
+ ```
136
+
137
+ ## Releasing
138
+
139
+ The publish workflow (`.github/workflows/publish.yml`) triggers on
140
+ `v*.*.*` tags:
141
+
142
+ 1. Bump `version` in `package.json` and add a `CHANGELOG.md` entry.
143
+ 2. Commit to `main` and push.
144
+ 3. `git tag v0.2.0 && git push origin v0.2.0`.
145
+ 4. The workflow runs lint + build, verifies the tag matches the
146
+ `package.json` version, then publishes to npm with provenance
147
+ (`NPM_CONFIG_PROVENANCE=true`).
148
+
149
+ Required setup:
150
+
151
+ - A repository secret `NPM_TOKEN` (granular npm automation token with
152
+ publish rights).
153
+ - A GitHub Environment named `npm-publish` bound to that secret. Add
154
+ yourself as a required reviewer if you want manual approval on
155
+ every release.
156
+
157
+ ## API reference (for contributors)
158
+
159
+ The OmniLogic backend is a non-standard SOAP service at
160
+ `https://www.haywardomnilogic.com/HAAPI/HomeAutomation/HomeAutomationService.asmx`.
161
+ This plugin implements requests directly with `axios` +
162
+ `fast-xml-parser`. Methods used:
163
+
164
+ - `Login`
165
+ - `GetSiteList`
166
+ - `GetMspConfigFile`
167
+ - `GetTelemetryData`
168
+ - `SetHeaterEnable`
169
+ - `SetUIHeaterCmd`
170
+ - `SetUIEquipmentCmd`
171
+ - `SetUIFilterSpeedCmd`
172
+ - `SetStandAloneLightShow`
173
+
174
+ The MSP and telemetry responses wrap the meaningful payload as a
175
+ string-encoded XML fragment inside a `<Parameter dataType="XML">`
176
+ element. The client handles both the inline-element and string-XML
177
+ forms.
178
+
179
+ ## Disclaimer
180
+
181
+ Unofficial. Not produced, endorsed, or supported by Hayward
182
+ Industries, Inc. "Hayward" and "OmniLogic" are trademarks of their
183
+ respective owners. Use at your own risk.
184
+
185
+ ## License
186
+
187
+ [Apache-2.0](./LICENSE)
@@ -0,0 +1,86 @@
1
+ {
2
+ "pluginAlias": "OmniLogicPool",
3
+ "pluginType": "platform",
4
+ "singular": true,
5
+ "headerDisplay": "Hayward OmniLogic Pool plugin. Enter the same credentials you use in the OmniLogic mobile app.",
6
+ "footerDisplay": "For help, file an issue at https://github.com/kzaky/OmniLogic/issues.",
7
+ "schema": {
8
+ "type": "object",
9
+ "properties": {
10
+ "name": {
11
+ "title": "Platform Name",
12
+ "type": "string",
13
+ "default": "OmniLogic Pool",
14
+ "required": true
15
+ },
16
+ "username": {
17
+ "title": "OmniLogic Username (email)",
18
+ "type": "string",
19
+ "format": "email",
20
+ "required": true
21
+ },
22
+ "password": {
23
+ "title": "OmniLogic Password",
24
+ "type": "string",
25
+ "format": "password",
26
+ "required": true,
27
+ "description": "Stored in plain text in Homebridge config.json. Treat your Homebridge config file accordingly."
28
+ },
29
+ "pollIntervalSeconds": {
30
+ "title": "Telemetry Poll Interval (seconds)",
31
+ "type": "integer",
32
+ "default": 30,
33
+ "minimum": 15,
34
+ "maximum": 600,
35
+ "description": "How often to poll OmniLogic for live state. Values below 30s may trigger rate limiting from the Hayward cloud."
36
+ },
37
+ "temperatureUnits": {
38
+ "title": "Temperature Units",
39
+ "type": "string",
40
+ "default": "F",
41
+ "oneOf": [
42
+ { "title": "Fahrenheit", "enum": ["F"] },
43
+ { "title": "Celsius", "enum": ["C"] }
44
+ ]
45
+ },
46
+ "exposeChlorinator": {
47
+ "title": "Expose Chlorinator as Switch",
48
+ "type": "boolean",
49
+ "default": true
50
+ },
51
+ "exposePumps": {
52
+ "title": "Expose Pumps",
53
+ "type": "boolean",
54
+ "default": true
55
+ },
56
+ "exposeLights": {
57
+ "title": "Expose Lights",
58
+ "type": "boolean",
59
+ "default": true
60
+ },
61
+ "exposeHeaters": {
62
+ "title": "Expose Heaters as Thermostats",
63
+ "type": "boolean",
64
+ "default": true
65
+ },
66
+ "hideEquipmentIds": {
67
+ "title": "Hide Equipment IDs",
68
+ "type": "array",
69
+ "items": { "type": "integer" },
70
+ "description": "OmniLogic system IDs to suppress from HomeKit. Look at the Homebridge log on startup to see the discovered IDs."
71
+ },
72
+ "debug": {
73
+ "title": "Debug Logging",
74
+ "type": "boolean",
75
+ "default": false,
76
+ "description": "Logs full SOAP request/response XML. Passwords and tokens are redacted automatically."
77
+ },
78
+ "disableLogs": {
79
+ "title": "Disable All Plugin Logs",
80
+ "type": "boolean",
81
+ "default": false,
82
+ "description": "Suppress every log line emitted by this plugin. Use only if you've already validated the setup."
83
+ }
84
+ }
85
+ }
86
+ }
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BaseAccessory = void 0;
4
+ class BaseAccessory {
5
+ platform;
6
+ accessory;
7
+ ctx;
8
+ setQueue = Promise.resolve();
9
+ constructor(platform, accessory) {
10
+ this.platform = platform;
11
+ this.accessory = accessory;
12
+ this.ctx = accessory.context;
13
+ this.setInformation();
14
+ this.setup();
15
+ if (platform.latestTelemetry) {
16
+ this.onTelemetry(platform.latestTelemetry);
17
+ }
18
+ }
19
+ setInformation() {
20
+ const info = this.accessory.getService(this.platform.Service.AccessoryInformation) ??
21
+ this.accessory.addService(this.platform.Service.AccessoryInformation);
22
+ info
23
+ .setCharacteristic(this.platform.Characteristic.Manufacturer, 'Hayward')
24
+ .setCharacteristic(this.platform.Characteristic.Model, `OmniLogic ${this.ctx.kind}`)
25
+ .setCharacteristic(this.platform.Characteristic.SerialNumber, `${this.ctx.mspSystemId}-${this.ctx.equipmentId}`);
26
+ }
27
+ getOrAddService(type) {
28
+ return (this.accessory.getService(type) ?? this.accessory.addService(type));
29
+ }
30
+ telemetryNode(snap) {
31
+ return snap.byId.get(this.ctx.equipmentId);
32
+ }
33
+ bowTelemetry(snap) {
34
+ return snap.byId.get(this.ctx.bowId);
35
+ }
36
+ /**
37
+ * Serialize SET operations on this accessory. HomeKit can fire concurrent
38
+ * SETs for related characteristics (e.g. TargetState + TargetTemperature)
39
+ * which race over OmniLogic's serial protocol; we queue them per accessory
40
+ * so each completes before the next begins.
41
+ */
42
+ runSet(fn) {
43
+ const next = this.setQueue.then(fn, fn);
44
+ // Swallow errors from queue chain so a failure doesn't poison later sets.
45
+ this.setQueue = next.catch(() => undefined);
46
+ return next;
47
+ }
48
+ /**
49
+ * Kick a telemetry refresh shortly after a successful SET so HomeKit
50
+ * doesn't show stale state for the full poll interval.
51
+ */
52
+ requestPostSetRefresh() {
53
+ this.platform.scheduleTelemetryRefresh();
54
+ }
55
+ /**
56
+ * Standard wrapper for a HomeKit SET handler: serialize via the
57
+ * per-accessory mutex, log+rethrow on failure, refresh telemetry on
58
+ * success.
59
+ */
60
+ runApiSet(label, fn) {
61
+ return this.runSet(async () => {
62
+ try {
63
+ await fn();
64
+ this.requestPostSetRefresh();
65
+ }
66
+ catch (err) {
67
+ this.platform.log.error(`${label} failed:`, err.message);
68
+ throw err;
69
+ }
70
+ });
71
+ }
72
+ }
73
+ exports.BaseAccessory = BaseAccessory;
74
+ //# sourceMappingURL=base-accessory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base-accessory.js","sourceRoot":"","sources":["../../src/accessories/base-accessory.ts"],"names":[],"mappings":";;;AAKA,MAAsB,aAAa;IAKZ;IACA;IALF,GAAG,CAAmB;IACjC,QAAQ,GAAqB,OAAO,CAAC,OAAO,EAAE,CAAC;IAEvD,YACqB,QAA2B,EAC3B,SAA8C;QAD9C,aAAQ,GAAR,QAAQ,CAAmB;QAC3B,cAAS,GAAT,SAAS,CAAqC;QAEjE,IAAI,CAAC,GAAG,GAAG,SAAS,CAAC,OAAO,CAAC;QAC7B,IAAI,CAAC,cAAc,EAAE,CAAC;QACtB,IAAI,CAAC,KAAK,EAAE,CAAC;QACb,IAAI,QAAQ,CAAC,eAAe,EAAE,CAAC;YAC7B,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;QAC7C,CAAC;IACH,CAAC;IAES,cAAc;QACtB,MAAM,IAAI,GACR,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,oBAAoB,CAAC;YACrE,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;QAExE,IAAI;aACD,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,YAAY,EAAE,SAAS,CAAC;aACvE,iBAAiB,CAChB,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,KAAK,EAClC,aAAa,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAC7B;aACA,iBAAiB,CAChB,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,YAAY,EACzC,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,IAAI,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAClD,CAAC;IACN,CAAC;IAES,eAAe,CAAC,IAA0B;QAClD,OAAO,CACL,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,CACnE,CAAC;IACJ,CAAC;IAES,aAAa,CAAC,IAAuB;QAC7C,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAC7C,CAAC;IAES,YAAY,CAAC,IAAuB;QAC5C,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACvC,CAAC;IAED;;;;;OAKG;IACO,MAAM,CAAI,EAAoB;QACtC,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QACxC,0EAA0E;QAC1E,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC5C,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;OAGG;IACO,qBAAqB;QAC7B,IAAI,CAAC,QAAQ,CAAC,wBAAwB,EAAE,CAAC;IAC3C,CAAC;IAED;;;;OAIG;IACO,SAAS,CAAC,KAAa,EAAE,EAAuB;QACxD,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE;YAC5B,IAAI,CAAC;gBACH,MAAM,EAAE,EAAE,CAAC;gBACX,IAAI,CAAC,qBAAqB,EAAE,CAAC;YAC/B,CAAC;YAAC,OAAO,GAAQ,EAAE,CAAC;gBAClB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,KAAK,UAAU,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;gBACzD,MAAM,GAAG,CAAC;YACZ,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;CAIF;AAvFD,sCAuFC"}
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FilterPumpAccessory = void 0;
4
+ const base_accessory_1 = require("./base-accessory");
5
+ /**
6
+ * Variable-speed filter pump exposed as a Fan with rotation speed = % of max.
7
+ * On/Off maps to speed 0 vs. the last non-zero speed (default 100).
8
+ */
9
+ class FilterPumpAccessory extends base_accessory_1.BaseAccessory {
10
+ service;
11
+ isOn = false;
12
+ speed = 100;
13
+ lastNonZeroSpeed = 100;
14
+ setup() {
15
+ this.service = this.getOrAddService(this.platform.Service.Fan);
16
+ this.service.setCharacteristic(this.platform.Characteristic.Name, this.ctx.name);
17
+ this.service
18
+ .getCharacteristic(this.platform.Characteristic.On)
19
+ .onGet(() => this.isOn)
20
+ .onSet(this.handleOnSet.bind(this));
21
+ this.service
22
+ .getCharacteristic(this.platform.Characteristic.RotationSpeed)
23
+ .setProps({ minValue: 0, maxValue: 100, minStep: 1 })
24
+ .onGet(() => this.speed)
25
+ .onSet(this.handleSpeedSet.bind(this));
26
+ }
27
+ onTelemetry(snap) {
28
+ const node = this.telemetryNode(snap);
29
+ if (!node)
30
+ return;
31
+ const reported = Number(node['@_filterSpeed']) ||
32
+ Number(node['@_speed']) ||
33
+ Number(node['@_pumpSpeed']);
34
+ if (Number.isFinite(reported)) {
35
+ this.speed = Math.max(0, Math.min(100, reported));
36
+ this.isOn = this.speed > 0;
37
+ if (this.speed > 0)
38
+ this.lastNonZeroSpeed = this.speed;
39
+ this.service.updateCharacteristic(this.platform.Characteristic.On, this.isOn);
40
+ this.service.updateCharacteristic(this.platform.Characteristic.RotationSpeed, this.speed);
41
+ }
42
+ }
43
+ async handleOnSet(value) {
44
+ const on = !!value;
45
+ const desired = on ? this.lastNonZeroSpeed || 100 : 0;
46
+ await this.applySpeed(desired);
47
+ }
48
+ async handleSpeedSet(value) {
49
+ const desired = Number(value);
50
+ if (desired > 0)
51
+ this.lastNonZeroSpeed = desired;
52
+ await this.applySpeed(desired);
53
+ }
54
+ async applySpeed(speed) {
55
+ this.speed = speed;
56
+ this.isOn = speed > 0;
57
+ await this.runApiSet('Filter speed set', () => this.platform.api.setFilterSpeed(this.ctx.mspSystemId, this.ctx.bowId, this.ctx.equipmentId, speed));
58
+ }
59
+ }
60
+ exports.FilterPumpAccessory = FilterPumpAccessory;
61
+ //# sourceMappingURL=filter-pump-accessory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter-pump-accessory.js","sourceRoot":"","sources":["../../src/accessories/filter-pump-accessory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HeaterAccessory = void 0;
4
+ const base_accessory_1 = require("./base-accessory");
5
+ const F_MIN = 50;
6
+ const F_MAX = 104;
7
+ class HeaterAccessory extends base_accessory_1.BaseAccessory {
8
+ service;
9
+ currentTempC = 20;
10
+ targetTempC = 26;
11
+ enabled = false;
12
+ setup() {
13
+ this.service = this.getOrAddService(this.platform.Service.Thermostat);
14
+ this.service.setCharacteristic(this.platform.Characteristic.Name, this.ctx.name);
15
+ this.service
16
+ .getCharacteristic(this.platform.Characteristic.TargetHeatingCoolingState)
17
+ .setProps({
18
+ validValues: [
19
+ this.platform.Characteristic.TargetHeatingCoolingState.OFF,
20
+ this.platform.Characteristic.TargetHeatingCoolingState.HEAT,
21
+ ],
22
+ })
23
+ .onGet(() => this.enabled
24
+ ? this.platform.Characteristic.TargetHeatingCoolingState.HEAT
25
+ : this.platform.Characteristic.TargetHeatingCoolingState.OFF)
26
+ .onSet(this.handleTargetStateSet.bind(this));
27
+ this.service
28
+ .getCharacteristic(this.platform.Characteristic.CurrentHeatingCoolingState)
29
+ .setProps({
30
+ validValues: [
31
+ this.platform.Characteristic.CurrentHeatingCoolingState.OFF,
32
+ this.platform.Characteristic.CurrentHeatingCoolingState.HEAT,
33
+ ],
34
+ });
35
+ const minC = this.fToC(F_MIN);
36
+ const maxC = this.fToC(F_MAX);
37
+ this.service
38
+ .getCharacteristic(this.platform.Characteristic.TargetTemperature)
39
+ .setProps({ minValue: minC, maxValue: maxC, minStep: 0.5 })
40
+ .onGet(() => this.targetTempC)
41
+ .onSet(this.handleTargetTempSet.bind(this));
42
+ this.service
43
+ .getCharacteristic(this.platform.Characteristic.CurrentTemperature)
44
+ .onGet(() => this.currentTempC);
45
+ const wantsF = (this.platform.config.temperatureUnits ?? 'F') === 'F';
46
+ this.service.setCharacteristic(this.platform.Characteristic.TemperatureDisplayUnits, wantsF
47
+ ? this.platform.Characteristic.TemperatureDisplayUnits.FAHRENHEIT
48
+ : this.platform.Characteristic.TemperatureDisplayUnits.CELSIUS);
49
+ }
50
+ onTelemetry(snap) {
51
+ const node = this.telemetryNode(snap);
52
+ const bow = this.bowTelemetry(snap);
53
+ if (!node && !bow)
54
+ return;
55
+ const reportedF = Number(node?.['@_temp']) ||
56
+ Number(bow?.['@_waterTemp']) ||
57
+ Number(bow?.['@_temp']);
58
+ const setpointF = Number(node?.['@_Current-Set-Point']) ||
59
+ Number(node?.['@_currentSetPoint']) ||
60
+ Number(node?.['@_settingsTemp']);
61
+ const enabled = Number(node?.['@_enable']) === 1 ||
62
+ String(node?.['@_enable']).toLowerCase() === 'true' ||
63
+ Number(node?.['@_heaterState']) === 1;
64
+ if (Number.isFinite(reportedF) && reportedF > 0) {
65
+ this.currentTempC = this.fToC(reportedF);
66
+ this.service.updateCharacteristic(this.platform.Characteristic.CurrentTemperature, this.currentTempC);
67
+ }
68
+ if (Number.isFinite(setpointF) && setpointF > 0) {
69
+ this.targetTempC = this.fToC(setpointF);
70
+ this.service.updateCharacteristic(this.platform.Characteristic.TargetTemperature, this.targetTempC);
71
+ }
72
+ this.enabled = enabled;
73
+ this.service.updateCharacteristic(this.platform.Characteristic.CurrentHeatingCoolingState, enabled
74
+ ? this.platform.Characteristic.CurrentHeatingCoolingState.HEAT
75
+ : this.platform.Characteristic.CurrentHeatingCoolingState.OFF);
76
+ this.service.updateCharacteristic(this.platform.Characteristic.TargetHeatingCoolingState, enabled
77
+ ? this.platform.Characteristic.TargetHeatingCoolingState.HEAT
78
+ : this.platform.Characteristic.TargetHeatingCoolingState.OFF);
79
+ }
80
+ async handleTargetStateSet(value) {
81
+ const want = value === this.platform.Characteristic.TargetHeatingCoolingState.HEAT;
82
+ this.enabled = want;
83
+ await this.runApiSet('Heater enable', () => this.platform.api.setHeaterEnable(this.ctx.mspSystemId, this.ctx.bowId, this.ctx.equipmentId, want));
84
+ }
85
+ async handleTargetTempSet(value) {
86
+ const tempC = Number(value);
87
+ this.targetTempC = tempC;
88
+ const tempF = this.cToF(tempC);
89
+ await this.runApiSet('Heater setpoint', () => this.platform.api.setHeaterSetpoint(this.ctx.mspSystemId, this.ctx.bowId, this.ctx.equipmentId, tempF));
90
+ }
91
+ fToC(f) {
92
+ return Math.round((((f - 32) * 5) / 9) * 2) / 2;
93
+ }
94
+ cToF(c) {
95
+ return Math.round((c * 9) / 5 + 32);
96
+ }
97
+ }
98
+ exports.HeaterAccessory = HeaterAccessory;
99
+ //# sourceMappingURL=heater-accessory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heater-accessory.js","sourceRoot":"","sources":["../../src/accessories/heater-accessory.ts"],"names":[],"mappings":";;;AACA,qDAAiD;AAGjD,MAAM,KAAK,GAAG,EAAE,CAAC;AACjB,MAAM,KAAK,GAAG,GAAG,CAAC;AAElB,MAAa,eAAgB,SAAQ,8BAAa;IACxC,OAAO,CAAW;IAClB,YAAY,GAAG,EAAE,CAAC;IAClB,WAAW,GAAG,EAAE,CAAC;IACjB,OAAO,GAAG,KAAK,CAAC;IAExB,KAAK;QACH,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QACtE,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAC5B,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,EACjC,IAAI,CAAC,GAAG,CAAC,IAAI,CACd,CAAC;QAEF,IAAI,CAAC,OAAO;aACT,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,yBAAyB,CAAC;aACzE,QAAQ,CAAC;YACR,WAAW,EAAE;gBACX,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,yBAAyB,CAAC,GAAG;gBAC1D,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,yBAAyB,CAAC,IAAI;aAC5D;SACF,CAAC;aACD,KAAK,CAAC,GAAG,EAAE,CACV,IAAI,CAAC,OAAO;YACV,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,yBAAyB,CAAC,IAAI;YAC7D,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,yBAAyB,CAAC,GAAG,CAC/D;aACA,KAAK,CAAC,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAE/C,IAAI,CAAC,OAAO;aACT,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,0BAA0B,CAAC;aAC1E,QAAQ,CAAC;YACR,WAAW,EAAE;gBACX,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,0BAA0B,CAAC,GAAG;gBAC3D,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,0BAA0B,CAAC,IAAI;aAC7D;SACF,CAAC,CAAC;QAEL,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9B,IAAI,CAAC,OAAO;aACT,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,iBAAiB,CAAC;aACjE,QAAQ,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;aAC1D,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC;aAC7B,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAE9C,IAAI,CAAC,OAAO;aACT,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,kBAAkB,CAAC;aAClE,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAElC,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,gBAAgB,IAAI,GAAG,CAAC,KAAK,GAAG,CAAC;QACtE,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAC5B,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,uBAAuB,EACpD,MAAM;YACJ,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,uBAAuB,CAAC,UAAU;YACjE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,uBAAuB,CAAC,OAAO,CACjE,CAAC;IACJ,CAAC;IAED,WAAW,CAAC,IAAuB;QACjC,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,CAAC,IAAI,IAAI,CAAC,GAAG;YAAE,OAAO;QAE1B,MAAM,SAAS,GACb,MAAM,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,CAAC;YACxB,MAAM,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,CAAC;YAC5B,MAAM,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC1B,MAAM,SAAS,GACb,MAAM,CAAC,IAAI,EAAE,CAAC,qBAAqB,CAAC,CAAC;YACrC,MAAM,CAAC,IAAI,EAAE,CAAC,mBAAmB,CAAC,CAAC;YACnC,MAAM,CAAC,IAAI,EAAE,CAAC,gBAAgB,CAAC,CAAC,CAAC;QACnC,MAAM,OAAO,GACX,MAAM,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YAChC,MAAM,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,WAAW,EAAE,KAAK,MAAM;YACnD,MAAM,CAAC,IAAI,EAAE,CAAC,eAAe,CAAC,CAAC,KAAK,CAAC,CAAC;QAExC,IAAI,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;YAChD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACzC,IAAI,CAAC,OAAO,CAAC,oBAAoB,CAC/B,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,kBAAkB,EAC/C,IAAI,CAAC,YAAY,CAClB,CAAC;QACJ,CAAC;QACD,IAAI,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;YAChD,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACxC,IAAI,CAAC,OAAO,CAAC,oBAAoB,CAC/B,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,iBAAiB,EAC9C,IAAI,CAAC,WAAW,CACjB,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,OAAO,CAAC,oBAAoB,CAC/B,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,0BAA0B,EACvD,OAAO;YACL,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,0BAA0B,CAAC,IAAI;YAC9D,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,0BAA0B,CAAC,GAAG,CAChE,CAAC;QACF,IAAI,CAAC,OAAO,CAAC,oBAAoB,CAC/B,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,yBAAyB,EACtD,OAAO;YACL,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,yBAAyB,CAAC,IAAI;YAC7D,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,yBAAyB,CAAC,GAAG,CAC/D,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,oBAAoB,CAAC,KAA0B;QAC3D,MAAM,IAAI,GACR,KAAK,KAAK,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,yBAAyB,CAAC,IAAI,CAAC;QACxE,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,MAAM,IAAI,CAAC,SAAS,CAAC,eAAe,EAAE,GAAG,EAAE,CACzC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,eAAe,CAC/B,IAAI,CAAC,GAAG,CAAC,WAAW,EACpB,IAAI,CAAC,GAAG,CAAC,KAAK,EACd,IAAI,CAAC,GAAG,CAAC,WAAW,EACpB,IAAI,CACL,CACF,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,mBAAmB,CAAC,KAA0B;QAC1D,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5B,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;QACzB,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC/B,MAAM,IAAI,CAAC,SAAS,CAAC,iBAAiB,EAAE,GAAG,EAAE,CAC3C,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,iBAAiB,CACjC,IAAI,CAAC,GAAG,CAAC,WAAW,EACpB,IAAI,CAAC,GAAG,CAAC,KAAK,EACd,IAAI,CAAC,GAAG,CAAC,WAAW,EACpB,KAAK,CACN,CACF,CAAC;IACJ,CAAC;IAEO,IAAI,CAAC,CAAS;QACpB,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IAClD,CAAC;IAEO,IAAI,CAAC,CAAS;QACpB,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC;IACtC,CAAC;CACF;AA5ID,0CA4IC"}
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LightAccessory = void 0;
4
+ const base_accessory_1 = require("./base-accessory");
5
+ /**
6
+ * ColorLogic light. Modeled as a Lightbulb with on/off. "Off" sends show 0,
7
+ * "on" restores the last-known show (default Voodoo Lounge / 1).
8
+ */
9
+ class LightAccessory extends base_accessory_1.BaseAccessory {
10
+ service;
11
+ isOn = false;
12
+ lastShow = 1;
13
+ setup() {
14
+ this.service = this.getOrAddService(this.platform.Service.Lightbulb);
15
+ this.service.setCharacteristic(this.platform.Characteristic.Name, this.ctx.name);
16
+ this.service
17
+ .getCharacteristic(this.platform.Characteristic.On)
18
+ .onGet(() => this.isOn)
19
+ .onSet(this.handleOnSet.bind(this));
20
+ }
21
+ onTelemetry(snap) {
22
+ const node = this.telemetryNode(snap);
23
+ if (!node)
24
+ return;
25
+ const state = Number(node['@_lightState']) ||
26
+ Number(node['@_state']) ||
27
+ Number(node['@_speed']);
28
+ const show = Number(node['@_currentShow']);
29
+ if (Number.isFinite(show) && show > 0)
30
+ this.lastShow = show;
31
+ const on = state > 0 || (Number.isFinite(show) && show > 0);
32
+ this.isOn = on;
33
+ this.service.updateCharacteristic(this.platform.Characteristic.On, on);
34
+ }
35
+ async handleOnSet(value) {
36
+ const on = !!value;
37
+ this.isOn = on;
38
+ await this.runApiSet('Light set', () => this.platform.api.setLightShow(this.ctx.mspSystemId, this.ctx.bowId, this.ctx.equipmentId, on ? this.lastShow : 0));
39
+ }
40
+ }
41
+ exports.LightAccessory = LightAccessory;
42
+ //# sourceMappingURL=light-accessory.js.map