@johanwistbacka/node-red-contrib-sw-light 0.1.4

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,47 @@
1
+ # Changelog
2
+
3
+ ## 0.1.4 — scoped distribution candidate
4
+
5
+ - Changes npm identity to `@johanwistbacka/node-red-contrib-sw-light`.
6
+ - Keeps Node-RED type `sw-light`, display name SW Light and all runtime/editor behavior unchanged.
7
+ - Retains version 0.1.4 for the first publication under the new npm name.
8
+ - Documents replacing the unscoped package without installing both packages together.
9
+
10
+ The scoped candidate is not published yet. The unscoped 0.1.4 was already
11
+ published; the runtime verification notes below describe that implementation.
12
+
13
+
14
+ Runtime verification work carried forward from the unscoped 0.1.4.
15
+
16
+ - Identifies the reported constructor stack as the original 0.1.0 implementation.
17
+ - Verifies the existing lazy provider accessor fix on Node-RED 5.0.7 with HA contrib 0.80.3.
18
+ - Extends real-registry regression coverage to close while HA is unready.
19
+ - Documents explicit rejection/no replay of commands before readiness.
20
+
21
+ Runtime/editor behavior is unchanged from 0.1.3. The real host must verify
22
+ which source/version is executing; no new speculative API fix is claimed.
23
+
24
+ ## 0.1.3
25
+
26
+ Normal SemVer development candidate for npm `latest`, prepared but not yet published.
27
+
28
+ - Promotes the unchanged beta runtime/editor to a normal development version.
29
+ - Updates publication tag and installation/version documentation.
30
+ - Keeps the explicit 11-file public package allowlist.
31
+
32
+ The release remains experimental. Community Catalogue listing requires a
33
+ separate Flow Library submission/refresh after npm publication.
34
+
35
+ ## 0.1.3-beta.1
36
+
37
+ First public npm beta release.
38
+
39
+ - Controls one Home Assistant light directly: On, Off, Toggle and From msg.
40
+ - Applies automatic brightness limits, transitions and manual override protection.
41
+ - Supports optional Adaptive Lighting color-only apply.
42
+ - Retains lazy HA client resolution, reconnect handling and temporary structural diagnostics from 0.1.2.
43
+ - Adds beta publication metadata and an explicit public file allowlist.
44
+
45
+ Requires HA contrib exactly 0.80.3. Real Home Assistant runtime verification
46
+ with a confirmed installed version remains pending. No runtime behavior was
47
+ changed while preparing this beta candidate.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Studio Wistbacka
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,412 @@
1
+ # SW Light — v0.1.4
2
+
3
+ A reusable Node-RED node for one Home Assistant `light.*` entity. Select an
4
+ existing HA server, select a light, deploy in a **development** environment,
5
+ and select Action **On**, **Off**, **Toggle**, or **From msg**. Display name:
6
+ **SW Light**; flow type: `sw-light`.
7
+
8
+ The basic flow is **Trigger → SW Light → Home Assistant**. SW Light reads the
9
+ selected light, checks manual override and calls the HA light service itself.
10
+ No external HA Action node is required; the two outputs are for observation
11
+ and integration only. With a fixed Action, a normal timestamp Inject is enough.
12
+
13
+ ## Package identity and migration
14
+
15
+ The permanent npm package name is `@johanwistbacka/node-red-contrib-sw-light`.
16
+ The Node-RED type stays `sw-light` and its display name stays **SW Light**.
17
+ The scoped package starts at version 0.1.4; version numbers are independent
18
+ for each npm package name. No runtime/editor or HA integration behavior changes.
19
+
20
+ If upgrading from the old unscoped `node-red-contrib-sw-light`, export your
21
+ flows first. Remove the old package from the Node-RED user directory before
22
+ installing the scoped one; do not install both together because they register
23
+ the same node type. Use Palette Manager's package removal/installation controls
24
+ or, with Node-RED stopped, run these commands in its user directory:
25
+
26
+ ```sh
27
+ npm uninstall node-red-contrib-sw-light
28
+ npm install @johanwistbacka/node-red-contrib-sw-light@0.1.4
29
+ ```
30
+
31
+ Restart Node-RED and reload the editor. Existing flow nodes retain type
32
+ `sw-light`; verify the scoped package/version in Palette Manager before testing.
33
+ The scoped candidate is not published yet; registry installation becomes
34
+ available after its first authorized publication.
35
+
36
+ ## Compatibility and installation
37
+
38
+ Requires Node.js >=18.2.0, Node-RED >=3.1.1 and
39
+ `node-red-contrib-home-assistant-websocket` **exactly 0.80.3**. This initial
40
+ adapter is intentionally pinned because it uses that package's internal
41
+ shared-client module. It does not require the HA Node-RED companion integration.
42
+ HA contrib itself requires Home Assistant >=2023.12. Numeric bounds and
43
+ capabilities are read from the selected light.
44
+
45
+ This is an experimental development release. Local tests pass, but the runtime integration
46
+ has not yet been confirmed with the correct installed version in the real
47
+ Home Assistant environment. The adapter uses internal HA contrib APIs;
48
+ compatibility outside 0.80.3 is unsupported. Temporary structural diagnostics
49
+ are enabled. Test with a development light before relying on this node.
50
+
51
+ After this version is published, install it in your Node-RED user directory:
52
+
53
+ ```sh
54
+ npm install @johanwistbacka/node-red-contrib-sw-light@0.1.4
55
+ ```
56
+
57
+ After publication, the current development release is available under npm latest:
58
+
59
+ ```sh
60
+ npm install @johanwistbacka/node-red-contrib-sw-light@latest
61
+ ```
62
+
63
+ This candidate has been prepared locally and is not published yet. Restart
64
+ Node-RED after installation and verify the version in Palette Manager.
65
+
66
+ Select/create a Home Assistant server using the existing HA contrib config
67
+ node. A new server must be deployed before its entities are available in the
68
+ autocomplete. The text field also accepts an entity ID directly. This package
69
+ does not store tokens, create connections, change global context, or alter the
70
+ server config. Installation commands above are for your separate dev instance;
71
+ do not replace a working installation's versions without checking compatibility.
72
+
73
+ For edits, use npm's local directory installation/link or reinstall an archive
74
+ created with `npm pack`. Restart the isolated Node-RED runtime after changes.
75
+ There is no build step or additional runtime dependency.
76
+
77
+ The current development release uses the normal SemVer version `0.1.4` and
78
+ npm tag `latest` for Node-RED compatibility. This version remains experimental;
79
+ it is not a SemVer prerelease. Every installation/publication candidate needs a
80
+ unique version. Use `npm version patch --no-git-tag-version` for the next normal
81
+ development version. Separate prerelease builds may use `beta` tags. Git tags,
82
+ GitHub releases and npm publication require an explicit request.
83
+
84
+ Publishing to npm does not automatically add a node to the Community Catalogue.
85
+ After publication, submit the package through the
86
+ [Node-RED Flow Library](https://flows.nodered.org/add/node), or request a refresh
87
+ if it is already listed.
88
+
89
+ ## Temporary runtime diagnostics in 0.1.4
90
+
91
+ This build logs `[SW Light debug]` at construction and on the first input,
92
+ then when the relevant structure/readiness changes. It reports version,
93
+ server-ID presence, whether the server resolves, constructor/type checks,
94
+ expected method/property presence, provider version and client readiness.
95
+ It never logs server IDs, entity IDs, credentials, hosts, full objects or state
96
+ snapshots. Provider initialization errors are reduced to a generic message.
97
+
98
+ After installation, restart Node-RED and verify a log line containing
99
+ `[SW Light debug] version=0.1.4`. Inject once and retain the prefixed lines,
100
+ along with any error/stack. Those lines distinguish missing configuration,
101
+ a missing provider client and a connected client with unexpected methods.
102
+ The real installation problem remains unverified until this build is tested
103
+ there. Diagnostics are temporary and intended to be removed after diagnosis.
104
+
105
+ The adapter follows the accessor used by official Current State and Action,
106
+ using one Node module resolution anchored at `RED.settings.userDir`. HA contrib
107
+ 0.80.3 exposes no documented public third-party transport API or client method
108
+ on its server config node. Its internal dependency remains isolated and pinned;
109
+ there is no `require.cache` scan or search for alternate package copies.
110
+
111
+ ## Configuration
112
+
113
+ | Setting | Default | Meaning |
114
+ |---|---|---|
115
+ | Action | From msg | On/Off always choose that behavior; Toggle resolves the current state; From msg reads the message |
116
+ | On brightness | 94% | Used when no message brightness is supplied |
117
+ | Automatic maximum | 94% | Ceiling for every automatic ON/step |
118
+ | Minimum | 1% | Floor for nonzero brightness, including manual commands |
119
+ | On/off transition | 1s each | Sent only with light transition capability |
120
+ | Manual override | Enabled | Pause automatic commands while override is active |
121
+ | Detection | Brightness threshold + flag | Alternative: explicit flag only |
122
+ | Threshold | 100% | Lamp brightness at/above this value activates override |
123
+ | Color mode | Adaptive Lighting | No configured color sent during ordinary ON |
124
+ | Adaptive discovery | Enabled | Unique membership match in switch attributes |
125
+ | Adaptive apply on ON | Disabled | Optional color-only `adaptive_lighting.apply` |
126
+ | Respect adaptive manual control | Enabled | Skip apply when light is manually controlled |
127
+
128
+ Require `minimum <= on brightness <= automatic maximum`. When threshold
129
+ detection is enabled, automatic maximum must be below the threshold.
130
+ Transitions are 0–3600 seconds. Configuration errors prevent action calls.
131
+ Known lamp capabilities disable irrelevant fixed color choices in the editor;
132
+ runtime validation is authoritative.
133
+
134
+ Color modes: Adaptive Lighting, Keep current, Fixed Kelvin, Fixed mired, Lamp
135
+ default, Fixed color (`#rrggbb`). Kelvin and mired are normalized to Kelvin,
136
+ validated against the lamp's advertised limits and sent as
137
+ `color_temp_kelvin`. Fixed RGB translates to RGB, HS, XY, RGBW or RGBWW
138
+ according to capabilities. White channels are zero when expanding RGB.
139
+ White channels supplied explicitly are never silently discarded.
140
+
141
+ Keep current and Lamp default both omit configured color in v0.1. They use
142
+ HA/device restoration behavior; Keep current cannot guarantee that a device
143
+ or HA default profile preserves color across an OFF/ON cycle. Brightness
144
+ defaults still apply in every color mode.
145
+
146
+ ## Input contract
147
+
148
+ Properties are on the **top-level message**, not nested under `payload`:
149
+
150
+ ```json
151
+ { "action": "on" }
152
+ { "action": "off" }
153
+ { "action": "toggle" }
154
+ { "action": "on", "brightness_pct": 45, "transition": 3 }
155
+ { "action": "on", "color_temp_kelvin": 2200 }
156
+ { "action": "on", "source": "manual", "brightness_pct": 100 }
157
+ { "action": "on", "source": "manual", "manual_override": true }
158
+ { "action": "on", "source": "manual", "manual_override": false }
159
+ ```
160
+
161
+ With Action **From msg**, `msg.action` is required; `msg.auto_light.action`
162
+ is accepted when the top-level action property is absent. Top-level action
163
+ takes precedence, including invalid values (which produce a clear error).
164
+ Fixed Action settings ignore message action properties. From msg remains the
165
+ default for backward compatibility. `source` is `automatic` (default) or `manual`.
166
+ Manual source bypasses the automatic ceiling and automatic command blocking;
167
+ it does not automatically latch override. Use the boolean `manual_override`
168
+ with `source: "manual"` for an explicit latch or release. An invalid source or
169
+ flag produces an error before service calls.
170
+
171
+ Message values override configuration for that command. Within a conflicting
172
+ group the first supplied property in the following order wins; other properties
173
+ are reported in `ignored_conflicts`. **All supplied recognized values are
174
+ validated**, even those that lose precedence.
175
+
176
+ | Group, in precedence order | Accepted values |
177
+ |---|---|
178
+ | `brightness_pct`, `brightness`, `brightness_step_pct`, `brightness_step` | 0–100, integer 0–255, -100–100, integer -255–255 |
179
+ | `color_temp_kelvin`, `color_temp` | Integer 1000–40000 K; 25–1000 mired, then lamp limits |
180
+ | `rgbww_color`, `rgbw_color`, `rgb_color` | Arrays of 5, 4, 3 integer bytes 0–255 |
181
+ | `hs_color`, `xy_color`, `color_name` | [0–360,0–100], [0–1,0–1], HA color name |
182
+ | `transition` | 0–3600 seconds |
183
+
184
+ The complete color precedence follows the table top to bottom. `color_name`
185
+ is passed to HA for name resolution/validation, only on a color-capable lamp.
186
+ Unrecognized message fields are preserved and never forwarded as service data.
187
+ Scalar numeric strings are accepted (useful for config); empty strings, null,
188
+ booleans, NaN, infinity and values outside bounds are errors.
189
+
190
+ Absolute brightness is clamped to the configured nonzero minimum and source's
191
+ maximum. Steps resolve against the last observed HA brightness (zero when off),
192
+ then clamp to 0–100 and the configured bounds. Step commands fail when an ON
193
+ lamp's brightness is unknown. Resolved zero turns the light off; it passes the
194
+ same override protection as ordinary OFF. HA uses byte brightness; the automatic
195
+ ceiling rounds **down** so 94% sends at most 239/255 (93.7%). Other values round
196
+ to the nearest byte, so a 1% minimum becomes 3/255 (1.2%).
197
+
198
+ `toggle` resolves current ON to `light.turn_off`, and current OFF to
199
+ `light.turn_on` with ON defaults. Brightness and color parameters supplied with an OFF command or
200
+ an ON→OFF toggle are omitted with an unsupported event. Transition is filtered
201
+ using the light's `supported_features` bit 32. Unknown/missing color capabilities
202
+ are treated conservatively. RGB/HS can translate between supported RGB/HS/XY
203
+ modes. XY and white-channel representations need their matching native mode.
204
+
205
+ ## Manual override
206
+
207
+ **ON at 100% = manual override: automatic ON, OFF and Toggle do nothing.**
208
+ This hard guard requires brightness exactly **255/255**, so 94%, 95%, 99% and
209
+ 254/255 do not trigger it. It applies even if the optional threshold detector
210
+ is disabled or set to explicit-only. Lowering the light below 255 allows
211
+ automatic control again unless an additional configured override is active.
212
+
213
+ Threshold detection observes HA state, not requested brightness. The configured
214
+ threshold is converted to a byte; default 100% means exactly 255, not 254 rounded
215
+ for display. With defaults, automation uses 1–94%, and observed 100% activates
216
+ override. It pauses **all automatic commands**, including ON, OFF, toggle and
217
+ brightness zero, so an automatic ON cannot accidentally lower and release a
218
+ manual setting. A manually lowered observed brightness releases the threshold
219
+ flag. OFF releases all flags. Missing/unknown/unavailable state preserves flags
220
+ and rejects commands.
221
+
222
+ An explicit latch is independent of the threshold. It persists until explicit
223
+ release or observed OFF. A release flag clears both flags for its command;
224
+ subsequent threshold observations can reactivate override. Flags are in memory,
225
+ so redeployment rebuilds threshold state from HA but loses explicit latches.
226
+ This detector is separated from the planner for future HA-context-based origin
227
+ detection. `source` is the v0.1 command-origin contract.
228
+
229
+ ## Adaptive Lighting
230
+
231
+ Ordinary ON in Adaptive mode sends brightness and supported transition, **no
232
+ color or temperature**. Adaptive Lighting retains ownership of color. A message
233
+ color explicitly overrides that behavior for one command, and suppresses optional
234
+ apply even when the color is unsupported and filtered.
235
+
236
+ Manual switch selection takes precedence. Discovery uses the light's exact
237
+ membership in `attributes.configuration.lights` or `attributes.lights` on
238
+ switch entities with adaptive-like attributes. It only selects one unique
239
+ candidate; it does not infer names or configure HA. This is best-effort attribute
240
+ discovery, not a registry-backed guarantee that a switch belongs to the integration.
241
+ No actual installation was available for validating its attribute schema.
242
+ Use the switch picker/manual entity ID when discovery fails or is ambiguous.
243
+
244
+ Optional apply runs **after** successful turn-on, only for an enabled selected
245
+ switch, without explicit message color. It calls `adaptive_lighting.apply` with
246
+ `adapt_brightness: false`, `adapt_color: true`, `turn_on_lights: false`. It respects
247
+ `manual_control` / `manual_control_color` lists by default and never clears them,
248
+ turns on an adaptive switch or changes integration settings. No discovery is
249
+ required when apply is disabled: the ordinary HA light action works on its own.
250
+ An apply failure emits output 2 with `phase: "adaptive_apply"` and
251
+ `light_service_completed: true`; the successful light result still emits.
252
+
253
+ Adaptive Lighting may interpret explicit brightness commands as manual control
254
+ depending on its own `take_over_control` and related settings. Its background
255
+ brightness adaptation may also exceed this node's maximum; the ceiling only
256
+ applies to **SW Light's outgoing commands**. Configure the integration's own
257
+ brightness limits/ownership in your dev environment and test that combination.
258
+ SW Light never repeatedly corrects brightness or fights its adaptation.
259
+
260
+ ## Test the updated 0.1.4 in your HA Node-RED installation
261
+
262
+ 1. Save/export your current test flow. Transfer the rebuilt local
263
+ `johanwistbacka-node-red-contrib-sw-light-0.1.4.tgz` to the Node-RED host. Check that HA contrib
264
+ is 0.80.3; do not upgrade it for this task.
265
+ 2. In a terminal for that Node-RED instance, find **User directory** in its
266
+ startup log. Change to that directory (not a global npm directory) and run
267
+ `npm install ./johanwistbacka-node-red-contrib-sw-light-0.1.4.tgz --ignore-scripts --no-audit --no-fund`.
268
+ Use the add-on's supported local package installation mechanism if its terminal
269
+ does not expose that user directory/npm. Restart Node-RED/the Node-RED add-on
270
+ and reload the editor so the updated runtime and HTML are both loaded.
271
+ 3. Add a timestamp Inject directly into SW Light. Choose your **existing HA
272
+ server**, one `light.*` entity, Action **On**, and the desired On brightness
273
+ (94% by default). Keep optional Adaptive apply disabled for this basic test.
274
+ Remove downstream HA Action nodes; leave outputs unconnected or use Debug.
275
+ 4. Deploy. SW Light constructs without resolving HA and initially shows
276
+ `HA NOT READY`. With the light OFF, click Inject: the light should turn on at the
277
+ configured automatic level. Select Action **Off**, redeploy and inject:
278
+ it should turn off. Test **Toggle** from both ON and OFF.
279
+ 5. Set the lamp to 100% manually in HA (verify attribute `brightness: 255`).
280
+ Try On, Off and Toggle: no automatic light service should run, and the node
281
+ should show `MANUAL · 100% · BLOCK ...`. Lower the light manually to 94%, 95%
282
+ or 99%, then retry; automatic control should resume with default override settings.
283
+ 6. Select **From msg** and use Inject properties `action` (string) with `on`,
284
+ `off` and `toggle`. Also test `auto_light.action` when top-level action is
285
+ absent. A missing/invalid action should show an error, emit output 2, and
286
+ perform no service call. After HA disconnects, the node shows `HA NOT READY`;
287
+ retry after HA reconnects. Initialization failures are retried on the next input.
288
+ Also inject before HA is ready, then after it becomes ready without redeploying:
289
+ the second input should succeed. Test partial redeploy and reconnect as well.
290
+
291
+ These are manual acceptance steps, not evidence of a live deployment.
292
+
293
+ ## Outputs and status
294
+
295
+ Both outputs preserve original message properties. Only `sw_light` (output 1)
296
+ or `sw_light_event` (output 2) is assigned by this node.
297
+
298
+ Output 1 follows successful light service acknowledgement:
299
+
300
+ ```json
301
+ {
302
+ "action": "on",
303
+ "sw_light": {
304
+ "entity_id": "light.example", "state": "on",
305
+ "brightness": 115, "brightness_pct": 45.1,
306
+ "color_mode": "color_temp", "color_temp_kelvin": 2700,
307
+ "color_temp": null, "color": {},
308
+ "manual_override": false, "adaptive_lighting": true,
309
+ "action": "turn_on", "requested_action": "on", "source": "automatic",
310
+ "service_data": { "brightness": 115, "transition": 1 },
311
+ "ignored_conflicts": [], "state_confirmation": "last_observed",
312
+ "observed_at": "2026-09-30T10:00:00Z",
313
+ "adaptive": { "entity_id": null, "active": false, "manual_control": false, "state": null }
314
+ }
315
+ }
316
+ ```
317
+
318
+ `adaptive_lighting` means **configured mode**. `adaptive.active` means a resolved
319
+ switch is ON. `adaptive.applied` is present when optional apply is evaluated.
320
+ The state is the **last observed HA snapshot**, possibly preceding the action
321
+ or an unfinished transition. Service acknowledgement does not confirm a physical
322
+ lamp change. No requested value is fabricated as observed state. The first input
323
+ resolves the selected server through the provider accessor and starts observation.
324
+ Every subsequent input revalidates the server/client; temporary failures remain
325
+ retryable. Once attached, status updates live from HA events (including
326
+ brightness-only changes), reconnects and deletions. A replaced provider client
327
+ gets fresh listeners; closing/redeploying removes this node's old subscriptions.
328
+ Output 1 does not emit every unsolicited state change in v0.1.
329
+
330
+ Inputs received before HA readiness are rejected explicitly: output 2 reports
331
+ `unavailable` and `done(error)` triggers Catch-node handling. They are not queued
332
+ or replayed. Send a new input after readiness. The node stays alive and retries
333
+ resolution on the next input. Readiness observation begins with the first input.
334
+
335
+ If a constructor stack shows `sw-light.js:54`, `ha-adapter.js:33` and
336
+ `light.js:16`, it identifies the original 0.1.0 implementation, not this
337
+ candidate. After installation, restart Node-RED and verify both Palette Manager
338
+ and the startup debug version before interpreting a new runtime failure.
339
+
340
+
341
+ Status distinguishes `CONFIG ERROR` (invalid/missing server or local config),
342
+ `HA NOT READY` (client/connection/states initializing), and `ENTITY UNAVAILABLE`
343
+ (missing, unknown or unavailable light). Blocked commands show
344
+ `MANUAL · 100% · BLOCK ON/OFF`; completed commands show `AUTO · ON · 94%`
345
+ or `AUTO · OFF` with the actual command level.
346
+
347
+ Output 2 has a stable versioned event envelope:
348
+
349
+ ```json
350
+ {
351
+ "sw_light_event": {
352
+ "version": 1, "type": "manual_override", "entity_id": "light.example",
353
+ "timestamp": "2026-09-30T10:00:00Z", "active": true,
354
+ "blocked": true, "requested_action": "off"
355
+ }
356
+ }
357
+ ```
358
+
359
+ Types: `manual_override`, `override_released`, `unavailable`, `unsupported`,
360
+ `error`. Details include `reason`, `parameters`, `message`, `phase` as applicable.
361
+ Unsupported parameters are omitted and the supported action still runs.
362
+ Blocked commands emit only output 2. Invalid input and transport failures also
363
+ call Node-RED `done(error)` for Catch-node handling. Connection/state events have
364
+ no incoming message, so only the event property is supplied.
365
+
366
+ ## Architecture and checks
367
+
368
+ `lib/light.js` owns config validation, override detection, capabilities, color
369
+ normalization and the pure command planner. `lib/adaptive.js` owns discovery and
370
+ apply behavior. `lib/ha-adapter.js` owns the sole internal HA dependency,
371
+ state-cache access, service calls and resubscribing event subscriptions.
372
+ `nodes/sw-light.js` connects Node-RED lifecycle, sequential inputs, status and
373
+ the two output contracts. `nodes/sw-light.html` implements the editor/help.
374
+
375
+ ```sh
376
+ npm test
377
+ npm run check
378
+ npm pack --dry-run
379
+ ```
380
+
381
+ Run these checks from the source checkout; test tooling is not included in the npm package.
382
+ Tests use Node's built-in test runner without HA or npm dependencies. They cover
383
+ numeric ranges, precedence, max/min, manual override, color translation,
384
+ unsupported capabilities, adaptive discovery/manual control, runtime outputs,
385
+ async errors, reconnect readiness and listener cleanup. The source checkout retains a separate development integration report; it is
386
+ excluded from the public npm package. Importable examples are in `examples/`; select your
387
+ own HA server and light before deploying them in a dev instance.
388
+
389
+ ## Known limits and next steps
390
+
391
+ - The actual installed Node-RED/HA/Adaptive Lighting versions and attributes
392
+ could not be inspected; compatibility outside the stated baseline is unverified.
393
+ - Service results contain cached state, without physical confirmation. Rapid
394
+ relative commands and toggles can use stale state despite serial service calls.
395
+ - A v0.1 raw state subscription receives all state changes and filters locally;
396
+ one is created per SW Light node. It is removed on close, and HA's websocket
397
+ resubscription handles reconnects. Sharing subscriptions is a v0.2 improvement.
398
+ - Adaptive attributes are not standardized. Existing manual-control detection
399
+ and automatic brightness ownership require testing with the actual integration.
400
+ - Keep current depends on HA/device behavior. Color gamut correction and XY to
401
+ other color-space conversion are not implemented. ON/OFF lights remain usable
402
+ without brightness/color capabilities.
403
+
404
+ Before live use, test server selection/autocomplete, each lamp capability,
405
+ external brightness changes, threshold/explicit release, adaptive takeover,
406
+ long transitions, deletion/unavailability, reconnect and repeated redeployment
407
+ in your real editor with a development light. No live deployment, global config
408
+ change, npm publication or GitHub release is part of this implementation.
409
+
410
+ Recommended v0.2: verify and add your installed HA contrib version to the
411
+ adapter's tested compatibility matrix, then add state-confirmation/relative-command
412
+ coordination and shared per-server subscriptions.
@@ -0,0 +1,174 @@
1
+ [
2
+ {
3
+ "id": "sw-example-basic",
4
+ "type": "tab",
5
+ "label": "SW Light \u2014 basic",
6
+ "disabled": false,
7
+ "info": "Development example. Select your own Home Assistant server and light before deployment. No HA credentials are included."
8
+ },
9
+ {
10
+ "id": "sw-example-basic-note",
11
+ "type": "comment",
12
+ "z": "sw-example-basic",
13
+ "name": "Select HA server + light in SW Light before deploy",
14
+ "info": "SW Light uses Action: From msg and calls HA itself. Outputs are optional debug information; no HA Action node is required. Select a server and one light before deploying.",
15
+ "x": 350,
16
+ "y": 60,
17
+ "wires": []
18
+ },
19
+ {
20
+ "id": "sw-example-basic-inject-0",
21
+ "type": "inject",
22
+ "z": "sw-example-basic",
23
+ "name": "ON",
24
+ "props": [
25
+ {
26
+ "p": "action",
27
+ "v": "on",
28
+ "vt": "str"
29
+ }
30
+ ],
31
+ "repeat": "",
32
+ "crontab": "",
33
+ "once": false,
34
+ "onceDelay": 0.1,
35
+ "x": 180,
36
+ "y": 120,
37
+ "wires": [
38
+ [
39
+ "sw-example-basic-light"
40
+ ]
41
+ ]
42
+ },
43
+ {
44
+ "id": "sw-example-basic-inject-1",
45
+ "type": "inject",
46
+ "z": "sw-example-basic",
47
+ "name": "OFF",
48
+ "props": [
49
+ {
50
+ "p": "action",
51
+ "v": "off",
52
+ "vt": "str"
53
+ }
54
+ ],
55
+ "repeat": "",
56
+ "crontab": "",
57
+ "once": false,
58
+ "onceDelay": 0.1,
59
+ "x": 180,
60
+ "y": 180,
61
+ "wires": [
62
+ [
63
+ "sw-example-basic-light"
64
+ ]
65
+ ]
66
+ },
67
+ {
68
+ "id": "sw-example-basic-inject-2",
69
+ "type": "inject",
70
+ "z": "sw-example-basic",
71
+ "name": "Toggle",
72
+ "props": [
73
+ {
74
+ "p": "action",
75
+ "v": "toggle",
76
+ "vt": "str"
77
+ }
78
+ ],
79
+ "repeat": "",
80
+ "crontab": "",
81
+ "once": false,
82
+ "onceDelay": 0.1,
83
+ "x": 180,
84
+ "y": 240,
85
+ "wires": [
86
+ [
87
+ "sw-example-basic-light"
88
+ ]
89
+ ]
90
+ },
91
+ {
92
+ "id": "sw-example-basic-inject-3",
93
+ "type": "inject",
94
+ "z": "sw-example-basic",
95
+ "name": "45% / 3s",
96
+ "props": [
97
+ {
98
+ "p": "action",
99
+ "v": "on",
100
+ "vt": "str"
101
+ },
102
+ {
103
+ "p": "brightness_pct",
104
+ "v": "45",
105
+ "vt": "num"
106
+ },
107
+ {
108
+ "p": "transition",
109
+ "v": "3",
110
+ "vt": "num"
111
+ }
112
+ ],
113
+ "repeat": "",
114
+ "crontab": "",
115
+ "once": false,
116
+ "onceDelay": 0.1,
117
+ "x": 180,
118
+ "y": 300,
119
+ "wires": [
120
+ [
121
+ "sw-example-basic-light"
122
+ ]
123
+ ]
124
+ },
125
+ {
126
+ "id": "sw-example-basic-light",
127
+ "type": "sw-light",
128
+ "z": "sw-example-basic",
129
+ "name": "SW Light",
130
+ "server": "",
131
+ "entityId": "",
132
+ "x": 460,
133
+ "y": 210,
134
+ "wires": [
135
+ [
136
+ "sw-example-basic-result"
137
+ ],
138
+ [
139
+ "sw-example-basic-events"
140
+ ]
141
+ ],
142
+ "action": "msg"
143
+ },
144
+ {
145
+ "id": "sw-example-basic-result",
146
+ "type": "debug",
147
+ "z": "sw-example-basic",
148
+ "name": "result",
149
+ "active": true,
150
+ "tosidebar": true,
151
+ "console": false,
152
+ "tostatus": false,
153
+ "complete": "true",
154
+ "targetType": "full",
155
+ "x": 710,
156
+ "y": 180,
157
+ "wires": []
158
+ },
159
+ {
160
+ "id": "sw-example-basic-events",
161
+ "type": "debug",
162
+ "z": "sw-example-basic",
163
+ "name": "events",
164
+ "active": true,
165
+ "tosidebar": true,
166
+ "console": false,
167
+ "tostatus": false,
168
+ "complete": "true",
169
+ "targetType": "full",
170
+ "x": 710,
171
+ "y": 260,
172
+ "wires": []
173
+ }
174
+ ]