Closed burmistrzak closed 4 days ago
Previously I added this so that but actually showed that something was happening. Perhaps they changed the Home.app since the initial implementation though.
Do you have an "official" HomeKit window covering so we can peek at when they update their states?
Do you have an "official" HomeKit window covering so we can peek at when they update their states?
Sure! Here are some examples:
Both only seem to update their current position once stopped. They also don't support HoldPosition
.
This one is interesting. It supports HoldPosition
and actually does update its CurrentPosition
while in motion!
However, I now know what's actually going on here. Give me a second to verify... ⚙️
Edit: Below are all position
values reported by a Bosch BMCT-SLZ when fully closing a covering from HomeKit. Latest values on top.
0
0
0
9
19
29
39
49
59
69
79
89
98
99
99
0
100
@itavero Ok, here's what's going on:
We're continually updating TargetPosition
alongside CurrentPosition
.
That's unfortunately incorrect. TargetPosition
should only be set once, ideally before the covering starts moving.
I can confirm that all of the aforementioned accessories behave in exactly this manner, that is, writing TargetPosition
only once per movement.
@itavero Did some further testing, and logged HAP commands coming from a Bosch Shutter Control Gen. I when using the in-wall switch directly.
a.
PositionState
changes from STOPPED
to DECREASING
TargetPosition
is set to 0%
PositionState
changes from DECREASING
to STOPPED
CurrentPosition
is set to 63%
TargetPosition
is set to 63%
And fully open from HomeKit:
b.
TargetPosition
is set to 100%
PositionState
changes from STOPPED
to INCREASING
PositionState
changes from INCREASING
to STOPPED
CurrentPosition
is set to 100%
‼️ Key takeaway here's that TargetPosition
should only be set more than once in case a covering gets STOPPED
before reaching the initial TargetPosition
(see a.2. != a.5.), otherwise Home.app gets confused.
Hmm, I went through the code, but I'm a bit unsure about where these TargetPosition
updates are actually coming from.AFAICT, only CurrentPosition
should receive periodic updates... 🤔
Edit: After adding some non-invasive logging (to print key functions and their arguments in cover.ts
), I saw the following:
homebridge-1 | updateState:
homebridge-1 | didStop: true
homebridge-1 | latestPosition: 100
homebridge-1 | this.positionCurrent: -1
homebridge-1 | scaleAndUpdateCurrentPosition: 100
homebridge-1 | isStopped: true
homebridge-1 | updateState:
homebridge-1 | didStop: true
homebridge-1 | latestPosition: 0
homebridge-1 | this.positionCurrent: -1
homebridge-1 | scaleAndUpdateCurrentPosition: 0
homebridge-1 | isStopped: true
homebridge-1 | updateState:
homebridge-1 | didStop: true
homebridge-1 | latestPosition: 100
homebridge-1 | this.positionCurrent: 100
homebridge-1 | scaleAndUpdateCurrentPosition: 100
homebridge-1 | isStopped: true
homebridge-1 | updateState:
homebridge-1 | didStop: true
homebridge-1 | latestPosition: 0
homebridge-1 | this.positionCurrent: 0
homebridge-1 | scaleAndUpdateCurrentPosition: 0
homebridge-1 | isStopped: true
homebridge-1 | handleSetTargetPosition: 100
homebridge-1 | startOrRestartUpdateTimer
homebridge-1 | updateState:
homebridge-1 | didStop: true
homebridge-1 | latestPosition: 0
homebridge-1 | this.positionCurrent: 0
homebridge-1 | scaleAndUpdateCurrentPosition: 0
homebridge-1 | isStopped: true
homebridge-1 | updateState:
homebridge-1 | didStop: true
homebridge-1 | latestPosition: 100
homebridge-1 | this.positionCurrent: 0
homebridge-1 | scaleAndUpdateCurrentPosition: 100
homebridge-1 | isStopped: true
homebridge-1 | updateState:
homebridge-1 | didStop: true
homebridge-1 | latestPosition: 1
homebridge-1 | this.positionCurrent: 100
homebridge-1 | scaleAndUpdateCurrentPosition: 1
homebridge-1 | isStopped: true
homebridge-1 | updateState:
homebridge-1 | didStop: true
homebridge-1 | latestPosition: 1
homebridge-1 | this.positionCurrent: 1
homebridge-1 | scaleAndUpdateCurrentPosition: 1
homebridge-1 | isStopped: true
homebridge-1 | updateState:
homebridge-1 | didStop: true
homebridge-1 | latestPosition: 2
homebridge-1 | this.positionCurrent: 1
homebridge-1 | scaleAndUpdateCurrentPosition: 2
homebridge-1 | isStopped: true
homebridge-1 | updateState:
homebridge-1 | didStop: true
homebridge-1 | latestPosition: 11
homebridge-1 | this.positionCurrent: 2
homebridge-1 | scaleAndUpdateCurrentPosition: 11
homebridge-1 | isStopped: true
homebridge-1 | updateState:
homebridge-1 | didStop: true
homebridge-1 | latestPosition: 21
homebridge-1 | this.positionCurrent: 11
homebridge-1 | scaleAndUpdateCurrentPosition: 21
homebridge-1 | isStopped: true
homebridge-1 | updateState:
homebridge-1 | didStop: true
homebridge-1 | latestPosition: 31
homebridge-1 | this.positionCurrent: 21
homebridge-1 | scaleAndUpdateCurrentPosition: 31
homebridge-1 | isStopped: true
homebridge-1 | updateState:
homebridge-1 | didStop: true
homebridge-1 | latestPosition: 41
homebridge-1 | this.positionCurrent: 31
homebridge-1 | scaleAndUpdateCurrentPosition: 41
homebridge-1 | isStopped: true
homebridge-1 | updateState:
homebridge-1 | didStop: true
homebridge-1 | latestPosition: 51
homebridge-1 | this.positionCurrent: 41
homebridge-1 | scaleAndUpdateCurrentPosition: 51
homebridge-1 | isStopped: true
homebridge-1 | updateState:
homebridge-1 | didStop: true
homebridge-1 | latestPosition: 61
homebridge-1 | this.positionCurrent: 51
homebridge-1 | scaleAndUpdateCurrentPosition: 61
homebridge-1 | isStopped: true
homebridge-1 | updateState:
homebridge-1 | didStop: true
homebridge-1 | latestPosition: 71
homebridge-1 | this.positionCurrent: 61
homebridge-1 | scaleAndUpdateCurrentPosition: 71
homebridge-1 | isStopped: true
homebridge-1 | updateState:
homebridge-1 | didStop: true
homebridge-1 | latestPosition: 81
homebridge-1 | this.positionCurrent: 71
homebridge-1 | scaleAndUpdateCurrentPosition: 81
homebridge-1 | isStopped: true
homebridge-1 | updateState:
homebridge-1 | didStop: true
homebridge-1 | latestPosition: 91
homebridge-1 | this.positionCurrent: 81
homebridge-1 | scaleAndUpdateCurrentPosition: 91
homebridge-1 | isStopped: true
homebridge-1 | updateState:
homebridge-1 | didStop: true
homebridge-1 | latestPosition: 100
homebridge-1 | this.positionCurrent: 91
homebridge-1 | scaleAndUpdateCurrentPosition: 100
homebridge-1 | isStopped: true
homebridge-1 | updateState:
homebridge-1 | didStop: true
homebridge-1 | latestPosition: 100
homebridge-1 | this.positionCurrent: 100
homebridge-1 | scaleAndUpdateCurrentPosition: 100
homebridge-1 | isStopped: true
homebridge-1 | updateState:
homebridge-1 | didStop: true
homebridge-1 | latestPosition: 100
homebridge-1 | this.positionCurrent: 100
homebridge-1 | scaleAndUpdateCurrentPosition: 100
homebridge-1 | isStopped: true
@itavero It seems to me, that isStopped
shouldn't always be true. Right?
Edit#2: Also, another big reason for the weird UX glitches in Home.app is that we're setting see below 👇CurrentPosition
to the same value as TargetPosition
, right as the covering starts to move.
Edit#3: Reverting this particular commit https://github.com/Koenkk/zigbee-herdsman-converters/commit/dfd46d6c8c7f4781ded45fc00b023660a6bee63e (i.e. not returning the set value) makes the UX noticeably better, but isStopped
being off still seems to be the main issue here.
Thanks for looking into this. I did not have time to process all your information, but I can see indeed some opportunities for improvements.
I'm not sure when I'll have time to dive into this myself as well, but I'm open to accepting a PR to improve this behavior.
Thanks for looking into this. I did not have time to process all your information, but I can see indeed some opportunities for improvements.
No worries, it's indeed a lot. Let me quickly summarize my key findings for you:
TargetPosition
is always updated alongside CurrentPosition
.CurrentPosition
during movement is fine, as long as TargetPosition
is only set once at the beginning.TargetPosition
and CurrentPosition
must be set, together alongside PositionState.STOPPED
, in case the covering gets abruptly stopped (e.g. by in-wall switch) before reaching the initial target position.PositionState
correctly, is more important than constant CurrentPosition
updates.I'm not sure when I'll have time to dive into this myself as well, but I'm open to accepting a PR to improve this behavior.
I'm happy to provide a PR, but probably will need a little bit of support, because I'm not yet super familiar with the codebase. 😊 Please let me know, if you have any suggestions to get me started.
@itavero I guess we can approach this from three different directions:
cover.ts
to provide a perfect experience with a select few, and a good enough experience with all other devices.@itavero I did some more in depth analysis of the plugin's current behavior with the Bosch BCMT-SLZ, and here's what I've found:
isStopped
. Corresponding logs here and here https://github.com/itavero/homebridge-z2m/blob/c02eb2d668dd3e8a021aac529a6bc45aca14a661/src/converters/cover.ts#L257-L263PositionState
is not updated when controlled from outside of HomeKit, e.g. in-wall switch or MQTT.Previously I added this so that but actually showed that something was happening. Perhaps they changed the Home.app since the initial implementation though.
Looks to be the case. 😬 Home.app on iOS seems to be a bit more tolerant, but on macOS, the UX is even worse.
The sooner we can get WindowCovering
into compliance, the less issues will crop up with every new release.
Also, what's the deal with that updateTimer
thingy? Why are we manually polling for updates when there's reporting? 🤔
@itavero I have an update for you. 😎
The Good: I have a 99% HAP-compliant WindowCovering
implementation.
The Bad: It requires motor_state
to be exposed.
There's unfortunately no way around it. We need some kind of "direction of movement" indicator from the device itself , otherwise we're just guessing based on position
alone, and that's not good enough.
By default, your original code is used for all devices, unless motor_state
is detected, then my stuff takes over.
I'll publish a gist of the compiled JS asap, and update https://github.com/itavero/homebridge-z2m/pull/854 with a proper TypeScript implementation later. Cool?
@itavero So, https://github.com/itavero/homebridge-z2m/pull/854 is ready for review! 🤓
I hope I'll have some time in the next week or so to review it. Thanks!
Yey! Thanks for merging! 🥳
Is there an existing issue for this?
Describe the bug
As far as I understand the HAP specification, we shouldn't update the
TargetPosition
ofWindowCovering
accessory while it's still moving. This is especially true when a open/close command came directly from Home.app via user input.Otherwise, the UX is kind awful and UI elements are quickly jumping around.
All certified accessories I've seen so far, update their
TargetPosition
only once before they're starting to move. I suggest we follow their example and adjust the current implementation accordingly.Related devices
Related Devices
No response
Steps To Reproduce
A)
WindowCovering
service in Home.app to fully open (or close) a coveringB)
WindowCovering
service in Home.app to reveal controlsExpected behavior
A)
WindowCovering
service in Home.app to e.g. fully open a coveringB)
WindowCovering
service in Home.app to reveal controlsDevice entry
Status update
Messages from this plugin
No response
This plugin
1.11.0-beta.3
Homebridge
1.7.0
Zigbee2MQTT
1.36.1-dev
Homebridge Config UI X (if applicable)
No response