Repository navigation
Legacy Matter Thermostat profile generator can select 11 profile names that do not exist #3271
Description
Summary
The legacy thermostat profiling path generates a profile name from humidity support, fan support, heating/cooling support, ThermostatRunningState support, and battery reporting type. Exhaustively enumerating those dimensions produces 72 valid generic thermostat profile names, but only 61 of them exist in the driver package. Eleven reachable names are missing.
This affects the legacy path (api < 14 or rpc < 8); the newer modular profile path avoids this particular problem.
Validated against main commit 43f47501b217336d17ed2fc92b64d08971f305ce and the current profiles/ directory.
Affected code
legacy_device_configuration.lua lines 214-251:
elseif #thermostat_eps > 0 then
profile_name = "thermostat"
if #humidity_eps > 0 then
profile_name = profile_name .. "-humidity"
end
local fan_name = LegacyConfigurationHelpers.create_fan_profile(device)
if fan_name ~= "" then
profile_name = profile_name .. "-fan"
end
local thermostat_modes = LegacyConfigurationHelpers.create_thermostat_modes_profile(device)
...
profile_name = profile_name .. thermostat_modes
if not running_state_supported then
profile_name = profile_name .. "-nostate"
end
if battery_supported == fields.battery_support.BATTERY_LEVEL then
profile_name = profile_name .. "-batteryLevel"
elseif battery_supported == fields.battery_support.NO_BATTERY then
profile_name = profile_name .. "-nobattery"
end
end
...
device:try_update_metadata({profile = profile_name})The legacy/modular branch is selected in device_configuration.lua lines 319-328:
if version.api >= 14 and version.rpc >= 8 and match_modular_device_type[primary_device_type] then
match_modular_device_type[primary_device_type](device)
return
else
local legacy_device_cfg = require "thermostat_utils.legacy_device_configuration"
legacy_device_cfg.match_profile(device)
endReproduction case
One concrete reachable combination is:
humidity: yes
fan: yes
Thermostat heating: no
Thermostat cooling: yes
ThermostatRunningState yes
battery reporting: BatChargeLevel
The generator constructs:
thermostat-humidity-fan-cooling-only-batteryLevel
but that profile is absent.
An exhaustive enumeration found these 11 missing names:
thermostat-fan-cooling-only-nobattery
thermostat-fan-heating-only-nobattery
thermostat-fan-nobattery
thermostat-humidity-cooling-only-nobattery
thermostat-humidity-fan-cooling-only-batteryLevel
thermostat-humidity-fan-cooling-only-nobattery
thermostat-humidity-fan-cooling-only-nostate-batteryLevel
thermostat-humidity-fan-heating-only-nobattery
thermostat-humidity-fan-nobattery
thermostat-humidity-heating-only-nobattery
thermostat-humidity-nobattery
Expected behavior
Every profile name reachable from the legacy generator should resolve to a packaged profile, or the generator should explicitly collapse unsupported combinations to an existing compatible profile.
Proposed fix
The least invasive compatibility fix is to add the 11 profile files the legacy generator can currently construct but which are absent from profiles/:
thermostat-fan-cooling-only-nobattery.yml
thermostat-fan-heating-only-nobattery.yml
thermostat-fan-nobattery.yml
thermostat-humidity-cooling-only-nobattery.yml
thermostat-humidity-fan-cooling-only-batteryLevel.yml
thermostat-humidity-fan-cooling-only-nobattery.yml
thermostat-humidity-fan-cooling-only-nostate-batteryLevel.yml
thermostat-humidity-fan-heating-only-nobattery.yml
thermostat-humidity-fan-nobattery.yml
thermostat-humidity-heating-only-nobattery.yml
thermostat-humidity-nobattery.yml
For example, thermostat-fan-heating-only-nobattery.yml should be the same capability composition implied by its name:
name: thermostat-fan-heating-only-nobattery
components:
- id: main
capabilities:
- id: temperatureMeasurement
version: 1
- id: thermostatMode
version: 1
- id: thermostatFanMode
version: 1
- id: thermostatHeatingSetpoint
version: 1
- id: thermostatOperatingState
version: 1
config:
values:
- key: thermostatOperatingState.value
enabledValues:
- idle
- heating
- id: firmwareUpdate
version: 1
- id: refresh
version: 1
categories:
- name: ThermostatThe other ten files can be generated from the nearest existing profile by applying the dimensions already encoded in the filename (humidity, fan, heating/cooling mode, nostate, batteryLevel/nobattery).
A regression test should also enumerate every state reachable by LegacyConfigurationHelpers.match_profile() and assert that the resulting profile name exists. Pseudocode for the core assertion:
for _, profile_name in ipairs(all_reachable_legacy_thermostat_profile_names()) do
assert(
profile_exists(profile_name),
string.format("legacy thermostat profile does not exist: %s", profile_name)
)
endThat test is important because merely adding the current 11 files fixes today's gap but does not prevent another missing combination when a new profile dimension is introduced later.
Suggested regression test
Enumerate:
humidity 2 states
fan 2 states
heat/cool 3 states (both, heat-only, cool-only)
running state 2 states
battery 3 states (percentage/default, batteryLevel, no battery)
This gives 2 * 2 * 3 * 2 * 3 = 72 names. Assert all 72 are present in profiles/ for the legacy path.
Activity