Skip to content

Diagnostic sensors#1414

Open
lehneres wants to merge 15 commits intobasnijholt:mainfrom
lehneres:diagnostic-sensors
Open

Diagnostic sensors#1414
lehneres wants to merge 15 commits intobasnijholt:mainfrom
lehneres:diagnostic-sensors

Conversation

@lehneres
Copy link
Copy Markdown

Summary

This PR adds optional diagnostic status sensors per light, giving visibility into whether Adaptive Lighting is actively controlling a light, blocked, manually overridden, or idle. It also aggregates status across multiple profiles controlling the same light.

Key Changes

  • New sensor platform (sensor.py) with one diagnostic sensor per light.
  • Status tracking in switch.py with per-profile and combined state.
  • Constants and attributes in const.py.
  • Option enable_diagnostic_sensors (default false) to toggle sensors per profile.
  • Ensures new entities are enabled when diagnostics are turned on.
  • Docs: docs/status_sensors.md and README note.

Behavior

  • Sensor states: inactive, adopting, active, manual_override, blocked, error.
  • Combined state priority: error > manual_override > adopting > active > blocked > inactive.
  • status_profiles attribute exposes per-profile detail.
  • If light is off, combined state is inactive.
  • Sensors are created only when enable_diagnostic_sensors is true.

Testing

  • Manual validation in HA:
    • Toggle diagnostic sensors on/off in options.
    • Verify entities appear without restart.
    • Confirm state transitions during adaptation and manual override.
  • python3 -m py_compile on updated files.

Notes

  • No breaking changes; feature is opt-in.
  • Sensors are diagnostic category and grouped with their light device when possible.

lehneres and others added 5 commits January 23, 2026 20:13
- Implemented per-light status tracking to provide detailed states like "active," "inactive," "blocked," and "manual override."
- Added new `sensor` platform for enhanced diagnostics.
- Updated README to include diagnostic status sensors.
- Introduced various status-related attributes and methods for better monitoring and troubleshooting.
Added a new configuration option `enable_diagnostic_sensors` to control the creation of per-light status sensors in the Adaptive Lighting integration. Sensors are now disabled by default and can be enabled via the options flow. Updated related files, including initialization logic, constants, translations, and documentation.
@lehneres lehneres marked this pull request as ready for review January 24, 2026 09:09
@lehneres lehneres requested a review from basnijholt as a code owner January 24, 2026 09:09
@lehneres lehneres marked this pull request as draft January 25, 2026 13:53
@lehneres lehneres marked this pull request as ready for review January 26, 2026 14:14
@lehneres lehneres marked this pull request as draft January 28, 2026 09:51
@lehneres lehneres marked this pull request as ready for review January 29, 2026 04:49
Copy link
Copy Markdown

@florianhorner florianhorner left a comment

Choose a reason for hiding this comment

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

Review Summary

Useful diagnostic feature — knowing why a light isn't adapting is a common user question. The architecture (opt-in sensors per profile) is well-chosen. Some issues to address before merge.

Critical Issues

1. Duplicate _expand_light_groups in sensor.py

This function already exists in switch.py (line 588). Import it from there instead of reimplementing — duplicated logic will drift over time.

2. Redundant double set_light_status(STATUS_ACTIVE, "apply")

Called in both _adapt_light AND execute_cancellable_adaptation_calls. The first call is immediately overwritten by the second. Remove the first — it's dead code.

3. Test/framework deps in runtime dependencies

pytest-asyncio==1.3.0 and homeassistant>=2024.12.5 are in [project] runtime deps. Both belong in [dependency-groups]. HA custom components cannot declare HA itself as a pip dependency.

Quality Issues

4. ensure_status_sensors_enabled overrides user-disabled entities

If a user intentionally disables a diagnostic sensor via the UI, this re-enables it. Check disabled_by != EntityRegistryEntryDisabler.USER before re-enabling.

5. STATUS constants should be StrEnum

The codebase already uses StrEnum for TakeOverControlMode. Plain strings miss typo detection. Define LightStatus(StrEnum).

6. Unused return type change

execute_cancellable_adaptation_calls return type changed to bool but neither call site checks the return value.

