Skip to content

Releases: Ctrlable/rfwc5_controller

v1.4.3 — Device card entity picker UX

16 Apr 22:46

Choose a tag to compare

What's new

Device card improvements

  • Action type select now shows friendly labels ("Stateful Scene", "HA Scene", "Cover Cycle (Up / Stop / Down)", etc.) instead of raw internal keys
  • Action entity is now a domain-filtered select entity (replaces the plain text input) — shows all matching entities as "Friendly Name (entity_id)" options
    • Stateful Scene → switch entities
    • HA Scene → scene entities
    • Automation → automation entities
    • Script → script entities
    • Entity Toggle → light / switch / fan / input_boolean / media_player / climate
    • Cover Cycle / None → unavailable (configure multi-cover via Reconfigure)
  • Changing the action type automatically clears the linked entity
  • Button action sensor native_value now shows entity friendly names; raw action_type and action_entity exposed as extra_state_attributes

Breaking change

The text.*_entity entities are removed. HA will mark them as unavailable/orphaned; the new select.*_entity entities replace them.

UX: friendly action names, entity autocomplete, reconfigure pre-population

16 Apr 20:06

Choose a tag to compare

Three Config Flow UX Fixes

Fix 1 — Action types show friendly names

The action type dropdown previously showed raw internal keys (stateful_scene, cover_cycle, etc.). It now uses HA's SelectSelector with a labelled list:

Value Now shows as
stateful_scene Stateful Scene
ha_scene HA Scene
automation Automation
script Script
entity_toggle Entity Toggle (On/Off)
cover_cycle Cover Cycle (Up / Stop / Down)
none None (LED only)

Fix 2 — Entity fields use HA's built-in entity picker

Entity selection previously used either a static dropdown (limited to entities visible at setup time) or a plain text input. All entity fields now use EntitySelector with domain filtering:

Action type Domain filter
Stateful Scene switch
HA Scene scene
Automation automation
Script script
Entity Toggle switch, light, input_boolean, fan, cover, climate, media_player, lock
Cover Cycle covermulti-select (select multiple covers natively; stored as CSV)
Indicator entity sensor, number
Basic sensor sensor

Cover Cycle now uses multiple=True on the EntitySelector — the user selects covers from a proper multi-select picker instead of typing a comma-separated string.

Fix 3 — Reconfigure pre-populates all fields

Re-opening Configure previously showed blank fields. All steps now pre-fill with current values:

  • OptionsFlow.__init__ merges entry.options over entry.data into self._data, so the most recent reconfigure values always take priority
  • ConfigFlow.async_step_user seeds self._data from the existing entry when launched from a reconfigure context
  • Every step (MQTT, buttons, entities, indicator, basic sensor) uses the merged data as field defaults

Files Changed

  • const.pyACTION_TYPE_LABELS dict
  • config_flow.py — complete rewrite with SelectSelector, EntitySelector, merged self._data, _current() helper; removed stale entity-list helpers
  • strings.json / translations/en.json — removed {cover_hint} placeholder (no longer needed with EntitySelector)
  • manifest.json — bumped to 1.4.2

Fix: cover cycle now responds to every button press using internal state machine

16 Apr 18:00

Choose a tag to compare

What Was Broken

The cover cycle only worked on the first press. Every subsequent press read HA cover state, which still reflected the pre-command position (HA state lags by seconds after a cover command is issued). So every press after the first saw the same state and repeated the same command.

Fix: Internal State Machine

CoverCycleController now maintains its own CoverMovementState that updates instantly on button press, independent of HA cover state.

State Machine

OPEN / STOPPED_OPEN  → close_cover → CLOSING
CLOSING              → stop_cover  → STOPPED_CLOSE
STOPPED_CLOSE        → open_cover  → OPENING
OPENING              → stop_cover  → STOPPED_OPEN
CLOSED               → open_cover  → OPENING
UNKNOWN              → seed from HA, then apply transition

State is stored at class level in _states dict (keyed by entry_id_button_idx) so it survives object recreation on integration reload.

HA State Is Still Used — But Only For

  1. Initial seeding — on first press, HA state is read once to set the starting CoverMovementState
  2. Terminal confirmationsync_from_ha_state() advances OPENING → OPEN or CLOSING → CLOSED only when HA confirms position ≥ 95% or ≤ 5%

Other Improvements

  • Instant state updateasync_cycle() sets next_state before the service call, so a second press 300ms later sees the correct new state
  • Correct revert on failureprev_state captured before update; reverted on service call exception
  • Accurate log messages — logs prev_state → service → next_state
  • 400ms debounce — rapid double-taps dropped cleanly

Files Changed

  • cover_controller.py — complete rewrite with CoverMovementState enum and internal state machine
  • __init__.py — cover entity state change handler calls sync_from_ha_state() then get_led_state()
  • manifest.json — bumped to 1.4.1

Feature: cover cycle action with position-safe Up/Stop/Down

16 Apr 17:10

Choose a tag to compare

New Feature: Cover Cycle Action Type

