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 +72 -0
- package/LICENSE +20 -0
- package/README.md +187 -0
- package/config.schema.json +86 -0
- package/dist/accessories/base-accessory.js +74 -0
- package/dist/accessories/base-accessory.js.map +1 -0
- package/dist/accessories/filter-pump-accessory.js +61 -0
- package/dist/accessories/filter-pump-accessory.js.map +1 -0
- package/dist/accessories/heater-accessory.js +99 -0
- package/dist/accessories/heater-accessory.js.map +1 -0
- package/dist/accessories/light-accessory.js +42 -0
- package/dist/accessories/light-accessory.js.map +1 -0
- package/dist/accessories/switch-accessory.js +42 -0
- package/dist/accessories/switch-accessory.js.map +1 -0
- package/dist/accessories/temperature-sensor-accessory.js +30 -0
- package/dist/accessories/temperature-sensor-accessory.js.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/omnilogic-api.js +255 -0
- package/dist/omnilogic-api.js.map +1 -0
- package/dist/platform.js +315 -0
- package/dist/platform.js.map +1 -0
- package/dist/settings.js +9 -0
- package/dist/settings.js.map +1 -0
- package/dist/token-store.js +129 -0
- package/dist/token-store.js.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/xml-utils.js +242 -0
- package/dist/xml-utils.js.map +1 -0
- package/package.json +62 -0
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":"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"}
|
|
@@ -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
|