7. _status_priority allocates a new dict on every call

This is constant data — make it a module-level STATUS_PRIORITY constant.

Efficiency Issues

8. get_combined_status called twice per sensor write

Both native_value and extra_state_attributes call it independently. Cache the result.

9. get_manual_control_attributes called twice

Same pattern in extra_state_attributes. Cache it.

10. Unbounded light_status dict

Never pruned when lights are removed. Add self.light_status.pop(light, None) in reset().

11. Bare async_dispatcher_send bypasses dedup

In state_changed_event_listener, the dispatcher fires even when no status changed.


Main blockers are #1 (duplicated code) and #3 (runtime deps). Looking forward to the next iteration! 👍

@lehneres lehneres force-pushed the diagnostic-sensors branch from 337bc93 to 4751047 Compare March 28, 2026 12:12
@lehneres lehneres force-pushed the diagnostic-sensors branch from 4751047 to cc64fbf Compare March 28, 2026 12:13
lehneres and others added 2 commits March 28, 2026 14:31
- Centralized light group expansion logic in `helpers.py` with `expand_light_groups`.
- Simplified light status management using a new `LightStatus` enum with priority mapping.
- Replaced scattered status strings with `LightStatus` constants for consistency.
- Updated dev dependencies in `pyproject.toml` and reorganized requirements.
- Enhanced `LightStatusInfo` for better status tracking and debugging.
@lehneres
Copy link
Copy Markdown
Author

Based on the feedback found on the pull request PR #1414, here are the 11 key points and their current status after the recent refactoring:

  1. Refactor Status Constants to Enum

    • Feedback: Use a StrEnum for status values like ACTIVE, INACTIVE, etc.
    • Status: ✅ Fixed. Implemented LightStatus(StrEnum) in const.py.
  2. Optimize Status Priority Mapping

    • Feedback: Move STATUS_PRIORITY into the enum or use a more efficient lookup to avoid dict allocations in property calls.
    • Status: ✅ Fixed. STATUS_PRIORITY is now a ClassVar inside the LightStatus enum.
  3. Minimize extra_state_attributes Recalculation

    • Feedback: The status sensor was recalculating all attributes on every access.
    • Status: ✅ Fixed. Added a _cached_status attribute and optimized extra_state_attributes in sensor.py.
  4. Respect User-Disabled Entities

    • Feedback: Ensure ensure_status_sensors_enabled does not re-enable sensors if the user explicitly disabled them.
    • Status: ❓ Question. I would have thought entry.disabled_by is not None will have the desired effect of retaining user-disabled status?
  5. Prune Stale Light Data

    • Feedback: The light_status dictionary should be cleared during a reset to avoid memory leaks or stale data.
    • Status: ✅ Fixed. manager.reset() now prunes the light_status dictionary.
  6. Dependency Management Cleanup

    • Feedback: Move homeassistant and pytest-asyncio from dependencies to dev-dependencies in pyproject.toml.
    • Status: ✅ Fixed. Updated pyproject.toml and uv.lock.
  7. Reduce Event Flooding

    • Feedback: Avoid calling async_dispatcher_send and set_light_status if the state hasn't actually changed.
    • Status: ✅ Fixed. Added guards in switch.py to prevent redundant updates.
  8. Shared Helper for Light Group Expansion

    • Feedback: Don't duplicate the logic for expanding light groups in sensor.py and switch.py.
    • Status: ✅ Fixed. Moved expand_light_groups to helpers.py.
  9. Remove Redundant Code in sensor.py

    • Feedback: Use get_friendly_name from helpers.py instead of custom logic.
    • Status: ✅ Fixed. Refactored AdaptiveLightingStatusSensor to use existing helpers.
  10. Fix Circular Dependencies and Move Data Classes

    • Feedback: LightStatusInfo was causing issues or was misplaced.
    • Status: ✅ Fixed. Moved LightStatusInfo to const.py for shared access.
  11. Python Version Compatibility

    • Feedback: The type alias syntax (PEP 695) is Python 3.12+, but the project supports 3.10.
    • Status: ✅ Fixed. Replaced with TypeAlias from typing_extensions or typing.

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