mdeweerd / zha-toolkit

🧰 Zigbee Home Assistant Toolkit - service for "rare" Zigbee operations using ZHA on Home Assistant
GNU General Public License v3.0
193 stars 29 forks source link

[Feature Request] Force state update #113

Closed HarvsG closed 1 year ago

HarvsG commented 1 year ago

A common issue with ZHA is that the state does not update correctly for a few devices in HA after a batch or light group turn_off/turn_on command.

It would be great to have a service call to request state updates from device(s)/groups that we could call after a batch command.

mdeweerd commented 1 year ago

Can you clarify what you expect (and how you imagine that would work).

Currently zha-toolkit allows you to read attributes, and you can also update a state or state attribute with that data. I also added the possibility to use a template like expression to define the value to write to the state.

When an attribute is read, my observation is that it will also be picked up by the usual mechanisms to update state values. For instance, when I read the battery percentage or the temperature of a device, the UI will update those values while no explicit state update was performed.

It seems to me that you can achieve your goal by triggering on the group/batch update and then read the relevant on/off states.

One other method that may work - depending on the device - is to configure reporting of the relevant zigbee attributes. I personnaly do have some lights that do not report their light statuses properly, but in principle that should work when the device is properly implemented.

When a zigbee attribute has reporting capability, you can set it to report when it changes, and also to report at least every X seconds. For instance you could set it to report every 15 minutes at least.

To know if the attribute is reportable, you can perform a zha_toolkit.scan_device. If your device implements the attribute discovery functionality, the resulting report will indicate which attributes are reportable.

HarvsG commented 1 year ago

Ok sounds as if the feature is already implemented. Just for clarity, which service would I call if I just wanted to know the on/off status of a bulb?

mdeweerd commented 1 year ago

zha_toolkit.attr_read - On/Off cluster, cluster 6, attribute 0.

Example:

service: zha_toolkit.attr_read
data:
  ieee: light.tz3000_dbou1ap4_ts0505a_9afc91fe_level_light_color_on_off
  cluster: 6
  attribute: 0
HarvsG commented 1 year ago

Solved: I use this in an automation to force light state updates:

alias: "[Lighting] Update states"
description: >-
  Sometime, particularly when batch turn on/turn off operations are performed.
  Lights' states in home assistant don't get updated accurately. This automation
  forces a state update.
trigger: []
condition: []
action:
  - repeat:
      for_each: >-
        {{states.light | map(attribute='entity_id') | select('in',
        integration_entities('zha')) | list }}
      sequence:
        - continue_on_error: true
          service: zha_toolkit.attr_read
          data:
            ieee: "{{ repeat.item }}"
            cluster: 6
            attribute: 0
mode: single
mdeweerd commented 1 year ago

Great, added as an example: https://github.com/mdeweerd/zha-toolkit/blob/main/examples/script_request_all_light_states.yaml .

HarvsG commented 1 year ago

Hmm When I call the service from the Dev tools UI, HA actually updates the state. But when I call it from an automation, no state change actually happens.... (though the call_service completes)

alias: "[Lighting] update state after change"
description: ""
trigger:
  - platform: event
    event_type: call_service
    event_data:
      domain: light
      service: turn_off
    id: turn_off
  - platform: event
    event_type: call_service
    event_data:
      domain: light
      service: turn_on
    id: turn_on
