Skip to content

chore: add pre-commit hooks and CI workflow#6

Merged
tclancy merged 17 commits into
mainfrom
claude/pre-commit-config-5
May 14, 2026
Merged

chore: add pre-commit hooks and CI workflow#6
tclancy merged 17 commits into
mainfrom
claude/pre-commit-config-5

Conversation

@metaframework-dispatch-bot
Copy link
Copy Markdown

Summary

  • Added .pre-commit-config.yaml with standard hooks (trailing-whitespace, end-of-file-fixer, check-yaml, check-toml, check-merge-conflict, ruff lint + format)
  • Added pre-push pytest gate
  • Added GitHub Actions CI workflow (ruff lint, format check, pytest)
  • Applied ruff formatting to 6 Python files
  • Added [tool.pytest.ini_options] to pyproject.toml

Test plan

  • 22 tests pass
  • Pre-commit hooks verified working
  • Pre-push pytest gate confirmed

Note: main has 14 local commits ahead of origin/main — Tom may need to push main before merging this PR.

Closes #5

🤖 Generated with Claude Code

dispatch-bot[bot] and others added 17 commits April 19, 2026 14:19
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Implements the JSON-ready payload builder for the firmware's POST /transmit
endpoint. Validates binary bit strings and extracts the six required timing
keys from the device profile.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Refactor the CLI from a single click command into a click group with two
subcommands:

- 'send': takes (device, unit, command) and POSTs to /transmit endpoint
- 'raw': takes arbitrary bit string and device profile, POSTs to /transmit

Both subcommands now hit the generic /transmit endpoint instead of the
per-button endpoints, matching the firmware's new generic handler.

Add comprehensive test suite (5 tests) covering subcommand structure,
error handling, and HTTP payload validation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Both fans are physically in the living room; rename from bedroom/living_room
to main/stairs to reflect which is nearer the main room vs the staircase
landing.

CLAUDE.md confirmed the operating frequency as 433.935 MHz during capture;
the YAML and PROTOCOL.md were stale with the old 315 MHz guess. Firmware
just toggles TX_PIN, so the YAML field was informational only, but the
mismatch was misleading.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
TX on 433 MHz confirmed; fans don't respond; RTL-SDR capture of our own
transmission shows smeared pulse envelope vs the real remote's crisp
pulses. Most likely TX module wiring (3V3 vs VIN) or timing constants
off. Includes ranked hypotheses, next-session plan, and pointers to
/tmp comparison artifacts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Loads a dumb shell script that curl-posts a fixed reminder pointing at
the fan-debugging findings doc. Lives in scripts/ with a template plist
for re-creation on a new machine. The live plist at
~/Library/LaunchAgents/com.tomclancy.rf-nudge.plist is already loaded
and smoke-tested.

Update scripts/rf-nudge.sh's MSG as the project's bottleneck shifts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After three weeks of "TX is alive but fans ignore us," the fix was that
our hand-decoded pulse and gap timings were systematically off. The
biggest miss was sync_gap_us: we had 670 µs, the real remote uses ~4500
µs to give the fan's AGC time to settle after the long sync burst before
trying to decode bits.

Recapturing remote 1's light press with rtl_fm -M am (instead of the
default WBFM) gave a clean envelope where pulse and gap widths are
unambiguous. Measured values:

  pulse_us:     400 → 560
  sync_us:     8000 → 8200
  sync_gap_us:  670 → 4500   (the critical fix)
  zero_gap_us:  670 → 570
  one_gap_us:  1800 → 1700

All 5 commands (light, off, speed1/2/3) now drive both fans (main and
stairs) reliably. The "derived" stairs commands turned out correct;
nothing about the bit pattern needed changing.

Also adds a "Tom's setup checklist" section to README so cold-start
sessions don't lose half an hour to "where's pio" / "what's the IP" /
"why is monitor showing noise" again.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Generic-bits firmware + corrected timing values + working fan control
for both Sofucor fans across all five commands.

Highlights:
- POST /transmit endpoint accepts {bits, timing} JSON; firmware no longer
  device-specific
- CLI split into 'send' (named commands from YAML profile) and 'raw'
  (arbitrary bit string) over click groups
- Units renamed bedroom/living_room → main/stairs
- Frequency corrected 315.4 → 433.935 MHz across YAML/PROTOCOL.md/tests
- Timing values re-measured from a clean AM-demod RTL-SDR capture; the
  original WBFM-decoded values were systematically off, especially
  sync_gap_us (was 670, real ≈ 4500)
- README has a Tom-setup-checklist for cold-start sessions
- 22/22 tests green
The original goal was an HA automation, not a CLI. Sketches the
rest_command config and flags the three open items: stale legacy
endpoint timing, no mDNS so nodemcu.local won't resolve, and the
template integration if HA needs to model fan state properly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two HA-readiness fixes in one reflash:

1. Timing constants (SYNC_US, SYNC_GAP, PULSE_US, ZERO_GAP, ONE_GAP)
   updated to the values measured from the AM-demod capture, matching
   what's in devices/sofa_king_fan.yaml. The legacy /fan/{N}/{cmd}
   endpoints now drive the fans correctly. Generic /transmit still
   reads timing from the JSON payload — both paths now equivalent.

2. mDNS responder added: chip advertises as ceilingfans.local and
   announces _http._tcp on :80 for service-discovery visibility.
   No more IP-hunting after DHCP lease changes — both Tom's CLI
   and HA's rest_command can use the stable hostname.

Also updates cli.py default --host to ceilingfans.local and refreshes
the README's HA notes (the previously-flagged caveats are now fixed)
plus the cold-start checklist (try the hostname before falling back
to serial-monitor IP-hunting).

Caveat for HA in Docker: bridge networking can't see mDNS. Use
network_mode: host or a DHCP reservation + hardcoded IP.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Standard hook set matching other active repos. Pre-push pytest gate.
Move pre-commit and ruff to dev dependencies.

Closes #5

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@tclancy tclancy merged commit 517b4aa into main May 14, 2026
1 check passed
@tclancy tclancy deleted the claude/pre-commit-config-5 branch May 14, 2026 17:05
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.

chore: no pre-commit config — formatting/linting not enforced

1 participant