1 """Matter climate platform."""
3 from __future__
import annotations
5 from enum
import IntEnum
8 from chip.clusters
import Objects
as clusters
9 from matter_server.client.models
import device_types
10 from matter_server.common.helpers.util
import create_attribute_path_from_attribute
14 ATTR_TARGET_TEMP_HIGH,
19 ClimateEntityDescription,
29 from .entity
import MatterEntity
30 from .helpers
import get_matter
31 from .models
import MatterDiscoverySchema
33 TEMPERATURE_SCALING_FACTOR = 100
34 HVAC_SYSTEM_MODE_MAP = {
36 HVACMode.HEAT_COOL: 1,
43 SINGLE_SETPOINT_DEVICES: set[tuple[int, int]] = {
81 SUPPORT_DRY_MODE_DEVICES: set[tuple[int, int]] = {
119 SUPPORT_FAN_MODE_DEVICES: set[tuple[int, int]] = {
157 SystemModeEnum = clusters.Thermostat.Enums.SystemModeEnum
158 ControlSequenceEnum = clusters.Thermostat.Enums.ControlSequenceOfOperationEnum
159 ThermostatFeature = clusters.Thermostat.Bitmaps.Feature
163 """Thermostat Running State, Matter spec Thermostat 7.33."""
176 config_entry: ConfigEntry,
177 async_add_entities: AddEntitiesCallback,
179 """Set up Matter climate platform from Config Entry."""
181 matter.register_platform_handler(Platform.CLIMATE, async_add_entities)
185 """Representation of a Matter climate entity."""
187 _attr_temperature_unit: str = UnitOfTemperature.CELSIUS
188 _attr_hvac_mode: HVACMode = HVACMode.OFF
189 _feature_map: int |
None =
None
190 _enable_turn_on_off_backwards_compatibility =
False
191 _platform_translation_key =
"thermostat"
194 """Set new target temperature."""
195 target_hvac_mode: HVACMode |
None = kwargs.get(ATTR_HVAC_MODE)
196 target_temperature: float |
None = kwargs.get(ATTR_TEMPERATURE)
197 target_temperature_low: float |
None = kwargs.get(ATTR_TARGET_TEMP_LOW)
198 target_temperature_high: float |
None = kwargs.get(ATTR_TARGET_TEMP_HIGH)
200 if target_hvac_mode
is not None:
204 if target_temperature
is not None:
207 if current_mode == HVACMode.COOL:
209 clusters.Thermostat.Attributes.OccupiedCoolingSetpoint
213 clusters.Thermostat.Attributes.OccupiedHeatingSetpoint
216 node_id=self.
_endpoint_endpoint.node.node_id,
217 attribute_path=create_attribute_path_from_attribute(
221 value=
int(target_temperature * TEMPERATURE_SCALING_FACTOR),
225 if target_temperature_low
is not None:
229 node_id=self.
_endpoint_endpoint.node.node_id,
230 attribute_path=create_attribute_path_from_attribute(
232 clusters.Thermostat.Attributes.OccupiedHeatingSetpoint,
234 value=
int(target_temperature_low * TEMPERATURE_SCALING_FACTOR),
237 if target_temperature_high
is not None:
241 node_id=self.
_endpoint_endpoint.node.node_id,
242 attribute_path=create_attribute_path_from_attribute(
244 clusters.Thermostat.Attributes.OccupiedCoolingSetpoint,
246 value=
int(target_temperature_high * TEMPERATURE_SCALING_FACTOR),
250 """Set new target hvac mode."""
251 system_mode_path = create_attribute_path_from_attribute(
252 endpoint_id=self.
_endpoint_endpoint.endpoint_id,
253 attribute=clusters.Thermostat.Attributes.SystemMode,
255 system_mode_value = HVAC_SYSTEM_MODE_MAP.get(hvac_mode)
256 if system_mode_value
is None:
257 raise ValueError(f
"Unsupported hvac mode {hvac_mode} in Matter")
259 node_id=self.
_endpoint_endpoint.node.node_id,
260 attribute_path=system_mode_path,
261 value=system_mode_value,
266 self.
_endpoint_endpoint.set_attribute_value(system_mode_path, system_mode_value)
271 """Update from device."""
274 clusters.Thermostat.Attributes.LocalTemperature
283 system_mode_value =
int(
285 clusters.Thermostat.Attributes.SystemMode
288 match system_mode_value:
289 case SystemModeEnum.kAuto:
291 case SystemModeEnum.kDry:
293 case SystemModeEnum.kFanOnly:
295 case SystemModeEnum.kCool | SystemModeEnum.kPrecooling:
297 case SystemModeEnum.kHeat | SystemModeEnum.kEmergencyHeat:
299 case SystemModeEnum.kFanOnly:
301 case SystemModeEnum.kDry:
309 clusters.Thermostat.Attributes.ThermostatRunningState
311 match running_state_value:
313 ThermostatRunningState.Heat
314 | ThermostatRunningState.HeatStage2
318 ThermostatRunningState.Cool
319 | ThermostatRunningState.CoolStage2
323 ThermostatRunningState.Fan
324 | ThermostatRunningState.FanStage2
325 | ThermostatRunningState.FanStage3
334 & ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
336 if supports_range
and self.
_attr_hvac_mode_attr_hvac_mode == HVACMode.HEAT_COOL:
339 clusters.Thermostat.Attributes.OccupiedCoolingSetpoint
342 clusters.Thermostat.Attributes.OccupiedHeatingSetpoint
350 clusters.Thermostat.Attributes.OccupiedCoolingSetpoint
354 clusters.Thermostat.Attributes.OccupiedHeatingSetpoint
359 attribute = clusters.Thermostat.Attributes.AbsMinCoolSetpointLimit
361 attribute = clusters.Thermostat.Attributes.AbsMinHeatSetpointLimit
367 if self.
_attr_hvac_mode_attr_hvac_mode
in (HVACMode.COOL, HVACMode.HEAT_COOL):
368 attribute = clusters.Thermostat.Attributes.AbsMaxCoolSetpointLimit
370 attribute = clusters.Thermostat.Attributes.AbsMaxHeatSetpointLimit
380 """Calculate features for HA Thermostat platform from Matter FeatureMap."""
390 product_id = self.
_endpoint_endpoint.node.device_info.productID
391 vendor_id = self.
_endpoint_endpoint.node.device_info.vendorID
392 self._attr_hvac_modes: list[HVACMode] = [HVACMode.OFF]
394 ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.TURN_OFF
396 if feature_map & ThermostatFeature.kHeating:
397 self._attr_hvac_modes.append(HVACMode.HEAT)
398 if feature_map & ThermostatFeature.kCooling:
399 self._attr_hvac_modes.append(HVACMode.COOL)
400 if (vendor_id, product_id)
in SUPPORT_DRY_MODE_DEVICES:
401 self._attr_hvac_modes.append(HVACMode.DRY)
402 if (vendor_id, product_id)
in SUPPORT_FAN_MODE_DEVICES:
403 self._attr_hvac_modes.append(HVACMode.FAN_ONLY)
404 if feature_map & ThermostatFeature.kAutoMode:
405 self._attr_hvac_modes.append(HVACMode.HEAT_COOL)
408 if (vendor_id, product_id)
not in SINGLE_SETPOINT_DEVICES:
410 ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
412 if any(mode
for mode
in self.
hvac_modeshvac_modes
if mode != HVACMode.OFF):
417 self, attribute: type[clusters.ClusterAttributeDescriptor]
419 """Return the scaled temperature value for the given attribute."""
421 return float(value) / TEMPERATURE_SCALING_FACTOR
426 DISCOVERY_SCHEMAS = [
428 platform=Platform.CLIMATE,
430 key=
"MatterThermostat",
433 entity_class=MatterClimate,
434 required_attributes=(clusters.Thermostat.Attributes.LocalTemperature,),
435 optional_attributes=(
436 clusters.Thermostat.Attributes.FeatureMap,
437 clusters.Thermostat.Attributes.ControlSequenceOfOperation,
438 clusters.Thermostat.Attributes.Occupancy,
439 clusters.Thermostat.Attributes.OccupiedCoolingSetpoint,
440 clusters.Thermostat.Attributes.OccupiedHeatingSetpoint,
441 clusters.Thermostat.Attributes.SystemMode,
442 clusters.Thermostat.Attributes.ThermostatRunningMode,
443 clusters.Thermostat.Attributes.ThermostatRunningState,
444 clusters.Thermostat.Attributes.TemperatureSetpointHold,
445 clusters.Thermostat.Attributes.UnoccupiedCoolingSetpoint,
446 clusters.Thermostat.Attributes.UnoccupiedHeatingSetpoint,
447 clusters.OnOff.Attributes.OnOff,
449 device_type=(device_types.Thermostat, device_types.RoomAirConditioner),
float|None target_temperature_low(self)
float|None target_temperature_high(self)
None async_set_hvac_mode(self, HVACMode hvac_mode)
float|None target_temperature(self)
HVACMode|None hvac_mode(self)
list[HVACMode] hvac_modes(self)
None _update_from_device(self)
float|None _get_temperature_in_degrees(self, type[clusters.ClusterAttributeDescriptor] attribute)
_attr_target_temperature_low
_attr_target_temperature_high
None _calculate_features(self)
_attr_current_temperature
None async_set_hvac_mode(self, HVACMode hvac_mode)
None async_set_temperature(self, **Any kwargs)
Any get_matter_attribute_value(self, type[ClusterAttributeDescriptor] attribute, bool null_as_none=True)
None _update_from_device(self)
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
MatterAdapter get_matter(HomeAssistant hass)