condition: []
action:
  - delay:
      hours: 0
      minutes: 0
      seconds: 1
      milliseconds: 0
  - choose:
      - conditions:
          - condition: trigger
            id: turn_on
        sequence:
          - repeat:
              for_each: "{{ trigger.event.data.service_data.entity_id}}"
              sequence:
                - if:
                    - condition: template
                      value_template: "{{is_state(repeat.item,'off')}}"
                  then:
                    - service: zha_toolkit.attr_read
                      data:
                        ieee: "{{ repeat.item }}"
                        cluster: 6
                        attribute: 0
                        event_done: zha_correction_event
                        fail_exception: false
                        tries: 3
                    - delay:
                        hours: 0
                        minutes: 0
                        seconds: 1
                        milliseconds: 0
                    - if:
                        - condition: template
                          value_template: "{{is_state(repeat.item,'on')}}"
                      then:
                        - service: persistent_notification.create
                          data:
                            message: "{{repeat.item}} was corrected"
                      else:
                        - service: persistent_notification.create
                          data:
                            message: "{{repeat.item}} was NOT corrected"
      - conditions:
          - condition: trigger
            id: turn_off
        sequence:
          - repeat:
              for_each: "{{ trigger.event.data.service_data.entity_id}}"
              sequence:
                - if:
                    - condition: template
                      value_template: "{{is_state(repeat.item,'on')}}"
                  then:
                    - service: zha_toolkit.attr_read
                      data:
                        ieee: "{{ repeat.item }}"
                        cluster: 6
                        attribute: 0
                        event_done: zha_correction_event
                        fail_exception: false
                        tries: 3
                    - delay:
                        hours: 0
                        minutes: 0
                        seconds: 1
                        milliseconds: 0
                    - if:
                        - condition: template
                          value_template: "{{is_state(repeat.item,'off')}}"
                      then:
                        - service: persistent_notification.create
                          data:
                            message: "{{repeat.item}} was corrected"
                      else:
                        - service: persistent_notification.create
                          data:
                            message: "{{repeat.item}} was NOT corrected"
mode: parallel

Event data

event_type: zha_correction_event
data:
  zha_toolkit_version: v0.8.26
  zigpy_version: 0.51.5
  zigpy_rf_version: 0.19.0
  ieee_org: light.study_bulb_1
  ieee: 0c:43:14:ff:fe:cb:c5:61
  command: attr_read
  command_data: null
  start_time: "2022-12-02T23:48:32.017610+00:00"
  errors: []
  params:
    cluster_id: 6
    attr_id: 0
    dir: 0
    tries: 3
    expect_reply: true
    args: []
    event_done: zha_correction_event
    fail_exception: true
    read_before_write: true
    read_after_write: true
  attr_type: "0x10"
  write_is_equal: false
  result_read:
    - "0": 0
    - {}
  success: true
origin: LOCAL
time_fired: "2022-12-02T23:48:32.078669+00:00"
context:
  id: 01GKAKVQ6EKBAHTSAVN6Z3XBR5
  parent_id: null
  user_id: null

And UI still lies: image

HarvsG commented 1 year ago

Increasing the delay to 7 seconds seems to work nicely.

alias: "[Lighting] update state after change"
description: ""
trigger:
  - platform: event
    event_type: call_service
    event_data:
      domain: light
      service: turn_off
    id: turn_off
  - platform: event
    event_type: call_service
    event_data:
      domain: light
      service: turn_on
    id: turn_on
condition: []
action:
  - delay:
      hours: 0
      minutes: 0
      seconds: 7
      milliseconds: 0
  - choose:
      - conditions:
          - condition: trigger
            id: turn_on
        sequence:
          - repeat:
              for_each: "{{ trigger.event.data.service_data.entity_id}}"
              sequence:
                - if:
                    - condition: template
                      value_template: "{{is_state(repeat.item,'off')}}"
                  then:
                    - service: zha_toolkit.attr_read
                      data:
                        ieee: "{{ repeat.item }}"
                        cluster: 6
                        attribute: 0
                        event_done: zha_correction_event
                        fail_exception: false
                        tries: 3
      - conditions:
          - condition: trigger
            id: turn_off
        sequence:
          - repeat:
              for_each: "{{ trigger.event.data.service_data.entity_id}}"
              sequence:
                - if:
                    - condition: template
                      value_template: "{{is_state(repeat.item,'on')}}"
                  then:
                    - service: zha_toolkit.attr_read
                      data:
                        ieee: "{{ repeat.item }}"
                        cluster: 6
                        attribute: 0
                        event_done: zha_correction_event
                        fail_exception: false
                        tries: 3
mode: parallel
mdeweerd commented 1 year ago

It could be that the light bulb gives a wrong report on its state after the read. I have a LIDL (tuya based) bulb that is just reporting it's on/off state incorrectly. https://github.com/zigpy/zha-device-handlers/issues/1303

I wanted to suggest to increase your delay, but you just posted that that helps. The thing is that this leaves the time for the previous command (list) to complete - 6 or 7 seconds is about the best time.