Adds cover_cycle as a new button action type, enabling Lutron-style Up / Stop / Down cycling over one or more cover entities without ever forcing an absolute position.

Cycle Logic

Current state Button press issues
Any cover moving (opening/closing) stop_cover — stops all covers
All covers at position ≤ 5% (or closed) open_cover — opens all covers
All covers at position ≥ 95% (or open) close_cover — closes all covers
Stopped mid-travel Resumes in last direction (open or close)

Position thresholds (5% / 95%) handle covers that never reach exact 0 or 100. Last-direction memory persists across button presses and survives integration reloads.

Multiple Covers

Enter a comma-separated list of cover entity IDs in the entity field:

cover.living_room_shade, cover.office_shade

All covers in the list are controlled together. LED state reflects the group: ON when any cover is open/opening (position > 5%), OFF when all are closed/closing.

LED Behavior

  • ON = any cover position > 5% or state is open / opening
  • OFF = all covers position ≤ 5% or state is closed / closing

The LED syncs automatically when covers are moved by other automations or by hand.

direction_key Scoping

Each button on each keypad gets its own direction memory key ({entry_id}_{button_index}), so multiple keypads sharing the same HA instance don't interfere.

Other Changes

  • async_execute_action gains **kwargs (backwards-compatible); all call sites pass direction_key for proper scoping
  • action_executor.py is now the single authoritative executor — removed the stale duplicate that was in __init__.py
  • get_tracked_state() returns correct initial LED state for cover_cycle buttons on HA startup

Files Changed

  • cover_controller.py (new) — CoverCycleController with cycle logic, direction memory, LED state computation
  • action_executor.py — cover_cycle branch, **kwargs, updated get_tracked_state
  • __init__.py — remove local executor duplicate, cover_cycle entity tracking, direction_key passing
  • switch.py — cover_cycle short-circuit in _apply_state
  • config_flow.py — free-text entity field for cover_cycle buttons
  • const.py — ACTION_TYPE_COVER_CYCLE, cover state/service constants
  • strings.json / translations/en.json — cover_hint placeholder in entities step
  • manifest.json — bumped to 1.4.0

Fix: coalescing write queue prevents Z-Wave indicator race condition on simultaneous button state changes

16 Apr 16:44

Choose a tag to compare

What's Fixed

Root cause

Physical button press → action fires → linked entity state changes → _on_linked_entity_state_change fires → schedules another write → two writes race each other within milliseconds, overwhelming the RFWC5's 300-series Z-Wave chip.

Coalescing write queue (led_manager.py)

  • Write guard (_write_in_progress): _async_write_now returns immediately if a write is already executing — no concurrent Z-Wave writes ever.
  • Pending flag (_write_pending): if state changes arrive while a write is executing, _write_pending is set. The finally block schedules a follow-up write after the coalesce window so the latest state is always sent.
  • Coalesce timer (_coalesce_window = 0.5s): rapid back-to-back _schedule_write() calls cancel and restart the timer, so only the final state after 500ms of quiet is written. Replaces the old 1s debounce.
  • async_sync_all updated to clear _write_pending and respect the in-progress guard.

Suppression window (led_manager.py + __init__.py)

  • New suppress_external_writes(duration) method sets a monotonic deadline.
  • After each button press action fires (ON and OFF paths), manager.suppress_external_writes(2.0) is called.
  • _on_linked_entity_state_change checks time.monotonic() > manager._suppress_until before calling _schedule_write(). If within the suppression window, the LED state is still updated in memory but no write is scheduled — the button press write already sent the correct state.

Constants Changed (const.py)

Old New
LED_WRITE_DEBOUNCE_S = 1.0 LED_COALESCE_WINDOW_S = 0.5
(new) LED_SUPPRESS_WINDOW_S = 2.0

Files Changed

  • led_manager.py — coalescing write queue, write guard, suppression window
  • const.py — new timing constants
  • __init__.py — suppress calls after action fires, suppression check in state change handler
  • manifest.json — bumped to 1.3.2

v1.3.1 — Options/data merge fix + auto-reload on reconfigure

16 Apr 16:18

Choose a tag to compare

What's Fixed

Options/data merge (entry.options vs entry.data)

HA's OptionsFlow saves reconfigured values to entry.options, not entry.data. Previously, async_setup_entry read only entry.data, so changes made via Configure (basic sensor, MQTT settings, button assignments, etc.) were silently ignored until a full HA restart wiped entry.options back into entry.data.

Fix: a _get_config helper now checks entry.options first, falling back to entry.data. The same merge is applied in MQTTProvisioner.__init__ so MQTT settings updated via reconfigure are used on the next provisioning run.

Auto-reload on reconfigure

After saving new options, HA did not automatically reload the integration — the new settings had no effect until the user manually restarted HA or reloaded the integration.

Fix: entry.add_update_listener now triggers an automatic reload whenever the config entry is updated, so reconfigure changes take effect immediately.

Files Changed

  • __init__.py_get_config helper, group_levels fix, update listener registration
  • provisioner.pyMQTTProvisioner.__init__ options-over-data merge
  • manifest.json — bumped to 1.3.1

