feat(entity): optional available on nodered/entity - #409
Conversation
Accept optional contrib_version on the config entry and clear it on disconnect. Expose contrib_announced_version as a fact helper for later use. Load VERSION from manifest.json so release-please owns the bump.
There was a problem hiding this comment.
Pull request overview
This PR extends the Node-RED Companion integration’s websocket protocol to support (1) an optional available field for discovery/entity messages (enabling availability-only updates and new “presence-available” semantics gated by contrib version announcement) and (2) storing the contrib package version via an enhanced nodered/version handshake.
Changes:
- Update websocket schemas to accept optional
availableand allow omittingstatefornodered/entityandnodered/discovery. - Add contrib-version handshake storage/clearing behavior on
nodered/version, plus helpers to gate “presence-available” semantics. - Adjust entity availability/state handling (notably sensor/binary_sensor + base entity behavior) and expand unit/integration tests for the new contract.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/test_websocket.py | Adds websocket-level tests for optional available, omitted state, and contrib version storage/clearing. |
| tests/test_utils.py | Adds tests ensuring VERSION matches manifest and contrib capability helpers behave as expected. |
| tests/test_sensor.py | Adds unit coverage for discovery-without-state semantics and availability-only restore behavior. |
| tests/test_entity.py | Adds unit coverage for legacy vs presence-available update semantics in the base entity attribute/availability handling. |
| tests/test_binary_sensor_unit.py | New unit tests for binary sensor availability + omitted-state semantics. |
| tests/test_availability_ws.py | New websocket integration tests validating end-to-end HA state for Unknown/Unavailable and restore flows. |
| custom_components/nodered/websocket.py | Expands websocket command schemas and adds contrib_version handshake persistence/cleanup. |
| custom_components/nodered/version.py | Loads runtime version from manifest.json as the source of truth. |
| custom_components/nodered/utils.py | Adds helpers to detect announced contrib version and gate presence-available semantics. |
| custom_components/nodered/sensor.py | Sets default availability for discovery-without-state under presence-available gating; preserves values on availability-only updates. |
| custom_components/nodered/binary_sensor.py | Adds omitted-state handling to preserve prior state; applies presence-available discovery behavior. |
| custom_components/nodered/manifest.json | Bumps integration version to 4.2.4. |
| custom_components/nodered/entity.py | Updates base attribute/availability update semantics for legacy vs presence-available behavior. |
| custom_components/nodered/const.py | Introduces CONF_AVAILABLE / CONF_CONTRIB_VERSION constants. |
| else: | ||
| self._attr_native_value = None | ||
| # No reading yet: unavailable once presence-available applies | ||
| if contrib_supports_presence_available(hass): | ||
| self._attr_available = False |
| if CONF_STATE in config: | ||
| self._attr_is_on = self._evaluate_sensor_state(config[CONF_STATE]) | ||
| else: | ||
| self._attr_is_on = None | ||
| # No reading yet: unavailable once presence-available applies | ||
| if contrib_supports_presence_available(hass): | ||
| self._attr_available = False |
There was a problem hiding this comment.
The issue only arises as a bit of an edge-case when "Resend state, available, and attributes" is set on: create entity -> set available (HA state now unknown/available) -> HA restart -> resend happens without state -> HA state now unknown/unavailable.
I agree it's a bit inconsistent, but it's a very edge case. Discovery without state still defaults to unavailable on purpose (avoid available+unknown on first deploy / empty rediscovery). Availability-only updates after the entity exists already honor available and keep the last reading.
I don't think we should change this for this case, but if we were it would be to disallow setting available to false for resend sensors that have never had a value.
| vol.Optional(CONF_STATE): vol.Any(bool, str, int, float, None), | ||
| vol.Optional(CONF_ATTRIBUTES): dict, | ||
| vol.Optional(CONF_AVAILABLE): bool, |
There was a problem hiding this comment.
The non-sensor entities can't have this yet, they haven't been updated with the new code.
|
@zachowj could you kindly turn off forced copilot reviews? I have a pretty limited credits. |
Paired with contrib#2041. Full design, wire contract, and smoke matrix live there.
Summary
availableonnodered/entityand discovery.availableholds; discovery without state → unavailable.available: true;discovery without state stays unknown.
Stacking note
Depends on #408 (contrib version handshake). This branch is stacked on that
tip; GitHub shows the handshake commit too until #408 merges (base is
mainbecause the handshake branch only exists on the fork). Fine to merge#408 first, or skip it and merge this PR alone — it carries the handshake
commit.
Test plan