Allow Humidifier.current_humidity to be a float (#111297)

* Allow Humidifier.current_humidity to be a float

* Code review

* Allow climate humidity values to be float

* Update demo integration
This commit is contained in:
Marc Mueller 2024-03-27 11:19:20 +01:00 committed by GitHub
parent b190cdceaf
commit 44eeb2eb5e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 66 additions and 66 deletions

View File

@ -292,9 +292,9 @@ class ClimateEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
_attr_hvac_mode: HVACMode | None
_attr_hvac_modes: list[HVACMode]
_attr_is_aux_heat: bool | None
_attr_max_humidity: int = DEFAULT_MAX_HUMIDITY
_attr_max_humidity: float = DEFAULT_MAX_HUMIDITY
_attr_max_temp: float
_attr_min_humidity: int = DEFAULT_MIN_HUMIDITY
_attr_min_humidity: float = DEFAULT_MIN_HUMIDITY
_attr_min_temp: float
_attr_precision: float
_attr_preset_mode: str | None
@ -302,7 +302,7 @@ class ClimateEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
_attr_supported_features: ClimateEntityFeature = ClimateEntityFeature(0)
_attr_swing_mode: str | None
_attr_swing_modes: list[str] | None
_attr_target_humidity: int | None = None
_attr_target_humidity: float | None = None
_attr_target_temperature_high: float | None
_attr_target_temperature_low: float | None
_attr_target_temperature_step: float | None = None
@ -517,12 +517,12 @@ class ClimateEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
return self._attr_temperature_unit
@cached_property
def current_humidity(self) -> int | None:
def current_humidity(self) -> float | None:
"""Return the current humidity."""
return self._attr_current_humidity
@cached_property
def target_humidity(self) -> int | None:
def target_humidity(self) -> float | None:
"""Return the humidity we try to reach."""
return self._attr_target_humidity
@ -826,12 +826,12 @@ class ClimateEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
return self._attr_max_temp
@cached_property
def min_humidity(self) -> int:
def min_humidity(self) -> float:
"""Return the minimum humidity."""
return self._attr_min_humidity
@cached_property
def max_humidity(self) -> int:
def max_humidity(self) -> float:
"""Return the maximum humidity."""
return self._attr_max_humidity

View File

@ -148,12 +148,12 @@ class ComelitHumidifierEntity(CoordinatorEntity[ComelitSerialBridge], Humidifier
return self._humidifier[3] == HumidifierComelitMode.AUTO
@property
def target_humidity(self) -> int:
def target_humidity(self) -> float:
"""Set target humidity."""
return self._humidifier[4] / 10
@property
def current_humidity(self) -> int:
def current_humidity(self) -> float:
"""Return current humidity."""
return self._humidifier[0] / 10

View File

@ -57,8 +57,8 @@ async def async_setup_entry(
preset=None,
current_temperature=22,
fan_mode="on_high",
target_humidity=67,
current_humidity=54,
target_humidity=67.4,
current_humidity=54.2,
swing_mode="off",
hvac_mode=HVACMode.COOL,
hvac_action=HVACAction.COOLING,
@ -106,8 +106,8 @@ class DemoClimate(ClimateEntity):
preset: str | None,
current_temperature: float,
fan_mode: str | None,
target_humidity: int | None,
current_humidity: int | None,
target_humidity: float | None,
current_humidity: float | None,
swing_mode: str | None,
hvac_mode: HVACMode,
hvac_action: HVACAction | None,
@ -188,12 +188,12 @@ class DemoClimate(ClimateEntity):
return self._target_temperature_low
@property
def current_humidity(self) -> int | None:
def current_humidity(self) -> float | None:
"""Return the current humidity."""
return self._current_humidity
@property
def target_humidity(self) -> int | None:
def target_humidity(self) -> float | None:
"""Return the humidity we try to reach."""
return self._target_humidity

View File

@ -36,8 +36,8 @@ async def async_setup_entry(
DemoHumidifier(
name="Dehumidifier",
mode=None,
target_humidity=54,
current_humidity=59,
target_humidity=54.2,
current_humidity=59.4,
action=HumidifierAction.DRYING,
device_class=HumidifierDeviceClass.DEHUMIDIFIER,
),
@ -60,8 +60,8 @@ class DemoHumidifier(HumidifierEntity):
self,
name: str,
mode: str | None,
target_humidity: int,
current_humidity: int | None = None,
target_humidity: float,
current_humidity: float | None = None,
available_modes: list[str] | None = None,
is_on: bool = True,
action: HumidifierAction | None = None,

View File

@ -36,13 +36,13 @@ HYGROSTAT_SCHEMA = vol.Schema(
vol.Optional(CONF_DEVICE_CLASS): vol.In(
[HumidifierDeviceClass.HUMIDIFIER, HumidifierDeviceClass.DEHUMIDIFIER]
),
vol.Optional(CONF_MAX_HUMIDITY): vol.Coerce(int),
vol.Optional(CONF_MAX_HUMIDITY): vol.Coerce(float),
vol.Optional(CONF_MIN_DUR): vol.All(cv.time_period, cv.positive_timedelta),
vol.Optional(CONF_MIN_HUMIDITY): vol.Coerce(int),
vol.Optional(CONF_MIN_HUMIDITY): vol.Coerce(float),
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_DRY_TOLERANCE, default=DEFAULT_TOLERANCE): vol.Coerce(float),
vol.Optional(CONF_WET_TOLERANCE, default=DEFAULT_TOLERANCE): vol.Coerce(float),
vol.Optional(CONF_TARGET_HUMIDITY): vol.Coerce(int),
vol.Optional(CONF_TARGET_HUMIDITY): vol.Coerce(float),
vol.Optional(CONF_KEEP_ALIVE): vol.All(cv.time_period, cv.positive_timedelta),
vol.Optional(CONF_INITIAL_STATE): cv.boolean,
vol.Optional(CONF_AWAY_HUMIDITY): vol.Coerce(int),

View File

@ -85,9 +85,9 @@ async def async_setup_platform(
name: str = config[CONF_NAME]
switch_entity_id: str = config[CONF_HUMIDIFIER]
sensor_entity_id: str = config[CONF_SENSOR]
min_humidity: int | None = config.get(CONF_MIN_HUMIDITY)
max_humidity: int | None = config.get(CONF_MAX_HUMIDITY)
target_humidity: int | None = config.get(CONF_TARGET_HUMIDITY)
min_humidity: float | None = config.get(CONF_MIN_HUMIDITY)
max_humidity: float | None = config.get(CONF_MAX_HUMIDITY)
target_humidity: float | None = config.get(CONF_TARGET_HUMIDITY)
device_class: HumidifierDeviceClass | None = config.get(CONF_DEVICE_CLASS)
min_cycle_duration: timedelta | None = config.get(CONF_MIN_DUR)
sensor_stale_duration: timedelta | None = config.get(CONF_STALE_DURATION)
@ -133,9 +133,9 @@ class GenericHygrostat(HumidifierEntity, RestoreEntity):
name: str,
switch_entity_id: str,
sensor_entity_id: str,
min_humidity: int | None,
max_humidity: int | None,
target_humidity: int | None,
min_humidity: float | None,
max_humidity: float | None,
target_humidity: float | None,
device_class: HumidifierDeviceClass | None,
min_cycle_duration: timedelta | None,
dry_tolerance: float,
@ -264,12 +264,12 @@ class GenericHygrostat(HumidifierEntity, RestoreEntity):
return self._state
@property
def current_humidity(self) -> int | None:
def current_humidity(self) -> float | None:
"""Return the measured humidity."""
return int(self._cur_humidity) if self._cur_humidity is not None else None
return self._cur_humidity
@property
def target_humidity(self) -> int | None:
def target_humidity(self) -> float | None:
"""Return the humidity we try to reach."""
return self._target_humidity
@ -326,7 +326,7 @@ class GenericHygrostat(HumidifierEntity, RestoreEntity):
self.async_write_ha_state()
@property
def min_humidity(self) -> int:
def min_humidity(self) -> float:
"""Return the minimum humidity."""
if self._min_humidity:
return self._min_humidity
@ -335,7 +335,7 @@ class GenericHygrostat(HumidifierEntity, RestoreEntity):
return super().min_humidity
@property
def max_humidity(self) -> int:
def max_humidity(self) -> float:
"""Return the maximum humidity."""
if self._max_humidity:
return self._max_humidity

View File

@ -637,7 +637,7 @@ class HomeKitClimateEntity(HomeKitBaseClimateEntity):
return self.service.value(CharacteristicsTypes.RELATIVE_HUMIDITY_TARGET)
@property
def min_humidity(self) -> int:
def min_humidity(self) -> float:
"""Return the minimum humidity."""
min_humidity = self.service[
CharacteristicsTypes.RELATIVE_HUMIDITY_TARGET
@ -647,7 +647,7 @@ class HomeKitClimateEntity(HomeKitBaseClimateEntity):
return super().min_humidity
@property
def max_humidity(self) -> int:
def max_humidity(self) -> float:
"""Return the maximum humidity."""
max_humidity = self.service[
CharacteristicsTypes.RELATIVE_HUMIDITY_TARGET

View File

@ -165,18 +165,18 @@ class HumidifierEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_AT
entity_description: HumidifierEntityDescription
_attr_action: HumidifierAction | None = None
_attr_available_modes: list[str] | None
_attr_current_humidity: int | None = None
_attr_current_humidity: float | None = None
_attr_device_class: HumidifierDeviceClass | None
_attr_max_humidity: int = DEFAULT_MAX_HUMIDITY
_attr_min_humidity: int = DEFAULT_MIN_HUMIDITY
_attr_max_humidity: float = DEFAULT_MAX_HUMIDITY
_attr_min_humidity: float = DEFAULT_MIN_HUMIDITY
_attr_mode: str | None
_attr_supported_features: HumidifierEntityFeature = HumidifierEntityFeature(0)
_attr_target_humidity: int | None = None
_attr_target_humidity: float | None = None
@property
def capability_attributes(self) -> dict[str, Any]:
"""Return capability attributes."""
data: dict[str, int | list[str] | None] = {
data: dict[str, Any] = {
ATTR_MIN_HUMIDITY: self.min_humidity,
ATTR_MAX_HUMIDITY: self.max_humidity,
}
@ -199,7 +199,7 @@ class HumidifierEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_AT
@property
def state_attributes(self) -> dict[str, Any]:
"""Return the optional state attributes."""
data: dict[str, int | str | None] = {}
data: dict[str, Any] = {}
if self.action is not None:
data[ATTR_ACTION] = self.action if self.is_on else HumidifierAction.OFF
@ -221,12 +221,12 @@ class HumidifierEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_AT
return self._attr_action
@cached_property
def current_humidity(self) -> int | None:
def current_humidity(self) -> float | None:
"""Return the current humidity."""
return self._attr_current_humidity
@cached_property
def target_humidity(self) -> int | None:
def target_humidity(self) -> float | None:
"""Return the humidity we try to reach."""
return self._attr_target_humidity
@ -263,12 +263,12 @@ class HumidifierEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_AT
await self.hass.async_add_executor_job(self.set_mode, mode)
@cached_property
def min_humidity(self) -> int:
def min_humidity(self) -> float:
"""Return the minimum humidity."""
return self._attr_min_humidity
@cached_property
def max_humidity(self) -> int:
def max_humidity(self) -> float:
"""Return the maximum humidity."""
return self._attr_max_humidity

View File

@ -1025,11 +1025,11 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = {
),
TypeHintMatch(
function_name="current_humidity",
return_type=["int", None],
return_type=["float", None],
),
TypeHintMatch(
function_name="target_humidity",
return_type=["int", None],
return_type=["float", None],
),
TypeHintMatch(
function_name="hvac_mode",
@ -1171,11 +1171,11 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = {
),
TypeHintMatch(
function_name="min_humidity",
return_type="int",
return_type="float",
),
TypeHintMatch(
function_name="max_humidity",
return_type="int",
return_type="float",
),
],
),
@ -1549,11 +1549,11 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = {
),
TypeHintMatch(
function_name="min_humidity",
return_type=["int"],
return_type=["float"],
),
TypeHintMatch(
function_name="max_humidity",
return_type=["int"],
return_type=["float"],
),
TypeHintMatch(
function_name="mode",
@ -1565,7 +1565,7 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = {
),
TypeHintMatch(
function_name="target_humidity",
return_type=["int", None],
return_type=["float", None],
),
TypeHintMatch(
function_name="set_humidity",

View File

@ -74,8 +74,8 @@ def test_setup_params(hass: HomeAssistant) -> None:
assert state.attributes.get(ATTR_TEMPERATURE) == 21
assert state.attributes.get(ATTR_CURRENT_TEMPERATURE) == 22
assert state.attributes.get(ATTR_FAN_MODE) == "on_high"
assert state.attributes.get(ATTR_HUMIDITY) == 67
assert state.attributes.get(ATTR_CURRENT_HUMIDITY) == 54
assert state.attributes.get(ATTR_HUMIDITY) == 67.4
assert state.attributes.get(ATTR_CURRENT_HUMIDITY) == 54.2
assert state.attributes.get(ATTR_SWING_MODE) == "off"
assert state.attributes.get(ATTR_HVAC_MODES) == [
HVACMode.OFF,
@ -219,7 +219,7 @@ async def test_set_temp_with_hvac_mode(hass: HomeAssistant) -> None:
async def test_set_target_humidity_bad_attr(hass: HomeAssistant) -> None:
"""Test setting the target humidity without required attribute."""
state = hass.states.get(ENTITY_CLIMATE)
assert state.attributes.get(ATTR_HUMIDITY) == 67
assert state.attributes.get(ATTR_HUMIDITY) == 67.4
with pytest.raises(vol.Invalid):
await hass.services.async_call(
@ -230,13 +230,13 @@ async def test_set_target_humidity_bad_attr(hass: HomeAssistant) -> None:
)
state = hass.states.get(ENTITY_CLIMATE)
assert state.attributes.get(ATTR_HUMIDITY) == 67
assert state.attributes.get(ATTR_HUMIDITY) == 67.4
async def test_set_target_humidity(hass: HomeAssistant) -> None:
"""Test the setting of the target humidity."""
state = hass.states.get(ENTITY_CLIMATE)
assert state.attributes.get(ATTR_HUMIDITY) == 67
assert state.attributes.get(ATTR_HUMIDITY) == 67.4
await hass.services.async_call(
DOMAIN,

View File

@ -57,8 +57,8 @@ def test_setup_params(hass: HomeAssistant) -> None:
"""Test the initial parameters."""
state = hass.states.get(ENTITY_DEHUMIDIFIER)
assert state.state == STATE_ON
assert state.attributes.get(ATTR_HUMIDITY) == 54
assert state.attributes.get(ATTR_CURRENT_HUMIDITY) == 59
assert state.attributes.get(ATTR_HUMIDITY) == 54.2
assert state.attributes.get(ATTR_CURRENT_HUMIDITY) == 59.4
assert state.attributes.get(ATTR_ACTION) == "drying"
@ -72,7 +72,7 @@ def test_default_setup_params(hass: HomeAssistant) -> None:
async def test_set_target_humidity_bad_attr(hass: HomeAssistant) -> None:
"""Test setting the target humidity without required attribute."""
state = hass.states.get(ENTITY_DEHUMIDIFIER)
assert state.attributes.get(ATTR_HUMIDITY) == 54
assert state.attributes.get(ATTR_HUMIDITY) == 54.2
with pytest.raises(vol.Invalid):
await hass.services.async_call(
@ -84,13 +84,13 @@ async def test_set_target_humidity_bad_attr(hass: HomeAssistant) -> None:
await hass.async_block_till_done()
state = hass.states.get(ENTITY_DEHUMIDIFIER)
assert state.attributes.get(ATTR_HUMIDITY) == 54
assert state.attributes.get(ATTR_HUMIDITY) == 54.2
async def test_set_target_humidity(hass: HomeAssistant) -> None:
"""Test the setting of the target humidity."""
state = hass.states.get(ENTITY_DEHUMIDIFIER)
assert state.attributes.get(ATTR_HUMIDITY) == 54
assert state.attributes.get(ATTR_HUMIDITY) == 54.2
await hass.services.async_call(
DOMAIN,

View File

@ -254,7 +254,7 @@ async def test_query_climate_request(
"thermostatTemperatureSetpoint": 21,
"thermostatTemperatureAmbient": 22,
"thermostatMode": "cool",
"thermostatHumidityAmbient": 54,
"thermostatHumidityAmbient": 54.2,
"currentFanSpeedSetting": "on_high",
}
@ -318,7 +318,7 @@ async def test_query_climate_request_f(
"thermostatTemperatureSetpoint": -6.1,
"thermostatTemperatureAmbient": -5.6,
"thermostatMode": "cool",
"thermostatHumidityAmbient": 54,
"thermostatHumidityAmbient": 54.2,
"currentFanSpeedSetting": "on_high",
}
hass_fixture.config.units.temperature_unit = UnitOfTemperature.CELSIUS
@ -363,8 +363,8 @@ async def test_query_humidifier_request(
assert devices["humidifier.dehumidifier"] == {
"on": True,
"online": True,
"humiditySetpointPercent": 54,
"humidityAmbientPercent": 59,
"humiditySetpointPercent": 54.2,
"humidityAmbientPercent": 59.4,
}
assert devices["humidifier.hygrostat"] == {
"on": True,