Feature: use Basic sensor entity for button press detection

16 Apr 15:48

Choose a tag to compare

What's new in v1.3.0

Button presses now detected via Basic sensor entity state changes

Replaces the zwave_js_value_updated bus listener with direct HA state change tracking on the Basic CC sensor entity.

  • Basic sensor value 10/20/30/40/50 → button press → LED updated + action fired
  • Basic sensor value 0 → button release → indicator refreshed → OFF actions fired for buttons that turned off

New config step: Select Basic Sensor

Pick the Basic CC sensor entity from your Z-Wave device in the setup and reconfigure flows. Disabled entities show with a [disabled] tag and are auto-enabled by the integration.

async_execute_action dispatcher

Action Type ON OFF
ha_scene scene.turn_on
stateful_scene switch.turn_on switch.turn_off
automation automation.trigger
script script.turn_on script.turn_off
entity_toggle homeassistant.turn_on homeassistant.turn_off

Auto-migration to schema v4

Existing entries are automatically migrated. No action required.

Fix: node_id migration, reprovision button, clean up log levels

14 Apr 21:57

Choose a tag to compare

What's new in v1.2.1

Auto-migration for existing entries

Existing config entries created before v1.2.0 are automatically migrated to schema version 2 with MQTT defaults. No action required after upgrading.

If `node_id` is not yet set, the provisioner logs a clear WARNING with step-by-step instructions to reconfigure, and the integration stays operational (LED control still works).

Run Provisioning button on device card

A new Run Provisioning button (`mdi:cog-sync`) appears on the device card. Press it to run MQTT provisioning without going to Developer Tools.

Log level cleanup

Diagnostic trace messages moved from WARNING → DEBUG. Production logs will no longer be flooded.

Message Old New
set_button called WARNING DEBUG
refresh_and_read WARNING DEBUG
ingest_indicator WARNING DEBUG
PRE-WRITE / POST-WRITE WARNING DEBUG
tracking entities WARNING DEBUG
linked entity changed WARNING DEBUG

Feature: automatic MQTT-based Z-Wave group provisioning

14 Apr 03:27

Choose a tag to compare

What's new in v1.2.0

MQTT-based Z-Wave provisioning (replaces broken zwave_js service approach)

The integration now provisions your RFWC5 keypad automatically on first setup via the Z-Wave JS UI MQTT API — no manual Z-Wave configuration required.

New config step: Z-Wave JS UI MQTT Settings

A new step in the setup flow (and reconfigure flow) collects:

Field Default Description
MQTT Prefix `zwave-js-ui` The MQTT topic prefix for Z-Wave JS UI
Gateway Name `zwave-js-ui` The gateway name in Z-Wave JS UI
MQTT Broker Host `localhost` Your MQTT broker
MQTT Broker Port `1883` Your MQTT broker port
RFWC5 Node ID (required) Z-Wave node ID of the RFWC5 keypad
Controller Node ID `1` Z-Wave controller node ID

Find your node ID in Z-Wave JS UI → Devices → your RFWC5 → Node ID.

What gets provisioned

Group Associations — Groups 1–5 (one per button) and group 255 (lifeline) are associated to the Z-Wave controller. Without these, button presses are never forwarded to Home Assistant.

Group Level Parameters — Configuration CC 112, parameters 1–5 are set to values 10 / 20 / 30 / 40 / 50. These are the Basic CC values the keypad sends when each button is pressed.

Provisioning runs once only

After the first successful run, `provisioned: true` is stored in the config entry. Subsequent HA restarts skip provisioning entirely. Use the `rfwc5_controller.reprovision` service to re-run if needed (e.g. after a device reset or Z-Wave network change).

Multi-keypad support

Each keypad config entry has its own MQTT settings — supports multiple RFWC5 keypads across different Z-Wave networks.

Feature: automatic Z-Wave group provisioning on setup

14 Apr 02:27

Choose a tag to compare

What's new in v1.1.0

Automatic Z-Wave provisioning

Every time the integration loads, it now runs a provisioning sequence before the LED manager starts. This ensures the keypad is correctly wired to the Z-Wave controller before any button presses are processed.

Part A — Group Associations (Association CC)
Groups 1–5 (one per button) and group 255 (lifeline) are associated to the Z-Wave controller node. Button presses are sent via Basic CC to these groups — without the associations they are never forwarded to HA.

Part B — Group Level Configuration (CC 112)
Configuration parameters 1–5 are set to values 10 / 20 / 30 / 40 / 50. These are the Basic CC values the keypad sends when each button is pressed. Each value is read back after writing to verify the change took effect.

Provisioning status sensor

A new sensor.<keypad>_Provisioning_Status entity appears on the device card:

  • State: ok / incomplete / unknown
  • Extra attributes: full report showing per-group association and level results

Manual reprovision service

rfwc5_controller.reprovision — re-runs the full provisioning sequence from Developer Tools without having to delete and re-add the integration. Accepts an optional entry_id field to target a specific keypad.