Repository navigation
Matter Thermostat: Shift the other setpoint on deadband violation - #3282
kshypachov wants to merge 1 commit into
Conversation
On thermostats with the AutoMode feature the driver rejected a heating setpoint above the cooling setpoint minus MinSetpointDeadBand (and a cooling setpoint below the heating setpoint plus the deadband) locally, so in Heat mode the heating setpoint could not be raised past the cooling setpoint and nothing was sent to the device. The Thermostat cluster specification expects the other setpoint to be moved instead; connectedhomeip does this since PR #35673 and fails the write only if the other setpoint would leave its limits. The driver now writes the other setpoint first and then the requested one, and keeps the local rejection only for that case. The moved setpoint is computed in 0.01 C units so that values at a limit are not rejected by floating point errors. The two writes are separate requests: the hub answered a single write request with both attributes with FAILURE. Assisted-by: Claude:claude-opus-5.5 luacheck Signed-off-by: Kirill Shypachov <kshypachov@outlook.com>
|
The current driver rejects setpoint changes locally even though the Thermostat server could satisfy the request by adjusting the complementary setpoint. However, after reviewing the implementation, I have some concerns about moving that adjustment logic into the Edge driver. 1. Deadband enforcement belongs to the Thermostat server!Matter 1.4.2, sections 4.3.9.13 and 4.3.9.14, defines the behavior when a setpoint change would violate For example, when This behavior is implemented in connectedhomeip#35673. Consequently, a compliant server should need only a single write to the requested attribute: device:send(
clusters.Thermostat.attributes.OccupiedHeatingSetpoint:write(
device, endpoint_id, requested_setpoint
)
)The server maintains the deadband, and the driver processes the resulting attribute reports! I would therefore favor removing the driver's cached-setpoint deadband rejection rather than replacing it with two client-side writes. 2. Two independent writes introduce consistency and concurrency risksThe proposed implementation writes the complementary setpoint first and the requested setpoint afterward. Although the ordering preserves the deadband when both writes succeed, these are separate Matter interactions. The first write may succeed while the second fails, leaving the device with a modified complementary setpoint but without the requested change. There is also a potentially more significant issue involving multiple Matter controllers. The decision to adjust the complementary setpoint is based on Consider: The request is valid against the device's actual state. However, the proposed implementation would calculate a new cooling setpoint of 25.5 °C based on the stale cached value and write it before updating the heating setpoint. This would unnecessarily lower the cooling setpoint from 27.0 °C to 25.5 °C. A single write to 3. Endpoint-specific feature detection and constraintsThe patch also retains two existing problems in the current driver:
These become especially relevant when calculating and writing complementary setpoints. For a device exposing multiple Thermostat endpoints, the current implementation could use the deadband, setpoint limits, or Auto feature information belonging to another endpoint. Any client-side validation should use metadata associated with the specific endpoint receiving the command. 4. Compatibility with older implementationsI understand the motivation for accommodating older Thermostat servers that reject deadband-violating writes rather than adjusting the complementary setpoint. However, I would prefer not to introduce additional Matter writes for every device to accommodate that behavior. A cleaner approach would be to use the specification-defined single-write behavior by default and investigate a compatibility fallback for servers demonstrably requiring it. Such a fallback would need careful handling of write responses, current device state, endpoint-specific constraints, and partial failures. Suggested approachFor the generic Matter Thermostat driver, I would recommend:
This would simplify the driver, avoid unnecessary state modifications, and keep responsibility for enforcing Matter's thermostat constraints where the specification places it. The existing test coverage could then be extended to verify that a setpoint request crossing the cached deadband still results in exactly one Matter write, without any client-side modification of the complementary setpoint. My concern is specifically about introducing additional client-side state management where the Matter server already has the necessary information and responsibility. |
|
Hi @ldeora Thank you for the detailed review. You are right, I will change the PR to a single write. The main argument for me is the stale cache. In my forum reply I wrote that the current driver depends on the same cache, but that was inaccurate. With a stale cache, the current driver can only reject a request by mistake, without changing anything on the device. My version can write a setpoint calculated from stale data to the device and overwrite a value set by another controller. That is worse. My device moves the other setpoint by itself: a single write of OccupiedHeatingSetpoint = 2200 with heating at 17.70 °C and cooling at 20.40 °C resulted in heating 22.00 °C and cooling 24.50 °C. What I plan to change:
Two clarifications:
One question. If the device rejects the write, there is no event, and the slider in the app stays where it was moved. On a local rejection the current driver emits the cached value, and the slider goes back. I saw this on my hub when the hub answered FAILURE to a write: the slider stayed in place until the device was reopened. I suggest reading the requested attribute after the write: the response arrives as a report, and the app shows the real value. What do you think? I will update the PR after testing on my hub. |
Check all that apply
Type of Change
Checklist
Description of Change
On thermostats with the AutoMode feature the driver rejects a heating setpoint above
cooling - MinSetpointDeadBand(and a cooling setpoint belowheating + MinSetpointDeadBand) locally, so for example in Heat mode the heating setpoint cannot be raised above the cooling setpoint minus the deadband, and nothing is sent to the device.The Thermostat cluster specification expects the other setpoint to be moved in this case; connectedhomeip does this since project-chip/connectedhomeip#35673 and rejects the write only if the other setpoint would leave its limits.
Ordering: written first, the other setpoint keeps the deadband against the current value of the requested side, and the requested value then keeps it against the new other setpoint. Servers that reject deadband violations (before #35673) therefore accept both writes, and servers that adjust by themselves find nothing to adjust.
The two writes are separate requests: on a hub with firmware 0.62 a single Write request with both attribute paths was answered with FAILURE for both paths and did not reach the device.
Forum: https://community.smartthings.com/t/incorrect-thermostat-behavior-in-mode-auto/311620
Summary of Completed Tests
test_matter_thermo_setpoint_limits.lua: the two "inside deadband" tests now expect both writes; added a boundary case (moved setpoint exactly at its max) and rejection cases when the other setpoint cannot be moved, in both directions.test_matter_thermostat.lua: without AutoMode a heating setpoint above the cooling setpoint is written alone.python3 tools/run_driver_tests.py -f matter-thermostat: 131/131 pass (lua_libs API v21);luacheckclean on the changed files.