Skip to content

Process improvement plan: fixes, robustness, tests/CI, behavior, perf, docs (v1.18.1 → v1.22.1) - #12

Merged
karolperkowski merged 7 commits into
mainfrom
claude/one-shot-processing-i1f4mo
Jul 5, 2026
Merged

Process improvement plan: fixes, robustness, tests/CI, behavior, perf, docs (v1.18.1 → v1.22.1)#12
karolperkowski merged 7 commits into
mainfrom
claude/one-shot-processing-i1f4mo

Conversation

@karolperkowski

Copy link
Copy Markdown
Owner

Implements all 29 findings from the v1.18.0 code review & improvement plan, as six milestone commits (each with its own version bump, so the release workflow publishes v1.22.1 on merge).

Commits / milestones

v1.18.1 — pure fixes & hygiene (#1, #5, #16–18, #21)

  • Service docs said Port Number; the code routes by Order. services.yaml + strings.json/en.json now describe destination/source as Order numbers with corrected examples — following the old docs routed the wrong source on any profile where Order ≠ Port (438 sources on the tested profile).
  • Diagnostics downloads redact the router host (safe to attach to public issues).
  • Dead code in helpers.device_info(), stale comments (ParseResult keying, button.py, CLAUDE.md keepalive), unused imports/variables, stale hacs.json filename key.

v1.19.0 — robustness (#2, #3, #6, #11, #13, #14)

  • Config entries get a {host}:{port} unique_id — adding the same router twice now aborts with already_configured.
  • Hidden CSV rows are kept in the name/port maps (MAGNUM still uses their Orders in .UV/.SV) but excluded from source dropdowns; flow wording aligned with actual behavior.
  • .E storm guard: the reader task runs concurrently with the connect-time sweep; mnemonic sweeps abort after 5 consecutive .E, and expected .E replies are counted in new interrogate_rejected/mnemonic_rejected stats instead of flooding recent_errors.
  • Destination selects cache their options list with a label → Order reverse map; duplicate Global Names get an (Order N) suffix so every label resolves unambiguously (also removes the per-state-write rebuild and per-take O(n) scan).

v1.20.0 — tests, CI, validation (#19, #24–26, #29)

  • 70 pytest cases via pytest-homeassistant-custom-component: csv_parser (all 5 formats, hidden rows, Order≠Port, malformed rows), client dispatch (.UV/.A/.BA/.RAD/.RAT/.E/.X), optimistic routing, sweep abort, an end-to-end fake-router test for CR-only framing, permission matrix, blocked-route event payloads, config/options flows, select labels.
  • ci.yml (ruff + mypy + pytest + strings.json ↔ en.json equality assert) and validate.yml (HACS + hassfest).
  • Flow annotations migrated to ConfigFlowResult; DeviceInfo imported under TYPE_CHECKING.

v1.21.0 — behavior (#4, #7, #8, #12)

  • Optimistic route state rolls back when the router answers a take with .E within 5 s (a .UV echo disarms the rollback).
  • Entity listeners unregister via async_on_remove — no more leaked callbacks on partial platform reloads.
  • Reconfigure flow: change host/port/name without deleting the entry; profile data, CSV names, and options are preserved.
  • Exponential reconnect backoff (configured delay → ×2 per failed cycle → 120 s cap, reset on success).

v1.22.0 — performance & refactors (#10, #15, #20, #22, #23)

  • Connect-time sweeps batched (8 commands per drain, 50 ms between batches) — a 512-destination matrix syncs in ~8 s instead of ~74 s; estimated_sync_seconds() matches.
  • Over-provision detection driven by sync_callback instead of sleep(10) per connect; grace/detection tasks tracked and cancelled on disconnect/unload.
  • Detect Destinations awaits the interrogation drain (with stall detection) instead of sleeping 2 s.
  • stop() awaits the cancelled run loop; .P re-query tasks tracked.
  • One helpers.detection_status() replaces three copies of the configured/detected/suggested logic.
  • Public QuartzClient.connected property replaces all _connected peeks.

v1.22.1 — docs (#27, #28)

  • README caught up (~5 releases): locks, sensors, detect/cleanup buttons, read-only destinations + allowed users, an Events section documenting the evertz_quartz_route_blocked contract with an example automation, startup sync notification, connection behavior, lock protocol commands.
  • CLAUDE.md drift fixed (file map, entity table, new architecture sections, testing).

Verification

  • 70 tests pass (3 consecutive runs), ruff and mypy clean, strings.json/en.json byte-identical.
  • Client exercised end-to-end against a fake TCP router: concurrent sync, .E attribution, sweep abort, optimistic routing + rollback all observed working.

🤖 Generated with Claude Code

https://claude.ai/code/session_01NTy9wAPHGwy2FM5yyjxqPR


Generated by Claude Code

claude added 7 commits July 4, 2026 18:44
…iene cleanups v1.18.1

- services.yaml + strings.json/en.json: destination/source are Order
  numbers (profile CSV Order column), not Quartz Port Numbers; fix the
  misleading example values (#1)
- diagnostics: redact router host via async_redact_data so diagnostics
  attached to public issues never leak the IP (#5)
- helpers.device_info: drop dead first version-read block and unused
  pkg_version import (#16)
- fix stale comments: ParseResult names are keyed by Order, dangling
  port-map comment in button.py, CLAUDE.md now documents the 60s
  keepalive probe (#17)
- remove unused imports/variables: lock.py EntityCategory,
  binary_sensor.py router_display_name, sensor.py time,
  options_flow.py CONF_NAME, config_flow.py csv_was_uploaded (#18)
- hacs.json: drop stale filename key left over from the card repo (#21)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTy9wAPHGwy2FM5yyjxqPR
…nambiguous source labels v1.19.0

- config flow: set a host:port unique_id and abort when the router is
  already configured — the already_configured abort string is live now (#2)
- hidden CSV rows: keep them in the name/port maps (MAGNUM still uses
  their Orders in .UV/.SV) but exclude hidden sources from the select
  dropdown options; persist hidden orders in entry.data and align the
  config/options flow wording with the actual behavior (#3)
- .E storm guard: the reader now runs concurrently with the connect-time
  sweep; mnemonic sweeps (.RD/.RT) abort after 5 consecutive .E replies,
  and .E replies attributable to .I interrogation or mnemonic queries are
  counted in new interrogate_rejected/mnemonic_rejected stats instead of
  flooding recent_errors (#6)
- destination select: options list and a label→Order reverse map are
  built once and cached (invalidated on name updates); duplicate Global
  Names get an ' (Order N)' suffix so every label resolves to exactly one
  source — also removes the per-take O(n) label scan and the per-state-
  write options rebuild (#11, #13, #14)
- Clear CSV button now also clears namespaces and hidden-row data

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTy9wAPHGwy2FM5yyjxqPR
- tests/: 55 pytest cases via pytest-homeassistant-custom-component —
  csv_parser (all 5 formats, hidden rows, Order≠Port, malformed rows),
  quartz_client dispatch (.UV/.A/.BA/.RAD/.RAT/.E/.X, order tracking,
  optimistic routing, sweep abort, end-to-end CR framing), helpers
  (user_can_route matrix, notify_blocked_route event/notification
  payloads), config flow (happy path, duplicate abort, cannot_connect),
  options-flow diff builders, select label dedup/hidden/namespace (#24)
- .github/workflows/ci.yml: ruff + mypy + pytest + a strings.json ↔
  translations/en.json equality assert (#25, #29)
- .github/workflows/validate.yml: hacs/action + hassfest (#26)
- pyproject.toml with pytest/ruff/mypy config; requirements_test.txt
- annotate flows with ConfigFlowResult (FlowResult is deprecated) and
  import DeviceInfo under TYPE_CHECKING so ruff/mypy pass clean (#19)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTy9wAPHGwy2FM5yyjxqPR
…reconnect backoff v1.21.0

- .E rollback: route() remembers (dest, prev_src); an .E within 5 s of
  the take restores the previous route and fires the route callback so
  the select snaps back. A .UV confirmation disarms the rollback; .I
  attribution now uses a bounded pending counter (keepalive probes
  excluded) so it can't swallow unrelated .E forever (#4)
- entity listeners: all platforms register via helpers.subscribe_listener
  + async_on_remove, so partial platform reloads no longer leak dead
  callbacks (#7)
- reconfigure flow: change host/port/name from the UI without deleting
  the entry — profile data, CSV names, and options are preserved;
  collisions with other entries abort as already_configured (#8)
- reconnect backoff: configured reconnect_delay is the floor, doubling
  per failed cycle up to 120 s, reset on successful connect or when the
  option changes (#12)
- tests: rollback/confirmation/stale-window, backoff sequence,
  reconfigure happy path + collision abort (63 tests total)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTy9wAPHGwy2FM5yyjxqPR
…nnected property v1.22.0

- connect-time sweeps (.I routes, .BI locks, .RD/.RT names) now write 8
  commands per drain with 50 ms between batches — a 512-destination
  matrix syncs in ~8 s instead of ~74 s; estimated_sync_seconds updated
  to match the batched pacing (#15)
- over-provision detection runs when the sync sweep completes (driven by
  sync_callback + reply grace) instead of a fixed 10 s sleep per connect;
  the grace task is tracked and cancelled on disconnect/unload, so
  flapping connections no longer pile up untracked tasks (#10, #22)
- Detect Destinations button awaits the interrogation drain (with stall
  detection for controllers that ignore .I) instead of sleeping 2 s (#10)
- QuartzClient.stop() awaits the cancelled run loop; .P re-query tasks
  are tracked and cancelled on stop (#22)
- new helpers.detection_status() is the single source of truth for the
  configured/detected/suggested numbers used by the Profile Mismatch
  sensor, the startup detection check, and the Detect button (#20)
- public QuartzClient.connected property replaces all private
  _connected peeks (#23)
- tests: batching, interrogation drain, detection status, listener
  unsubscribe, sync estimate (70 tests total)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTy9wAPHGwy2FM5yyjxqPR
- README: document lock entities, connection/profile sensors, Detect
  Destinations, Clean Up Stale Entities, read-only destinations +
  allowed users, the evertz_quartz_route_blocked event contract with an
  example push automation, startup sync notification, batched sweeps,
  reconnect backoff, .E rollback, reconfigure, lock protocol commands,
  keepalive probe, and diagnostics redaction (#27)
- CLAUDE.md: file map now lists lock/binary_sensor/sensor/tests/CI, the
  entity table covers all current entities, and new sections document
  hidden-row semantics, .E attribution, batched sync, backoff,
  unique_id/reconfigure, new stats, and the automated test suite (#28)
- deflake the reconfigure test: patch setup/unload around the
  reload-triggering abort

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTy9wAPHGwy2FM5yyjxqPR
…e state keys, hacs.json schema, MIT LICENSE v1.22.2

Caught by the new validate.yml on its first PR run:
- manifest.json keys sorted (domain, name, then alphabetical)
- log-level select state translation keys lowercased (hassfest requires
  [a-z0-9-_]+)
- hacs.json: 'category' is not a valid key (it is the hacs/action input,
  not part of the repo manifest)
- add the MIT LICENSE file the README already declares (HACS requires it)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTy9wAPHGwy2FM5yyjxqPR
@karolperkowski
karolperkowski merged commit bd706ba into main Jul 5, 2026
5 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants