nodejs-poolcontroller 8.1.2 → 8.4.0
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/.eslintrc.json +36 -36
- package/.github/ISSUE_TEMPLATE/1-bug-report.yml +84 -84
- package/.github/ISSUE_TEMPLATE/2-docs.md +12 -12
- package/.github/ISSUE_TEMPLATE/3-proposal.md +28 -28
- package/.github/ISSUE_TEMPLATE/config.yml +8 -8
- package/.github/copilot-instructions.md +63 -0
- package/.github/workflows/ghcr-publish.yml +67 -0
- package/AGENTS.md +597 -0
- package/CONTRIBUTING.md +74 -74
- package/Changelog +292 -257
- package/Dockerfile +62 -19
- package/Gruntfile.js +40 -40
- package/LICENSE +661 -661
- package/README.md +318 -191
- package/anslq25/MessagesMock.ts +221 -221
- package/anslq25/boards/MockBoardFactory.ts +49 -49
- package/anslq25/boards/MockEasyTouchBoard.ts +696 -696
- package/anslq25/boards/MockSystemBoard.ts +216 -216
- package/anslq25/chemistry/MockChlorinator.ts +98 -98
- package/anslq25/pumps/MockPump.ts +83 -83
- package/app.ts +115 -115
- package/config/Config.ts +57 -7
- package/config/VersionCheck.ts +63 -35
- package/controller/Constants.ts +809 -805
- package/controller/Equipment.ts +2688 -2664
- package/controller/Errors.ts +181 -181
- package/controller/Lockouts.ts +549 -549
- package/controller/State.ts +3738 -3690
- package/controller/boards/AquaLinkBoard.ts +1003 -1003
- package/controller/boards/BoardFactory.ts +53 -53
- package/controller/boards/EasyTouchBoard.ts +3202 -3202
- package/controller/boards/IntelliCenterBoard.ts +4393 -3899
- package/controller/boards/IntelliComBoard.ts +69 -69
- package/controller/boards/IntelliTouchBoard.ts +382 -382
- package/controller/boards/NixieBoard.ts +1944 -1929
- package/controller/boards/SunTouchBoard.ts +400 -400
- package/controller/boards/SystemBoard.ts +5268 -5268
- package/controller/comms/Comms.ts +1272 -1214
- package/controller/comms/ScreenLogic.ts +1665 -1665
- package/controller/comms/messages/Messages.ts +1433 -1243
- package/controller/comms/messages/config/ChlorinatorMessage.ts +5 -0
- package/controller/comms/messages/config/CircuitGroupMessage.ts +0 -0
- package/controller/comms/messages/config/CircuitMessage.ts +0 -0
- package/controller/comms/messages/config/ConfigMessage.ts +6 -0
- package/controller/comms/messages/config/CoverMessage.ts +0 -0
- package/controller/comms/messages/config/CustomNameMessage.ts +31 -31
- package/controller/comms/messages/config/EquipmentMessage.ts +216 -210
- package/controller/comms/messages/config/ExternalMessage.ts +96 -10
- package/controller/comms/messages/config/FeatureMessage.ts +0 -0
- package/controller/comms/messages/config/GeneralMessage.ts +0 -0
- package/controller/comms/messages/config/HeaterMessage.ts +0 -0
- package/controller/comms/messages/config/IntellichemMessage.ts +0 -0
- package/controller/comms/messages/config/OptionsMessage.ts +194 -174
- package/controller/comms/messages/config/PumpMessage.ts +0 -0
- package/controller/comms/messages/config/RemoteMessage.ts +0 -0
- package/controller/comms/messages/config/ScheduleMessage.ts +401 -390
- package/controller/comms/messages/config/SecurityMessage.ts +0 -0
- package/controller/comms/messages/config/ValveMessage.ts +0 -0
- package/controller/comms/messages/status/ChlorinatorStateMessage.ts +0 -0
- package/controller/comms/messages/status/EquipmentStateMessage.ts +1158 -822
- package/controller/comms/messages/status/HeaterStateMessage.ts +135 -135
- package/controller/comms/messages/status/IntelliChemStateMessage.ts +448 -448
- package/controller/comms/messages/status/IntelliValveStateMessage.ts +36 -36
- package/controller/comms/messages/status/PumpStateMessage.ts +0 -0
- package/controller/comms/messages/status/RegalModbusStateMessage.ts +411 -0
- package/controller/comms/messages/status/VersionMessage.ts +103 -41
- package/controller/nixie/Nixie.ts +173 -173
- package/controller/nixie/NixieEquipment.ts +104 -104
- package/controller/nixie/bodies/Body.ts +120 -120
- package/controller/nixie/bodies/Filter.ts +135 -135
- package/controller/nixie/chemistry/ChemController.ts +2724 -2724
- package/controller/nixie/chemistry/ChemDoser.ts +806 -806
- package/controller/nixie/chemistry/Chlorinator.ts +367 -367
- package/controller/nixie/circuits/Circuit.ts +478 -478
- package/controller/nixie/heaters/Heater.ts +834 -834
- package/controller/nixie/pumps/Pump.ts +1194 -996
- package/controller/nixie/schedules/Schedule.ts +401 -401
- package/controller/nixie/valves/Valve.ts +170 -170
- package/defaultConfig.json +352 -347
- package/docker-compose.yml +32 -0
- package/logger/DataLogger.ts +448 -448
- package/logger/Logger.ts +448 -436
- package/package.json +58 -60
- package/sendSocket.js +32 -32
- package/tsconfig.json +25 -25
- package/types/express-multer.d.ts +32 -0
- package/web/Server.ts +1937 -1927
- package/web/bindings/aqualinkD.json +559 -559
- package/web/bindings/influxDB.json +1066 -1066
- package/web/bindings/mqtt.json +721 -721
- package/web/bindings/mqttAlt.json +746 -746
- package/web/bindings/rulesManager.json +54 -54
- package/web/bindings/smartThings-Hubitat.json +31 -31
- package/web/bindings/valveRelays.json +20 -20
- package/web/bindings/vera.json +25 -25
- package/web/interfaces/baseInterface.ts +188 -188
- package/web/interfaces/httpInterface.ts +148 -148
- package/web/interfaces/influxInterface.ts +283 -283
- package/web/interfaces/mqttInterface.ts +695 -695
- package/web/interfaces/ruleInterface.ts +101 -87
- package/web/services/config/Config.ts +1063 -1053
- package/web/services/config/ConfigSocket.ts +0 -0
- package/web/services/state/State.ts +0 -0
- package/web/services/state/StateSocket.ts +0 -0
- package/web/services/utilities/Utilities.ts +233 -233
- package/.github/workflows/docker-publish-njsPC-linux.yml +0 -50
package/AGENTS.md
ADDED
|
@@ -0,0 +1,597 @@
|
|
|
1
|
+
# AGENTS.md - Lessons Learned & Guidelines
|
|
2
|
+
|
|
3
|
+
**Purpose:** Document patterns, corrections, and lessons learned during development to improve future interactions.
|
|
4
|
+
|
|
5
|
+
## ⚠️ FIRST: Check .plan/ Directory
|
|
6
|
+
|
|
7
|
+
**Before debugging protocol/packet issues:**
|
|
8
|
+
1. Read `.plan/INDEX.md` - Master index for protocol documentation (links to controller-specific indexes)
|
|
9
|
+
2. Check equipment-specific protocol files (e.g., `.plan/201-intellicenter-circuits-features.md`, `.plan/202-intellicenter-bodies-temps.md`)
|
|
10
|
+
3. Review `.plan/203-intellicenter-action-registry.md` for action code meanings
|
|
11
|
+
|
|
12
|
+
**Protocol documentation is in `.plan/`, NOT in this file.** This file contains coding patterns and lessons learned.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Architecture Patterns (CRITICAL)
|
|
17
|
+
|
|
18
|
+
### 0. State Access Pattern: Use Accessors, Not Raw JSON
|
|
19
|
+
**Rule:** Do NOT set/get nested state JSON objects directly unless the accessor is explicitly designed to work with an object.
|
|
20
|
+
- Most state fields are **persisted as JSON**, but the public API should be **scalar getters/setters** (number/string/boolean) that:
|
|
21
|
+
- normalize persisted shapes
|
|
22
|
+
- apply transforms/valueMaps
|
|
23
|
+
- manage `hasChanged` / `state.dirty` correctly
|
|
24
|
+
- Only use object-level access when there is an **explicit object accessor** whose contract is “you pass an object” (and it manages diffing/emits).
|
|
25
|
+
|
|
26
|
+
**Examples**
|
|
27
|
+
- **Scalar (preferred)**:
|
|
28
|
+
- Set: `state.status = 0`
|
|
29
|
+
- Get: `if (state.status === 0) { /* ... */ }`
|
|
30
|
+
- **Explicit object accessor (allowed)**:
|
|
31
|
+
- Set: `state.temps.bodies.getItemById(1).heaterOptions = { total: 2, gas: 1, heatpump: 1 }`
|
|
32
|
+
- Get: `const opts = state.temps.bodies.getItemById(1).heaterOptions`
|
|
33
|
+
|
|
34
|
+
### 1. Message Processing Location
|
|
35
|
+
**Rule:** NO direct message processing logic in `Messages.ts`
|
|
36
|
+
- `Messages.ts` is a ROUTER only
|
|
37
|
+
- Delegate to appropriate message files:
|
|
38
|
+
- Status messages → `EquipmentStateMessage.ts`
|
|
39
|
+
- Config messages → `ConfigMessage.ts`, `ExternalMessage.ts`, etc.
|
|
40
|
+
|
|
41
|
+
**Violated in this chat:**
|
|
42
|
+
- Action 217 handler with logic directly in Messages.ts (lines 792-810)
|
|
43
|
+
- Should be in `EquipmentStateMessage.ts` or similar
|
|
44
|
+
|
|
45
|
+
**Correction:** ✅ FIXED - Action 217 now delegated to EquipmentStateMessage.ts
|
|
46
|
+
|
|
47
|
+
### 2. State Persistence
|
|
48
|
+
**Rule:** NO local class variables for cross-session state
|
|
49
|
+
- Ephemeral state (current session only) → Local variables OK
|
|
50
|
+
- Persistent state (survives restarts) → Must use:
|
|
51
|
+
- `poolConfig.json` (configuration data)
|
|
52
|
+
- `poolState.json` (runtime state)
|
|
53
|
+
- With appropriate Equipment/State class methods
|
|
54
|
+
|
|
55
|
+
**Violated in this chat:**
|
|
56
|
+
- `_hasRegistered` and `_registrationConfirmed` as class variables
|
|
57
|
+
- These need to survive restarts if OCP remembers registration
|
|
58
|
+
|
|
59
|
+
**Correction:** ✅ FIXED - Now using `state.equipment.registration` in poolState.json
|
|
60
|
+
|
|
61
|
+
### 3. Equipment State vs Top-Level State
|
|
62
|
+
**Rule:** Equipment-related state belongs in `EquipmentState`, not top-level `State`
|
|
63
|
+
- Follow existing pattern: properties like `controllerType`, `model`, `maxCircuits` are in `EquipmentState`
|
|
64
|
+
- Equipment-related state → `state.equipment.propertyName`
|
|
65
|
+
- Top-level state is for collections (pumps, circuits, etc.) or system-wide state (temps, heliotrope)
|
|
66
|
+
|
|
67
|
+
**Violated in this chat:**
|
|
68
|
+
- Initially added `registration` property to top-level `State` class
|
|
69
|
+
- Should be in `EquipmentState` since it's controller registration state
|
|
70
|
+
|
|
71
|
+
**Correction:** ✅ FIXED - Moved to `state.equipment.registration` with getter/setter in EquipmentState
|
|
72
|
+
|
|
73
|
+
### 4. Protocol Values vs Invented States
|
|
74
|
+
**Rule:** Only use values that come from the actual protocol
|
|
75
|
+
- Don't invent intermediate states that aren't in the protocol
|
|
76
|
+
- State should reflect what OCP reports, not our internal workflow
|
|
77
|
+
- Example: Action 217 byte[2] has values 0, 1, 4 - don't add status=5 for "requested"
|
|
78
|
+
|
|
79
|
+
**Violated in this chat:**
|
|
80
|
+
- Initially added status=5 for "requested" state
|
|
81
|
+
- This value doesn't exist in the protocol, only 0/1/4 come from OCP
|
|
82
|
+
|
|
83
|
+
**Correction:** ✅ FIXED - Removed status=5, only track OCP's response (0/1/4)
|
|
84
|
+
|
|
85
|
+
### 5. Addressing Confusion
|
|
86
|
+
**Rule:** Always verify address mappings before implementing
|
|
87
|
+
- 15 = Broadcast
|
|
88
|
+
- 16 = OCP
|
|
89
|
+
- Got this backwards initially, causing confusion
|
|
90
|
+
|
|
91
|
+
**Lesson:** When dealing with protocol constants, explicitly verify from working examples first
|
|
92
|
+
|
|
93
|
+
### 6. Board Access Pattern (CRITICAL)
|
|
94
|
+
**Rule:** Never “rebind” / “re-initialize” the singleton board instance to a local variable (e.g., `const board = sys.board as IntelliCenterBoard`)
|
|
95
|
+
- Boards are initialized once at app startup
|
|
96
|
+
- Access the board via `sys.board` directly
|
|
97
|
+
- Persistent config/state should be accessed via `sys` (poolConfig.json) and `state` (poolState.json), not via cached board references
|
|
98
|
+
|
|
99
|
+
**Violated in this chat:**
|
|
100
|
+
- `EquipmentStateMessage.ts` used `const board = sys.board as IntelliCenterBoard` inside message handling
|
|
101
|
+
|
|
102
|
+
**Correction:** ✅ FIXED - Use `sys.board` directly without local rebinding
|
|
103
|
+
|
|
104
|
+
### 7. Controller-Specific Overrides (CRITICAL): SystemBoard.ts must be controller-agnostic
|
|
105
|
+
**Rule:** Do NOT add controller-specific branching (e.g., `if (sys.controllerType === ControllerType.IntelliCenter) ...`, `if (sys.equipment.isIntellicenterV3) ...`) inside shared base command classes in `controller/boards/SystemBoard.ts`.
|
|
106
|
+
|
|
107
|
+
- **Why:** The default `byteValueMaps` in `SystemBoard.ts` include generic/*Touch-style defaults (e.g., `heatModes`) which are **wrong for IntelliCenter v3.004+**.
|
|
108
|
+
- **Correct pattern:** Each controller board overrides the relevant command object (e.g., `IntelliCenterBoard` uses `IntelliCenterHeaterCommands extends HeaterCommands`) and implements controller-accurate behavior there.
|
|
109
|
+
- **Call-site rule:** Always call `sys.board.heaters.updateHeaterServices()` (or the appropriate `sys.board.<domain>` command), and rely on polymorphism to dispatch to the controller-specific implementation.
|
|
110
|
+
|
|
111
|
+
**Bug example (dashPanel heat mode labels):**
|
|
112
|
+
- `/config/options/heaters` returns `sys.board.valueMaps.heatModes.toArray()`
|
|
113
|
+
- If IntelliCenter-specific heater service mapping isn’t used, the UI can map numeric heatMode values to the wrong names (e.g., mapping `5` to “Solar Only” instead of “UltraTemp Only” on IC v3).
|
|
114
|
+
|
|
115
|
+
**Fix pattern:**
|
|
116
|
+
- Put IntelliCenter mapping in `IntelliCenterHeaterCommands.updateHeaterServices()` (already present in `controller/boards/IntelliCenterBoard.ts`)
|
|
117
|
+
- Keep `HeaterCommands.updateHeaterServices()` in `SystemBoard.ts` generic for non-IntelliCenter controllers
|
|
118
|
+
|
|
119
|
+
**Same pattern for body picklists:**
|
|
120
|
+
- Keep `BodyCommands.getHeatSources()` / `getHeatModes()` in `SystemBoard.ts` generic.
|
|
121
|
+
- Any IntelliCenter v3-specific filtering (e.g., suppressing `solarpref` / `ultratemppref` options for body-level mode picklists) must live in `IntelliCenterBodyCommands` in `controller/boards/IntelliCenterBoard.ts`.
|
|
122
|
+
|
|
123
|
+
**Generalization:** This applies to *anything* that is overridden by controller boards (IntelliCenter, EasyTouch, IntelliTouch, AquaLink, SunTouch, Nixie, etc.).
|
|
124
|
+
If you find yourself writing `if (sys.controllerType === ...)` inside `SystemBoard.ts`, that is almost always a design smell—create/extend the controller-specific command class in the appropriate board file and keep `SystemBoard.ts` purely generic.
|
|
125
|
+
|
|
126
|
+
## Analysis Patterns
|
|
127
|
+
|
|
128
|
+
### 6. Examine Working State, Not Just Failures
|
|
129
|
+
**Rule:** When debugging, look for WORKING periods in captures
|
|
130
|
+
- User insight: "Packet #576 shows byte[2]=1 (REGISTERED)"
|
|
131
|
+
- This means njsPC WAS working at that point
|
|
132
|
+
- Should analyze: What changed between #576 (working) and #999 (failed)?
|
|
133
|
+
- Should check: Are config requests working between #576 and #999?
|
|
134
|
+
|
|
135
|
+
**Violated in this chat:**
|
|
136
|
+
- Focused on failure at packet #999
|
|
137
|
+
- Didn't analyze the working period from #576 to #999
|
|
138
|
+
- Missed opportunity to see if config WAS loading successfully
|
|
139
|
+
|
|
140
|
+
**Correction needed:** Analyze packets #576-#999 to see if config requests worked
|
|
141
|
+
|
|
142
|
+
### 7. Source of Truth Identification
|
|
143
|
+
**Rule:** Identify authoritative data sources in protocol
|
|
144
|
+
- User insight: "Action 217 is the source of truth"
|
|
145
|
+
- Should have recognized this earlier from captures
|
|
146
|
+
|
|
147
|
+
**Lesson:** When multiple messages contain similar data, identify which is authoritative
|
|
148
|
+
|
|
149
|
+
### 8. Temperature Debugging: Use Time-Series Sources, Not `poolState.json`
|
|
150
|
+
**Rule:** When debugging temperature-driven behavior (solar/heater thresholds), do NOT rely on `poolState.json` for trends.
|
|
151
|
+
- `poolState.json` represents the **last/current** state snapshot, not a time series.
|
|
152
|
+
- To understand when thresholds were crossed, extract temps from **packet logs** (and/or other raw-sample logs that capture temps over time).
|
|
153
|
+
- Use the time-series to correlate: **AIR/WATER/SOLAR temps** ↔ **heater/valve commands** ↔ **triggering events**.
|
|
154
|
+
|
|
155
|
+
## Communication Patterns
|
|
156
|
+
|
|
157
|
+
### 8. Documentation Organization
|
|
158
|
+
**Rule:** ONE markdown file per topic in `.plan/` directory
|
|
159
|
+
- Before creating new `.plan/*.md` file, check if topic already covered
|
|
160
|
+
- Update existing file rather than creating duplicate
|
|
161
|
+
- If splitting needed, cross-reference between files
|
|
162
|
+
- Periodically review and delete outdated/superseded files
|
|
163
|
+
- **When completing work:** Update the EXISTING plan file with status, don't create new "_COMPLETE" files
|
|
164
|
+
|
|
165
|
+
**Violated in this chat (Dec 10, 2025):**
|
|
166
|
+
- Created `V3_ARCHITECTURE_FIX_COMPLETE.md` immediately after establishing "one file per topic" rule
|
|
167
|
+
- Should have updated existing `V3_REGISTRATION_FIX_PLAN.md` instead
|
|
168
|
+
- User feedback: "stop creating new .md files in .plan. Consolidate with the latest known info. follow the new rules in agent.md"
|
|
169
|
+
|
|
170
|
+
**Correction:** ✅ FIXED - Deleted duplicate file, updated existing plan file with completion status
|
|
171
|
+
|
|
172
|
+
**Hierarchy:**
|
|
173
|
+
1. **Master docs** (keep these):
|
|
174
|
+
- `200-intellicenter-v3-index.md` - Main implementation guide
|
|
175
|
+
- `204-intellicenter-v3-protocol.md` - Complete protocol reference / findings summary
|
|
176
|
+
- `203-intellicenter-action-registry.md` - Action code reference
|
|
177
|
+
|
|
178
|
+
2. **Active work** (current tasks):
|
|
179
|
+
- `V3_REGISTRATION_FIX_PLAN.md` - Current work plan
|
|
180
|
+
|
|
181
|
+
3. **Key insights** (important discoveries):
|
|
182
|
+
- `V3_ACTION_217_DISCOVERY.md` - Action 217 as source of truth
|
|
183
|
+
|
|
184
|
+
4. **Reference** (technical analysis):
|
|
185
|
+
- `ACTION_204_V1_VS_V3_ANALYSIS.md` - Module detection
|
|
186
|
+
|
|
187
|
+
**Delete:** Temporary files, superseded analysis, old troubleshooting
|
|
188
|
+
|
|
189
|
+
### 9. Meta-Rule: Ask About Documentation
|
|
190
|
+
**Rule:** When user provides correction or clarification, ASK:
|
|
191
|
+
> "Should I add this to AGENTS.md for future reference?"
|
|
192
|
+
|
|
193
|
+
This creates a feedback loop for continuous improvement.
|
|
194
|
+
|
|
195
|
+
### 10. Validate Assumptions Before Implementation
|
|
196
|
+
**Rule:** When making architectural changes, state assumptions and ask for validation
|
|
197
|
+
- Example: "I'm planning to use local variables for this. Is that appropriate?"
|
|
198
|
+
- Don't assume patterns - verify first
|
|
199
|
+
|
|
200
|
+
### 10.1 UI Error Attribution (dashPanel)
|
|
201
|
+
**Rule:** Treat dashPanel errors as **UI-initiated command failures only**.
|
|
202
|
+
- dashPanel only shows errors for actions a user initiates from dashPanel (e.g., a “turn on/off” click).
|
|
203
|
+
- It will NOT surface retries/timeouts from njsPC background flows (config polling, heartbeats, etc.).
|
|
204
|
+
- Therefore: absence of dashPanel errors is **not evidence** that the underlying protocol traffic is healthy; always confirm via packet logs and njsPC logs.
|
|
205
|
+
|
|
206
|
+
### 10.2 IntelliCenter v3 Source-of-Truth: Do NOT Process Wireless→OCP Requests
|
|
207
|
+
**Rule:** For IntelliCenter v3 (v3.004+), the **OCP (addr 16)** is the **only source of truth** for state/config changes.
|
|
208
|
+
|
|
209
|
+
- **Do not process** packets that are **Wireless/ICP/Indoor → OCP** (e.g. src=36 dest=16) as if they were authoritative.
|
|
210
|
+
- These are **requests**; the OCP may ACK or ignore them, and the final state can differ.
|
|
211
|
+
- Only update state/config from **OCP-originated** messages (source=16), and preferably from the established authoritative message types:
|
|
212
|
+
- Config/state broadcasts/responses (e.g., Action 30 / Action 204 / other status broadcasts)
|
|
213
|
+
- NOT from third-party device requests
|
|
214
|
+
|
|
215
|
+
**Implementation note:** In `Messages.ts`, gate IntelliCenter Action 168 handling to ignore non-OCP sources (especially src!=16 dest==16).
|
|
216
|
+
|
|
217
|
+
### 11. Modifying Existing Rules
|
|
218
|
+
**Rule:** Never modify, remove, or contradict existing rules in AGENTS.md without explicit user approval
|
|
219
|
+
- When a situation arises that conflicts with an existing rule, STOP and ask the user
|
|
220
|
+
- Present: "Existing rule X says Y, but I think Z. Which should I follow?"
|
|
221
|
+
- Let user decide whether to update the rule, create an exception, or follow existing rule
|
|
222
|
+
- Document the resolution in AGENTS.md
|
|
223
|
+
|
|
224
|
+
**Rationale:** Rules in AGENTS.md represent learned patterns from user feedback. Changing them without approval risks losing important context.
|
|
225
|
+
|
|
226
|
+
## Protocol Discovery Patterns
|
|
227
|
+
|
|
228
|
+
### 12. Hardware Protocol Work Requires User Collaboration
|
|
229
|
+
**Rule:** Don't guess protocol behavior - ask user to test
|
|
230
|
+
- We can't test with actual hardware
|
|
231
|
+
- User has the hardware and can capture real behavior
|
|
232
|
+
- Iterate based on real captures, not assumptions
|
|
233
|
+
|
|
234
|
+
**This chat did well:** Multiple capture iterations with user feedback
|
|
235
|
+
|
|
236
|
+
### 13. Incremental Understanding
|
|
237
|
+
**Rule:** Protocol understanding evolves through captures
|
|
238
|
+
- Initial understanding: "Action 179 is proactive heartbeat" ✗
|
|
239
|
+
- Corrected: "Action 179 is request/response from OCP" ✓
|
|
240
|
+
- User observation: "Wireless sends Action 180 to OCP, not broadcast"
|
|
241
|
+
|
|
242
|
+
**Lesson:** Be ready to revise understanding as new evidence emerges
|
|
243
|
+
|
|
244
|
+
## Current Issues to Address
|
|
245
|
+
|
|
246
|
+
### Issue 1: Action 217 Handler Location ✅ RESOLVED
|
|
247
|
+
- **Problem:** Logic in Messages.ts
|
|
248
|
+
- **Solution:** Moved to EquipmentStateMessage.ts
|
|
249
|
+
- **Status:** Complete
|
|
250
|
+
|
|
251
|
+
### Issue 2: Registration State Storage ✅ RESOLVED
|
|
252
|
+
- **Problem:** Using local class variables
|
|
253
|
+
- **Solution:** Stored in poolState.json via state.equipment.registration
|
|
254
|
+
- **Status:** Complete
|
|
255
|
+
|
|
256
|
+
### Issue 3: Duplicate Registration Flags ✅ RESOLVED
|
|
257
|
+
- **Problem:** Two flags when one suffices
|
|
258
|
+
- **Solution:** Single state.equipment.registration status (values: 0/1/4) exposed via accessor (persisted JSON is internal)
|
|
259
|
+
- **Status:** Complete
|
|
260
|
+
|
|
261
|
+
### Issue 4: Registration Status != Config Working (CRITICAL)
|
|
262
|
+
- **Problem:** byte[2]=1 (registered) doesn't mean config is loading
|
|
263
|
+
- **Evidence:** Packets #576-#999 showed byte[2]=1, heartbeat working, but ZERO Action 30 responses
|
|
264
|
+
- **Analysis:**
|
|
265
|
+
- OCP acknowledged njsPC (Action 179/180 heartbeat working)
|
|
266
|
+
- njsPC sent 462 Action 222 config requests
|
|
267
|
+
- OCP responded to NONE of them
|
|
268
|
+
- Registration alone is not sufficient for config loading
|
|
269
|
+
- **Question:** What else is required beyond registration?
|
|
270
|
+
- **Priority:** CRITICAL (blocks entire feature)
|
|
271
|
+
|
|
272
|
+
## Questions for Future
|
|
273
|
+
|
|
274
|
+
1. What triggers config responses beyond registration?
|
|
275
|
+
2. Why does byte[2]=1 not enable config loading?
|
|
276
|
+
3. Is there a timing window after registration?
|
|
277
|
+
4. Does OCP need fresh registration each session (not remember from previous)?
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## Quick Reference for Debugging
|
|
282
|
+
|
|
283
|
+
### 14. Always Check .plan/ First
|
|
284
|
+
**Rule:** Before debugging IntelliCenter v3 issues, read:
|
|
285
|
+
- `.plan/INDEX.md` - Protocol documentation index (then IntelliCenter: `.plan/200-intellicenter-v3-index.md`)
|
|
286
|
+
- `.plan/203-intellicenter-action-registry.md` - Action code meanings
|
|
287
|
+
- `.plan/204-intellicenter-v3-protocol.md` - Protocol findings summary
|
|
288
|
+
|
|
289
|
+
### 14.1 Packet Header DEST/SRC Order (CRITICAL)
|
|
290
|
+
**Rule:** The packet header is:
|
|
291
|
+
`[165, 1, DEST, SRC, ACTION, LEN]`
|
|
292
|
+
|
|
293
|
+
**Do NOT grep/read this backwards.** DEST comes before SRC in the header, for every packet.
|
|
294
|
+
|
|
295
|
+
**Examples:**
|
|
296
|
+
- **Wireless → OCP command**: `DEST=16`, `SRC=36` → header starts with **`[165, 1, 16, 36, ...]`**
|
|
297
|
+
- **OCP → Wireless ACK**: `DEST=36`, `SRC=16` → header starts with **`[165, 1, 36, 16, ...]`**
|
|
298
|
+
|
|
299
|
+
### 15. Message Flow Architecture
|
|
300
|
+
**Packet → Messages.ts (router) → Handler Files:**
|
|
301
|
+
|
|
302
|
+
```
|
|
303
|
+
┌─────────────────┐
|
|
304
|
+
│ RS-485 Packet │
|
|
305
|
+
└────────┬────────┘
|
|
306
|
+
▼
|
|
307
|
+
┌─────────────────┐
|
|
308
|
+
│ Messages.ts │ ← ROUTER ONLY, no business logic!
|
|
309
|
+
│ (by action #) │
|
|
310
|
+
└────────┬────────┘
|
|
311
|
+
▼
|
|
312
|
+
┌─────────────────────────────────────────────────────────┐
|
|
313
|
+
│ Action 2/204 → EquipmentStateMessage.ts (status) │
|
|
314
|
+
│ Action 30 → ConfigMessage.ts → category handlers │
|
|
315
|
+
│ Action 168 → ExternalMessage.ts (wireless changes) │
|
|
316
|
+
│ Action 184 → EquipmentStateMessage.ts (v3 circuits) │
|
|
317
|
+
│ Action 217 → EquipmentStateMessage.ts (device list) │
|
|
318
|
+
│ Action 251/253→ Registration handshake │
|
|
319
|
+
│ Chlorinator → ChlorinatorStateMessage.ts │
|
|
320
|
+
│ Pump protocol → PumpStateMessage.ts │
|
|
321
|
+
└─────────────────────────────────────────────────────────┘
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
**Config Handlers (Action 30, routed by payload byte[0]):**
|
|
325
|
+
|
|
326
|
+
| Category | Handler | Purpose |
|
|
327
|
+
|----------|---------|---------|
|
|
328
|
+
| 0 | OptionsMessage | System options |
|
|
329
|
+
| 1 | CircuitMessage | Circuit config |
|
|
330
|
+
| 2 | FeatureMessage | Feature config |
|
|
331
|
+
| 3 | ScheduleMessage | Schedule times/days |
|
|
332
|
+
| 4 | PumpMessage | Pump config |
|
|
333
|
+
| 5 | RemoteMessage | Remote buttons |
|
|
334
|
+
| 6 | CircuitGroupMessage | Light/circuit groups |
|
|
335
|
+
| 7 | ChlorinatorMessage | Chlorinator settings |
|
|
336
|
+
| 8 | IntellichemMessage | Chemistry controller |
|
|
337
|
+
| 9 | ValveMessage | Valve assignments |
|
|
338
|
+
| 10 | HeaterMessage | Heater config |
|
|
339
|
+
| 11 | SecurityMessage | PIN codes |
|
|
340
|
+
| 12 | GeneralMessage | Pool name, location |
|
|
341
|
+
| 13 | EquipmentMessage | Body/equipment config |
|
|
342
|
+
| 14 | CoverMessage | Pool covers |
|
|
343
|
+
|
|
344
|
+
**External Message Handlers (Action 168, routed by payload byte[0]):**
|
|
345
|
+
|
|
346
|
+
| Case | Handler Method | Purpose |
|
|
347
|
+
|------|----------------|---------|
|
|
348
|
+
| 0 | processTempSettings | Setpoints/heat mode |
|
|
349
|
+
| 1 | processCircuit | Circuit changes |
|
|
350
|
+
| 2 | processFeature | Feature changes |
|
|
351
|
+
| 3 | processSchedules | Schedule changes |
|
|
352
|
+
| 4 | processPump | Pump changes |
|
|
353
|
+
| 7 | processChlorinator | Chlorinator changes |
|
|
354
|
+
| 10 | processHeater | Heater changes |
|
|
355
|
+
|
|
356
|
+
### 16. v3.004 Development Baseline (CRITICAL)
|
|
357
|
+
**Rule:** All v3.004+ IntelliCenter changes started on **November 26, 2025**. Everything in the codebase before that date was working correctly for v1.x.
|
|
358
|
+
|
|
359
|
+
**Implications:**
|
|
360
|
+
- When investigating v3 issues, only look at commits from 11/26/2025 onwards
|
|
361
|
+
- Any code that existed before 11/26/2025 should be assumed correct for v1.x
|
|
362
|
+
- v3 changes MUST be gated behind `sys.equipment.isIntellicenterV3` to avoid breaking v1
|
|
363
|
+
- Do NOT modify pre-11/26 code paths without explicit approval
|
|
364
|
+
|
|
365
|
+
**Git command to see v3 changes:**
|
|
366
|
+
```bash
|
|
367
|
+
git log --oneline --since="2025-11-26" -- <file>
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
### 17. v3.004 Endianness Pattern (CRITICAL)
|
|
371
|
+
**Rule:** v3.004 uses BIG-ENDIAN for 16-bit time values; v1.x uses LITTLE-ENDIAN.
|
|
372
|
+
|
|
373
|
+
**Check:** Any `extractPayloadInt()` for schedule/time values needs v3 handling.
|
|
374
|
+
|
|
375
|
+
**Fix pattern:**
|
|
376
|
+
```typescript
|
|
377
|
+
if (sys.controllerType === ControllerType.IntelliCenter && sys.equipment.isIntellicenterV3) {
|
|
378
|
+
time = msg.extractPayloadIntBE(offset); // Big-endian for v3.004+
|
|
379
|
+
} else {
|
|
380
|
+
time = msg.extractPayloadInt(offset); // Little-endian for v1.x
|
|
381
|
+
}
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
**Already fixed:**
|
|
385
|
+
- `ScheduleMessage.processStartTimes()` - schedule start times
|
|
386
|
+
- `ScheduleMessage.processEndTimes()` - schedule end times
|
|
387
|
+
- `ExternalMessage.processSchedules()` - wireless schedule changes
|
|
388
|
+
|
|
389
|
+
**Check for similar issues in:** Any message handler parsing 16-bit time values.
|
|
390
|
+
|
|
391
|
+
### 18. State Sync Pattern
|
|
392
|
+
**Rule:** When config properties are set, ALWAYS sync corresponding state properties.
|
|
393
|
+
|
|
394
|
+
**Bug pattern:** Config property set but state not synced → UI shows undefined/blank.
|
|
395
|
+
|
|
396
|
+
**Example (chlorinator name bug we fixed):**
|
|
397
|
+
```typescript
|
|
398
|
+
// BAD - name set in config but never synced to state
|
|
399
|
+
chlor.name = "Chlorinator 1";
|
|
400
|
+
// schlor.name never set → dashPanel shows blank name!
|
|
401
|
+
|
|
402
|
+
// GOOD - always sync config properties to state
|
|
403
|
+
chlor.name = "Chlorinator 1";
|
|
404
|
+
schlor.name = chlor.name; // ← Don't forget this!
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
**Checklist when reviewing config handlers:**
|
|
408
|
+
1. Find all `chlor.property = value` assignments
|
|
409
|
+
2. Verify each has corresponding `schlor.property = chlor.property`
|
|
410
|
+
3. Check for default values when property might be undefined
|
|
411
|
+
|
|
412
|
+
### 19. Finding Configuration Data for Debugging
|
|
413
|
+
**Rule:** Feature/circuit names and IDs are in configuration files, not guesswork.
|
|
414
|
+
|
|
415
|
+
**Locations:**
|
|
416
|
+
- Live system: `./data/poolConfig.json` and `./data/poolState.json`
|
|
417
|
+
- Replay captures: `[replayname]/njspc/data/poolConfig.json` and `poolState.json`
|
|
418
|
+
|
|
419
|
+
**Key ID ranges (IntelliCenter):**
|
|
420
|
+
- Circuits: 1-40 (varies by system)
|
|
421
|
+
- Features: Start at 129 (`sys.board.equipmentIds.features.start`)
|
|
422
|
+
- Circuit Groups: Start at 192 (`sys.board.equipmentIds.circuitGroups.start`)
|
|
423
|
+
|
|
424
|
+
**Example lookup:**
|
|
425
|
+
```json
|
|
426
|
+
// From poolConfig.json
|
|
427
|
+
"features": [
|
|
428
|
+
{ "id": 129, "name": "Test_Feature" },
|
|
429
|
+
{ "id": 130, "name": "Air Blower222" },
|
|
430
|
+
{ "id": 131, "name": "TestF2" }
|
|
431
|
+
]
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
### 20. Packet Log Analysis
|
|
435
|
+
**Rule:** When analyzing packet captures, decode payloads systematically.
|
|
436
|
+
|
|
437
|
+
**CRITICAL: Always include packet IDs when discussing packets.** The `id` field in each packet log entry is essential for:
|
|
438
|
+
- Cross-referencing with user observations
|
|
439
|
+
- Correlating request/response pairs
|
|
440
|
+
- Debugging timing issues
|
|
441
|
+
- Reproducing specific scenarios
|
|
442
|
+
|
|
443
|
+
**Format for discussing packets:**
|
|
444
|
+
```
|
|
445
|
+
#2605 23:09:49 Action30/15 byte9=3 features: ['129', '130'] <-- CONFIG RESPONSE
|
|
446
|
+
#2607 23:09:52 Action204 byte19=2 features: ['130'] <-- OVERWRITES!
|
|
447
|
+
```
|
|
448
|
+
Always prefix with `#` and packet ID.
|
|
449
|
+
|
|
450
|
+
**Packet structure:** `[header][payload][checksum]`
|
|
451
|
+
- Header: `[165, 1, dest, src, action, length]`
|
|
452
|
+
- Action codes: See `.plan/203-intellicenter-action-registry.md`
|
|
453
|
+
|
|
454
|
+
**Common analysis steps:**
|
|
455
|
+
1. Filter by action code: `grep "action\":168"` for external messages
|
|
456
|
+
2. Decode payload bytes using handler code as reference
|
|
457
|
+
3. Check timestamps for sequence/timing issues
|
|
458
|
+
4. Look for request/response pairs (outbound `dir":"out"` → inbound `dir":"in"`)
|
|
459
|
+
5. **Always extract and display packet IDs** in any analysis output
|
|
460
|
+
|
|
461
|
+
**Decoding times (v3.004 big-endian):**
|
|
462
|
+
- Two bytes `[hi, lo]` → `hi * 256 + lo` = minutes since midnight
|
|
463
|
+
- Example: `[2, 33]` → `2*256 + 33 = 545` → 9:05 AM
|
|
464
|
+
|
|
465
|
+
### 21. v3.004+ Action 184 Circuit Control (CRITICAL)
|
|
466
|
+
**Rule:** v3.004+ uses Action 184 for circuit control, NOT Action 168.
|
|
467
|
+
|
|
468
|
+
**Problem:** When njsPC sends Action 168 to control circuits on v3.004+, OCP accepts it briefly then reverts the state. The Wireless remote uses Action 184, which OCP accepts permanently.
|
|
469
|
+
|
|
470
|
+
**Solution:**
|
|
471
|
+
- Each circuit has a unique `targetId` (16-bit value stored in poolConfig.json)
|
|
472
|
+
- njsPC learns Target IDs from OCP Action 184 broadcasts
|
|
473
|
+
- When controlling circuits, njsPC sends Action 184 with the learned Target ID
|
|
474
|
+
|
|
475
|
+
**Action 184 Payload (10 bytes):**
|
|
476
|
+
```
|
|
477
|
+
[channelHi, channelLo, seq, format, targetHi, targetLo, state, 0, 0, 0]
|
|
478
|
+
├─ Channel: 104,143 (default) or circuit-specific (e.g., 108,225 for Pool)
|
|
479
|
+
├─ Target: Circuit's unique ID (e.g., 168,237 for Spa, 108,225 for Pool)
|
|
480
|
+
└─ State: 0=OFF, 1=ON
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
**Known Target IDs (user's system):**
|
|
484
|
+
| Circuit | Target ID | Hex |
|
|
485
|
+
|---------|-----------|-----|
|
|
486
|
+
| Spa (circuit 1) | 168,237 | 0xA8ED |
|
|
487
|
+
| Pool (circuit 6) | 108,225 | 0x6CE1 |
|
|
488
|
+
| Body status | 212,182 | 0xD4B6 (not a circuit) |
|
|
489
|
+
|
|
490
|
+
**Learning strategies:**
|
|
491
|
+
1. **Channel=Target pattern**: When bytes 0-1 equal bytes 4-5, identifies circuit
|
|
492
|
+
2. **Unique state match**: When only one circuit matches broadcast state (ON/OFF)
|
|
493
|
+
3. **State correlation**: Schedule/automation triggers state change → learn mapping
|
|
494
|
+
|
|
495
|
+
**Unknowns (to investigate):**
|
|
496
|
+
- How Target IDs are assigned (hardware serial? config order?)
|
|
497
|
+
- Whether Target IDs can change (suspected: stable)
|
|
498
|
+
- Full decode of body status (212,182) payload
|
|
499
|
+
|
|
500
|
+
**Code locations:**
|
|
501
|
+
- `Circuit.targetId` property: `controller/Equipment.ts`
|
|
502
|
+
- Learn from broadcasts: `EquipmentStateMessage.ts` (case 184)
|
|
503
|
+
- Send commands: `IntelliCenterBoard.createAction184Message()`
|
|
504
|
+
- Circuit control: `IntelliCenterBoard.setCircuitStateAsync()`
|
|
505
|
+
|
|
506
|
+
### 22. v3.004+ Action 168 vs Action 30 Offset Difference (CRITICAL)
|
|
507
|
+
**Rule:** v3.004 Wireless Action 168 type 0 has DIFFERENT byte offsets than Action 30 type 0!
|
|
508
|
+
|
|
509
|
+
**Problem:** When parsing body setpoints/heat modes from Wireless remote messages (Action 168 type 0), using Action 30 offsets (19-24) produces garbage values.
|
|
510
|
+
|
|
511
|
+
**Root Cause:** The Wireless Action 168 payload has an extra byte in early positions, shifting all indices by +1.
|
|
512
|
+
|
|
513
|
+
**Correct Offsets:**
|
|
514
|
+
| Field | Action 30 | Action 168 |
|
|
515
|
+
|-------|-----------|------------|
|
|
516
|
+
| Pool setpoint | 19 | **20** |
|
|
517
|
+
| Pool cool | 20 | **21** |
|
|
518
|
+
| Spa setpoint | 21 | **22** |
|
|
519
|
+
| Spa cool | 22 | **23** |
|
|
520
|
+
| Pool mode | 23 | **24** |
|
|
521
|
+
| Spa mode | 24 | **25** |
|
|
522
|
+
|
|
523
|
+
**Code Location:** `ExternalMessage.ts` → `processTempSettings()` detects v3 and uses correct offsets.
|
|
524
|
+
|
|
525
|
+
**Verified from captures:** Replay 30 (5 packets), Replay 48 (multiple packets).
|
|
526
|
+
|
|
527
|
+
### 23. Protocol Documentation Structure
|
|
528
|
+
|
|
529
|
+
**Rule:** Detailed packet/flow documentation lives in `.plan/` directory, organized by controller type and equipment type.
|
|
530
|
+
|
|
531
|
+
**ALWAYS read `.plan/INDEX.md` first** when working on protocol issues. It indexes all protocol documentation (including IntelliCenter: `.plan/200-intellicenter-v3-index.md`).
|
|
532
|
+
|
|
533
|
+
#### Protocol Files by Controller Type
|
|
534
|
+
|
|
535
|
+
**IntelliCenter:**
|
|
536
|
+
- `.plan/201-intellicenter-circuits-features.md` - Circuits, features, groups (current)
|
|
537
|
+
- `.plan/203-intellicenter-action-registry.md` - Action code reference
|
|
538
|
+
- `.plan/204-intellicenter-v3-protocol.md` - Protocol findings summary
|
|
539
|
+
- `.plan/200-intellicenter-v3-index.md` - IntelliCenter v3.004 index (linked from `.plan/INDEX.md`)
|
|
540
|
+
|
|
541
|
+
**Active protocol files:**
|
|
542
|
+
- `201-intellicenter-circuits-features.md` - Circuits, features, groups
|
|
543
|
+
- `202-intellicenter-bodies-temps.md` - Body temps/setpoints/heat modes
|
|
544
|
+
|
|
545
|
+
**Future files (create as needed):**
|
|
546
|
+
- `INTELLICENTER_PUMPS_PROTOCOL.md` - Pump control/status
|
|
547
|
+
- `INTELLICENTER_HEATERS_PROTOCOL.md` - Heater control/status
|
|
548
|
+
- `INTELLICENTER_SCHEDULES_PROTOCOL.md` - Schedule management
|
|
549
|
+
- `INTELLICENTER_CHEMISTRY_PROTOCOL.md` - IntelliChem/chlorinator
|
|
550
|
+
|
|
551
|
+
**Other Controllers (create as needed):**
|
|
552
|
+
- `INTELLITOUCH_*.md` - IntelliTouch protocols
|
|
553
|
+
- `EASYTOUCH_*.md` - EasyTouch protocols
|
|
554
|
+
- `NIXIE_*.md` - Nixie/virtual protocols
|
|
555
|
+
|
|
556
|
+
#### File Organization
|
|
557
|
+
|
|
558
|
+
Each protocol file should include:
|
|
559
|
+
1. **Message Types Summary** - Table of all relevant actions
|
|
560
|
+
2. **Complete Flow Diagrams** - Step-by-step packet sequences
|
|
561
|
+
3. **Byte Offset Tables** - Where data lives in payloads
|
|
562
|
+
4. **Version Differences** - v1.x vs v3.004+ variations
|
|
563
|
+
5. **Handler Routing** - Which code files process each message
|
|
564
|
+
6. **Troubleshooting** - Common issues and solutions
|
|
565
|
+
|
|
566
|
+
#### Quick Reference: Circuits/Features (v3.004+)
|
|
567
|
+
|
|
568
|
+
**Authoritative source:** Action 30 case 15 (NOT Action 204!)
|
|
569
|
+
|
|
570
|
+
**Key v3.004+ bug:** Action 204 byte 19 contains STALE feature state. Must skip processing.
|
|
571
|
+
|
|
572
|
+
**See:** `.plan/201-intellicenter-circuits-features.md` for full details.
|
|
573
|
+
|
|
574
|
+
#### Quick Reference: Bodies/Setpoints (v3.004+)
|
|
575
|
+
|
|
576
|
+
**⚠️ CRITICAL: Action 168 Wireless has DIFFERENT offsets than Action 30!**
|
|
577
|
+
|
|
578
|
+
| Field | Action 30 (config) | Action 168 (Wireless) |
|
|
579
|
+
|-------|-------------------|----------------------|
|
|
580
|
+
| Pool setpoint | byte 19 | byte 20 |
|
|
581
|
+
| Pool cool | byte 20 | byte 21 |
|
|
582
|
+
| Spa setpoint | byte 21 | byte 22 |
|
|
583
|
+
| Spa cool | byte 22 | byte 23 |
|
|
584
|
+
| Pool mode | byte 23 | byte 24 |
|
|
585
|
+
| Spa mode | byte 24 | byte 25 |
|
|
586
|
+
|
|
587
|
+
**Root cause:** Wireless Action 168 payload has extra byte in early positions, shifting indices by +1.
|
|
588
|
+
|
|
589
|
+
**Heat mode valueMap fix:** Use `htypes.total > 1` to check for multi-heater setups (Solar+UltraTemp).
|
|
590
|
+
|
|
591
|
+
**See:** `.plan/202-intellicenter-bodies-temps.md` for full details.
|
|
592
|
+
|
|
593
|
+
---
|
|
594
|
+
|
|
595
|
+
**Last Updated:** December 16, 2025
|
|
596
|
+
**Source:** nodejs-poolController IntelliCenter v3.004 compatibility work
|
|
597
|
+
|