diff --git a/Common/Motion Light/README.md b/Common/Motion Light/README.md index d6c3c43..da1ec70 100644 --- a/Common/Motion Light/README.md +++ b/Common/Motion Light/README.md @@ -17,7 +17,7 @@ This blueprint creates a smart motion-activated light control system. It handles - Day/Night mode (different light settings based on time) - Scene support (activate scenes instead of light parameters) - Dim before off (visual warning before turning off) -- Manual override detection (stops automation if user changes light) +- Manual override detection (stops automation if user changes light) with configurable grace period - Brightness threshold (only trigger if light is dim) - Custom light parameters (brightness, color, etc.) - Callback actions for enable/disable/manual events @@ -37,7 +37,8 @@ The automation tracks these states via persistent storage: ## Behavior Notes - Will NOT turn on light if it's already ON (prevents hijacking user control) -- If user changes light while automation is active, enters MANUAL mode +- If user changes light while automation is active, enters MANUAL mode (after grace period) +- Grace period prevents false manual overrides from delayed device state reports (e.g., Zigbee) - MANUAL mode exits when light is turned OFF (by any means) - Timeout delay only applies when turning OFF (motion cleared) - Time conditions support overnight windows (e.g., 22:00 to 06:00) diff --git a/Common/Motion Light/blueprint.yaml b/Common/Motion Light/blueprint.yaml index 8ee373c..0538945 100644 --- a/Common/Motion Light/blueprint.yaml +++ b/Common/Motion Light/blueprint.yaml @@ -37,6 +37,7 @@ blueprint: - switch - group - light + - input_boolean multiple: true condition_switches: @@ -384,6 +385,22 @@ blueprint: step: 1 unit_of_measurement: "seconds" + manual_override_grace_period: + name: Manual Override Grace Period (seconds) + description: > + After the automation turns on a light, ignore state changes for + this many seconds to avoid false manual override detection. + Some devices (especially Zigbee) report delayed state updates + that can be mistaken for manual control. Increase this value + if you see false manual overrides in the debug log. + default: 10 + selector: + number: + min: 0 + max: 30 + step: 1 + unit_of_measurement: "seconds" + # ------------------------------------------------------------------------- # Debug # ------------------------------------------------------------------------- @@ -546,6 +563,7 @@ variables: min_on_duration: !input min_on_duration brightness_threshold: !input brightness_threshold transition_duration: !input transition_duration + manual_override_grace_period: !input manual_override_grace_period # --------------------------------------------------------------------------- # Target Device Resolution @@ -591,11 +609,22 @@ variables: # Reference light for state checks (first available) reference_light: "{{ resolved_all_lights[0] if resolved_all_lights | length > 0 else none }}" - # Check if any device is on + # Check if any device is on (respects brightness_threshold) any_device_on: > - {% set lights_on = resolved_all_lights | select('is_state', 'on') | list | length > 0 %} + {% set ns = namespace(lights_on=false) %} + {% for light in resolved_all_lights %} + {% if is_state(light, 'on') %} + {% if brightness_threshold | int(0) > 0 %} + {% if state_attr(light, 'brightness') | int(0) >= brightness_threshold | int(0) %} + {% set ns.lights_on = true %} + {% endif %} + {% else %} + {% set ns.lights_on = true %} + {% endif %} + {% endif %} + {% endfor %} {% set switches_on = resolved_all_switches | select('is_state', 'on') | list | length > 0 %} - {{ lights_on or switches_on }} + {{ ns.lights_on or switches_on }} all_devices_off: "{{ not any_device_on }}" @@ -884,9 +913,20 @@ action: # ----- Sub-case: User manually changed the light ----- # Transition from ENABLED to MANUAL (user took control) + # Grace period: ignore state changes shortly after the automation + # turns on the light to avoid false manual override detection. + # Some devices (especially Zigbee) report delayed state updates. - conditions: - condition: template - value_template: "{{ state_is_enabled }}" + value_template: > + {% set last_ts = automation_state.get(state_motion_light_last_action_timestamp, none) %} + {% set grace = (transition_duration | float(0)) + (manual_override_grace_period | float(10)) %} + {% if state_is_enabled and last_ts is not none %} + {% set parsed = last_ts | as_datetime %} + {{ parsed is none or (now() - parsed).total_seconds() > grace }} + {% else %} + {{ state_is_enabled }} + {% endif %} sequence: # BUG FIX: Fixed YAML structure - was 'data: >' instead of 'data:' with 'value: >' - service: input_text.set_value diff --git a/manifest.json b/manifest.json index 4928ba5..6cb5a26 100644 --- a/manifest.json +++ b/manifest.json @@ -1,3 +1,3 @@ { - "version": "2.0.0" + "version": "2.2.3" }