Skip to content

feat: Add always_enabled configuration option for machines - #66

Merged
jantman merged 14 commits into
mainfrom
always-enable
Nov 27, 2025
Merged

feat: Add always_enabled configuration option for machines#66
jantman merged 14 commits into
mainfrom
always-enable

Conversation

@jantman

@jantman jantman commented Nov 16, 2025

Copy link
Copy Markdown
Contributor

Summary

Adds a new always_enabled boolean configuration option to machines that allows them to be always enabled without requiring RFID authentication. When enabled, machines display "Always On" and remain on
unless Oopsed or in maintenance lockout.

Changes

Configuration

  • Added optional always_enabled boolean field to machine configuration schema
  • Machines with always_enabled: true bypass RFID authentication entirely
  • Oops and lockout states still override always-enabled behavior

Core Implementation

  • Added ALWAYS_ON_DISPLAY_TEXT constant to MachineState
  • Modified MachineState.update() to handle always-enabled logic:
    • Skips RFID insert/remove processing
    • Sets relay to on and display to "Always On"
    • Maintains green status LED
  • Updated unoops() to restore always-on state after un-oopsing

Testing

  • Added 5 comprehensive integration tests covering:
    • Basic always-on behavior (relay on, "Always On" display)
    • Oops/un-oops behavior (respects oops state)
    • RFID insertion ignored
    • RFID removal ignored
    • Immediate enable on first server contact
  • Updated all existing test fixtures for new machine
  • All 146 tests passing with 100% code coverage

Documentation

  • Updated CLAUDE.md with configuration details

Usage Example

Add to machines.json:

{
  "dust-collector": {
    "authorizations_or": ["This list is ignored when always_enabled is true"],
    "always_enabled": true
  }
}

The machine will:

  • ✅ Always have relay enabled (unless Oopsed/Locked)
  • ✅ Display "Always On" on LCD
  • ✅ Ignore RFID card insertions and removals
  • ✅ Enable immediately on first server contact

Test Plan

  • All existing tests pass (146/146)
  • 100% code coverage maintained
  • Type checking passes (mypy)
  • Linting passes (pre-commit)
  • Security checks pass (safety)

Breaking Changes

None - this is a backward-compatible addition. Existing machines continue to work unchanged.

🤖 Generated with https://claude.com/claude-code

jantman and others added 12 commits November 16, 2025 08:21
Add comprehensive documentation for Claude Code to assist with
development, including common commands, architecture overview,
and key implementation details.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Add comprehensive implementation plan for always-enabled machine feature,
breaking down the work into 4 milestones with specific tasks and acceptance
criteria.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Milestone 1 complete: Added always_enabled configuration option to Machine model.

Changes:
- Updated CONFIG_SCHEMA to include optional always_enabled boolean property
- Added always_enabled attribute to Machine class with default value False
- Updated Machine.as_dict property to include always_enabled field
- Updated existing tests to expect always_enabled in as_dict output
- Updated feature document with Milestone 1 completion status

All existing tests passing (141/141).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Milestone 2 complete: Implemented always-enabled machine logic in MachineState.

Changes:
- Added ALWAYS_ON_DISPLAY_TEXT constant to MachineState class
- Updated MachineState.update() to handle always-enabled machines:
  * After oops handling, check if machine.always_enabled is True
  * If always-enabled and not oopsed/locked: set relay on, display "Always On", green LED
  * Skip RFID processing for always-enabled machines
- Updated unoops() method to restore always-on state when un-oopsing always-enabled machines
- Updated feature document with Milestone 2 completion status

All existing tests passing (141/141).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Milestone 3 in progress: Added comprehensive unit tests for always-enabled machines.

Changes:
- Created test_machine_always_enabled.py with 5 comprehensive tests:
  * test_always_enabled_basic: Tests relay on and "Always On" display
  * test_always_enabled_oopsed: Tests oops and un-oops behavior
  * test_always_enabled_ignores_rfid_insert: Tests RFID insert is ignored
  * test_always_enabled_ignores_rfid_remove: Tests RFID remove is ignored
  * test_always_enabled_first_contact: Tests immediate enable on first contact
- Added always-on-machine to fixtures/machines.json
- Fixed test expectations for machine count (5 -> 6)
- Updated Slack handler tests to include always-on-machine in status output
- Partially updated Prometheus test fixtures (LED metrics complete)

Status: 143/146 tests passing
Remaining: Complete Prometheus test fixture updates for all metric types

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Milestone 3 complete: All unit tests written and all test fixtures updated.

Changes:
- Fixed test_always_enabled_oopsed to use DELETE /api/machine/oops endpoint for un-oopsing
- Added always-on-machine metrics to all Prometheus test fixtures
- Fixed Prometheus LED metric ordering (always-on-machine after esp32test)

All 146 tests passing!

Test coverage:
- Basic always-on behavior ✓
- Oops/un-oops behavior ✓
- RFID insert/remove ignored ✓
- Immediate enable on first contact ✓

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Milestone 4 complete: All acceptance criteria met.

