diff --git a/knx_cover_with_inverted_feedback.yaml b/knx_cover_with_inverted_feedback.yaml new file mode 100644 index 0000000..2a4779d --- /dev/null +++ b/knx_cover_with_inverted_feedback.yaml @@ -0,0 +1,168 @@ +blueprint: + name: "KNX Cover Control + Inverted Position Feedback (%)" + description: > + Control a Home Assistant cover entity via KNX group addresses (Up/Down + Stop) + and send back an inverted position feedback in percent to KNX whenever the + cover position changes (including changes initiated from Home Assistant). + Example: HA 0% → KNX 100%, HA 100% → KNX 0%. + domain: automation + homeassistant: + min_version: "2026.1.0" + + input: + cover_entity: + name: "Cover entity" + description: "Select the cover to control." + selector: + entity: + domain: cover + + ga_up_down: + name: "KNX group address Up/Down" + description: > + 1-bit command GA for Up/Down (typical: 0 = Up/Open, 1 = Down/Close). + KNX mapping: Up/Down command object. + selector: + text: + + ga_stop: + name: "KNX group address Stop" + description: > + 1-bit command GA for Stop/Step. + KNX mapping: Stop command object. + selector: + text: + + ga_position_feedback: + name: "KNX position feedback (%) group address" + description: > + GA for inverted position feedback in percent (DPT 5.001 / type 'percent'). + KNX mapping: Position status feedback (0..100%). + selector: + text: + + invert_direction: + name: "Invert Up/Down direction" + description: > + Enable if your KNX Up/Down semantics are reversed. + default: false + selector: + boolean: + + feedback_cooldown_s: + name: "Feedback cooldown (seconds)" + description: > + Minimum seconds between two KNX feedback telegrams to reduce bus traffic. + default: 1 + selector: + number: + min: 0 + max: 30 + step: 1 + mode: slider + +mode: queued +max: 10 + +variables: + cover_entity: !input cover_entity + ga_up_down: !input ga_up_down + ga_stop: !input ga_stop + ga_position_feedback: !input ga_position_feedback + invert_direction: !input invert_direction + feedback_cooldown_s: !input feedback_cooldown_s + +trigger: + # KNX Up/Down command + - trigger: knx.telegram + destination: !input ga_up_down + incoming: true + outgoing: false + group_value_write: true + group_value_read: false + group_value_response: false + id: knx_up_down + + # KNX Stop command + - trigger: knx.telegram + destination: !input ga_stop + incoming: true + outgoing: false + group_value_write: true + group_value_read: false + group_value_response: false + id: knx_stop + + # HA cover position change (attribute current_position) + - trigger: state + entity_id: !input cover_entity + attribute: current_position + id: ha_position_changed + +condition: [] + +action: + - choose: + + # -------- KNX Up/Down -------- + - conditions: + - condition: trigger + id: knx_up_down + sequence: + - variables: + raw: "{{ trigger.payload | int(0) }}" + # Normal semantics: 0 = Up/Open, 1 = Down/Close + is_down: >- + {% set down = (raw != 0) %} + {{ (not down) if invert_direction else down }} + - choose: + - conditions: "{{ is_down }}" + sequence: + - action: cover.close_cover + target: + entity_id: !input cover_entity + - conditions: "{{ not is_down }}" + sequence: + - action: cover.open_cover + target: + entity_id: !input cover_entity + + # -------- KNX Stop -------- + - conditions: + - condition: trigger + id: knx_stop + sequence: + - action: cover.stop_cover + target: + entity_id: !input cover_entity + + # -------- HA → KNX Inverted Position Feedback -------- + - conditions: + - condition: trigger + id: ha_position_changed + sequence: + - variables: + # HA: 0% = closed, 100% = open + # KNX inverted: 0% = open, 100% = closed + pos: >- + {% set p = state_attr(cover_entity, 'current_position') %} + {% if p is not none %} + {{ 100 - (p | int) }} + {% else %} + {{ none }} + {% endif %} + - condition: template + value_template: "{{ pos is not none }}" + - choose: + - conditions: "{{ feedback_cooldown_s | int(0) > 0 }}" + sequence: + - delay: + seconds: "{{ feedback_cooldown_s | int(0) }}" + - action: knx.send + data: + address: !input ga_position_feedback + type: percent + payload: "{{ pos }}" + response: false + + default: [] \ No newline at end of file