Changes:
- Updated CLAUDE.md with always_enabled configuration documentation
- Verified 100% test coverage
- Verified all nox sessions passing (tests, mypy, pre-commit, safety)
- Moved feature file to docs/features/completed/

Feature Summary:
Added always_enabled boolean option to machine configuration. When enabled:
- Machine is always enabled without RFID authentication
- Displays "Always On" on LCD
- Relay is always on (unless Oopsed or Locked)
- RFID card insertions/removals are ignored
- Immediately enabled on first server contact

Final Status:
✅ All 146 tests passing
✅ 100% code coverage
✅ All quality checks passing
✅ Full documentation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@jantman
jantman requested a review from Copilot November 16, 2025 14:46
@github-actions

github-actions Bot commented Nov 16, 2025

Copy link
Copy Markdown

Coverage

Coverage Report
FileStmtsMissCoverMissing
src/dm_mac
   __init__.py690100% 
   cli_utils.py150100% 
   neongetter.py1860100% 
   slack_handler.py1500100% 
   utils.py250100% 
src/dm_mac/models
   __init__.py00100% 
   machine.py352199%643
   users.py940100% 
src/dm_mac/views
   __init__.py00100% 
   api.py220100% 
   machine.py690100% 
   prometheus.py980100% 
TOTAL1080199% 

Tests Skipped Failures Errors Time
148 0 💤 0 ❌ 0 🔥 13.392s ⏱️

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds a new always_enabled configuration option for machines that allows them to be always enabled without requiring RFID authentication. Machines with this setting display "Always On" and remain on unless Oopsed or in maintenance lockout.

Key Changes

  • Added always_enabled boolean field to machine configuration schema with appropriate validation
  • Updated MachineState.update() to bypass RFID processing for always-enabled machines and maintain "Always On" state
  • Added 5 comprehensive integration tests covering basic behavior, Oops handling, RFID ignoring, and immediate enablement

Reviewed Changes

Copilot reviewed 11 out of 13 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/dm_mac/models/machine.py Added always_enabled configuration field, constant, and state management logic; updated unoops() to restore always-on state
tests/views/test_machine_always_enabled.py New test suite with 5 tests covering always-enabled functionality
tests/fixtures/machines.json Added always-on-machine test fixture
tests/models/test_machine.py Updated machine count assertions and as_dict expectations to include always_enabled field
tests/views/test_prometheus.py Updated Prometheus metrics test fixtures to include always-on-machine entries
tests/test_slack_handler.py Updated Slack handler tests to include always-on-machine in status listings
CLAUDE.md Documented the new always_enabled configuration option
docs/features/completed/always-enabled-machine.md Feature implementation documentation with milestones and status
docs/features/README.md Feature development process documentation
docs/source/hardware.rst Added M12 8-pin connector pinout image reference
hardware/v1_mcu/m12-8pinout.png Binary image file for hardware documentation
pyproject.toml Version bump from 0.2.3 to 0.3.0 (minor version for new feature)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/dm_mac/models/machine.py
Comment thread src/dm_mac/models/machine.py
Comment thread tests/fixtures/machines.json Outdated
Comment thread src/dm_mac/models/machine.py
Resolved all 4 Copilot review comments:

1. Updated unlock() to restore always-on state for always-enabled machines
   - Checks machine.always_enabled and restores appropriate state
   - Ensures consistent behavior after maintenance lockout

2. Updated _handle_reboot() to restore always-on state for always-enabled machines
   - Checks machine.always_enabled and sets appropriate state directly
   - No longer relies on subsequent always-enabled logic in update()

3. Fixed misleading comment in test fixture
   - Changed "This list is ignored" to "Not used for authentication"
   - Clarifies that authorizations_or is a schema requirement

4. Added last_update timestamp to always-enabled path
   - Ensures consistent timestamp tracking across all state change paths
   - Prevents stale last_update for always-enabled machines

Testing:
- Added 2 new integration tests:
  * test_always_enabled_unlock - verifies state restoration after unlock
  * test_always_enabled_reboot - verifies state restoration after reboot
- All 148 tests passing
- 100% code coverage maintained

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 11 out of 13 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/dm_mac/models/machine.py Outdated
Implemented RFID value tracking for always-enabled machines to support
auditing while maintaining the always-on state. RFID insertions and
removals are now logged with user information and session duration.

Changes:
- Added _handle_rfid_tracking_always_enabled() method to track RFID
  changes without affecting machine state
- Updated always-enabled logic in update() to call tracking method
  when RFID value changes
- Updated test_always_enabled_ignores_rfid_insert() to verify RFID
  values, current_user, and rfid_present_since are tracked
- Updated test_always_enabled_ignores_rfid_remove() to verify RFID
  removal clears tracked values
- Fixed test RFID values to use "8114346998" (Ashley Williams) instead
  of invalid "1234567890"

All 148 tests passing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 11 out of 13 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@jantman
jantman merged commit 9d8fe05 into main Nov 27, 2025
22 checks passed
@jantman
jantman deleted the always-enable branch November 27, 2025 19:03
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