Skip to content

Commit

Permalink
Configurable minimum and maximum values (#34)
Browse files Browse the repository at this point in the history
* Configurable minimum and maximum values
- Climate: for target_humidity and target_temperature
- Humidifier: for target_humidity
- Sensor: added configurable minimum value for set_value service
  • Loading branch information
oyvindwe authored Jul 9, 2024
1 parent b8d1d47 commit 0795afa
Show file tree
Hide file tree
Showing 8 changed files with 126 additions and 24 deletions.
45 changes: 40 additions & 5 deletions custom_components/connectlife/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ class ConnectLifeClimate(ConnectLifeEntity, ClimateEntity):
swing_mode_map: dict[int, str]
swing_mode_reverse_map: dict[str, int]
temperature_unit_map: dict[int, UnitOfTemperature]
min_temperature_map: dict[UnitOfTemperature: int]
max_temperature_map: dict[UnitOfTemperature: int]

def __init__(
self,
Expand Down Expand Up @@ -100,6 +102,8 @@ def __init__(
self.swing_mode_map = {}
self.swing_mode_reverse_map = {}
self.temperature_unit_map = {}
self.min_temperature_map = {}
self.max_temperature_map = {}
self.unknown_values = {}

for dd_entry in data_dictionary.values():
Expand All @@ -115,15 +119,17 @@ def __init__(
elif target == TARGET_HUMIDITY:
self._attr_supported_features |= ClimateEntityFeature.TARGET_HUMIDITY
self._attr_target_humidity = None
self._attr_min_humidity = data_dictionary[status].climate.min_value
self._attr_max_humidity = data_dictionary[status].climate.max_value
elif target == TARGET_TEMPERATURE:
self._attr_supported_features |= ClimateEntityFeature.TARGET_TEMPERATURE
self._attr_target_temperature = None
self.min_temperature_map = to_temperature_map(data_dictionary[status].climate.min_value)
self.max_temperature_map = to_temperature_map(data_dictionary[status].climate.max_value)
elif target == TEMPERATURE_UNIT:
for k, v in data_dictionary[status].climate.options.items():
if v == "celsius" or v == "C":
self.temperature_unit_map[k] = UnitOfTemperature.CELSIUS
elif v == "fahrenheit" or v == "F":
self.temperature_unit_map[k] = UnitOfTemperature.FAHRENHEIT
if unit := to_unit_of_temperature(v):
self.temperature_unit_map[k] = unit
elif target == HVAC_MODE:
modes = [mode.value for mode in HVACMode]
for (k, v) in data_dictionary[status].climate.options.items():
Expand Down Expand Up @@ -159,8 +165,14 @@ def __init__(
if IS_ON not in self.target_map:
self._attr_hvac_mode = HVACMode.AUTO
self.mapped_hvac_mode = HVACMode.AUTO

self._attr_hvac_modes = hvac_modes

if TEMPERATURE_UNIT not in self.target_map:
if min_temp := self.get_temperature_limit(self.min_temperature_map):
self._attr_min_temp = min_temp
if max_temp := self.get_temperature_limit(self.max_temperature_map):
self._attr_max_temp = max_temp

self.update_state()

@callback
Expand Down Expand Up @@ -201,6 +213,10 @@ def update_state(self) -> None:
elif target == TEMPERATURE_UNIT:
if value in self.temperature_unit_map:
self._attr_temperature_unit = self.temperature_unit_map[value]
if min_temp := self.get_temperature_limit(self.min_temperature_map):
self._attr_min_temp = min_temp
if max_temp := self.get_temperature_limit(self.max_temperature_map):
self._attr_max_temp = max_temp
else:
_LOGGER.warning("Got unexpected value %d for %s (%s)", value, status, self.nickname)
else:
Expand All @@ -211,6 +227,12 @@ def update_state(self) -> None:
self._attr_hvac_mode = self.mapped_hvac_mode if is_on else HVACMode.OFF
self._attr_available = self.coordinator.appliances[self.device_id].offline_state == 1

def get_temperature_limit(self, temperature_map: [UnitOfTemperature, int]):
if temperature_map and self._attr_temperature_unit in temperature_map:
return temperature_map[self._attr_temperature_unit]
else:
return None

async def async_set_humidity(self, humidity):
"""Set new target humidity."""
humidity = round(humidity)
Expand Down Expand Up @@ -277,3 +299,16 @@ async def async_set_swing_mode(self, swing_mode: str) -> None:
})
self._attr_swing_mode = swing_mode
self.async_write_ha_state()


def to_unit_of_temperature(temp: str) -> UnitOfTemperature | None:
if temp == "C" or temp == "celsius":
return UnitOfTemperature.CELSIUS
elif temp == "F" or temp == "fahrenheit":
return UnitOfTemperature.FAHRENHEIT
else:
return None


def to_temperature_map(items: dict[str, int] | None) -> dict[UnitOfTemperature, int] | None:
return {to_unit_of_temperature(k): v for k, v in items.items()} if items else None
6 changes: 6 additions & 0 deletions custom_components/connectlife/data_dictionaries/009-109.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ properties:
- property: t_temp
climate:
target: target_temperature
min_value:
celsius: 16
fahrenheit: 61
max_value:
celsius: 32
fahrenheit: 90
- property: t_temp_type
climate:
target: temperature_unit
Expand Down
41 changes: 25 additions & 16 deletions custom_components/connectlife/data_dictionaries/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ The file contains two top level items:

Each property is mapped to _one_ entity or (in the cases of `climate` and `humidifier`) _one_ target property.

If you change the type of a mapping, the old entity or state attribute will change to unavailable in Home Assistant.
If you change the type of mapping, the old entity or state attribute will change to unavailable in Home Assistant.

You need to restart Home Assistant to load mapping changes.

Expand Down Expand Up @@ -51,11 +51,13 @@ Domain `binary_sensor` can be used for read only properties where `0` is not ava
Domain `climate` can be used to map the property to a target property in a climate entity. If at least one property has
type `climate`, a climate entity is created for the appliance.

| Item | Type | Description |
|-----------------|---------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `target` | string | Any of these [climate entity](https://developers.home-assistant.io/docs/core/entity/climate#properties) attributes: `current_humidity`, `fan_mode`, `hvac_action`, `hvac_mode`, `swing_mode`, `current_temperature`, `target_humidity`, `target_temperature`, `temperature_unit`, or the special target `is_on`. |
| `options` | dictionary of integer to string | Required for `fan_mode`, `hvac_action`, `hvac_mode`, `swing_mode`, and `temperature_unit`. |
| `unknown_value` | integer | The value used by the API to signal unknown value. |
| Item | Type | Description |
|-----------------|----------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `target` | string | Any of these [climate entity](https://developers.home-assistant.io/docs/core/entity/climate#properties) attributes: `current_humidity`, `fan_mode`, `hvac_action`, `hvac_mode`, `swing_mode`, `current_temperature`, `target_humidity`, `target_temperature`, `temperature_unit`, or the special target `is_on`. |
| `options` | dictionary of integer to string | Required for `fan_mode`, `hvac_action`, `hvac_mode`, `swing_mode`, and `temperature_unit`. |
| `unknown_value` | integer | The value used by the API to signal unknown value. |
| `min_value` | [IntegerOrTemperature](#type-integerortemperature) | Minimum allowed value. Supported for `target_humidity` (integer) and `target_temperature` (temperature). |
| `max_value` | [IntegerOrTemperature](#type-integerortemperature) | Maximum allowed value. Supported for `target_humidity` (integer) and `target_temperature` (temperature). |

`temperature_unit` defaults to Celsius.

Expand All @@ -68,10 +70,6 @@ mapping to a sensor `enum` instead.
For `fan_mode` and `swing_mode`, remember to add options [translation strings](#translation-strings).

Not yet supported target properties:
- `max_humidity`
- `max_temp`
- `min_humidity`
- `min_temp`
- `preset_mode`

## Type `Humidifier`:
Expand All @@ -82,7 +80,7 @@ type `humidifier`, a humidifier entity is created for the appliance.
| Item | Type | Description |
|----------------|---------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `target` | string | Any of these [humidifier entity](https://developers.home-assistant.io/docs/core/entity/humidifier#properties) attributes: `action`, `is_on`, `current_humidity`, `target_humidity`, `mode`. |
| `options` | dictionary of integer to string | Required for `action` and `mode`. |
| `options` | dictionary of integer to string | Required for `action` and `mode`. |
| `device_class` | string | Name of any [HumidifierDeviceClass enum](https://developers.home-assistant.io/docs/core/entity/humidifier#available-device-classes). |

It is sufficient to set `device_class` on one property. The value of the first encountered property is used.
Expand All @@ -93,10 +91,6 @@ to a sensor `enum` instead.

For `mode`, remember to add options to [translation strings](#translation-strings).

Not yet supported target properties:
- `max_humidity`
- `min_humidity`

## Type `Select`

| Item | Type | Description |
Expand All @@ -110,8 +104,9 @@ Remember to add options to [translation strings](#translation-strings).
| Item | Type | Description |
|-----------------|--------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `unknown_value` | integer | The value used by the API to signal unknown value. |
| `min_value` | integer | Minimum value (checked when setting property). |
| `max_value` | integer | Maximum value (checked when setting property). |
| `writable` | `true`, `false` | If this property is writable (do not set if unknown). Only applies to `sensor`. |
| `writable` | `true`, `false` | If this property is writable (do not set if unknown). |
| `state_class` | `measurement`, `total`, `total_increasing` | Name of any [SensorStateClass enum](https://developers.home-assistant.io/docs/core/entity/sensor/#available-state-classes). For integer properties, defaults to `measurement`. Not allowed when `device_class` is `enum`. |
| `device_class` | `duration`, `energy`, `water`, etc. | Name of any [SensorDeviceClass enum](https://developers.home-assistant.io/docs/core/entity/sensor/#available-device-classes). |
| `unit` | `min`, `kWh`, `L`, etc. | Required if `device_class` is set, except not allowed when `device_class` is `ph` or `enum`. |
Expand All @@ -126,6 +121,20 @@ For device class `enum`, remember to add options to [translation strings](#trans
| `off` | integer | Off value. Defaults to 0. |
| `on` | integer | On value. Defaults to 1. |

## Type `IntegerOrTemperature`

Either just a numeric value, or values in Celsius and/or Fahrenheit.

```yaml
min_value: 10
```
or
```yaml
min_value:
celsius: 0
fahrenheit: 32
```
# Translation strings
By default, sensor entities are named by replacing `_` with ` ` in the property name. However, the property name is also
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@
"unknown_value": {
"$ref": "#/definitions/UnknownValue"
},
"max_value": {
"$ref": "#/definitions/IntegerOrTemperature"
},
"min_value": {
"$ref": "#/definitions/IntegerOrTemperature"
},
"options": {
"type": "object",
"additionalProperties": {
Expand All @@ -116,6 +122,12 @@
"target": {
"$ref": "#/definitions/HumidifierTarget"
},
"min_value": {
"type": "integer"
},
"max_value": {
"type": "integer"
},
"device_class": {
"$ref": "#/definitions/HumidifierDeviceClass"
},
Expand Down Expand Up @@ -165,6 +177,14 @@
"unknown_value": {
"$ref": "#/definitions/UnknownValue"
},
"min_value": {
"type": "integer",
"description": "Minimum value (checked when setting property).",
"examples": [
0,
1
]
},
"max_value": {
"type": "integer",
"description": "Maximum value (checked when setting property).",
Expand Down Expand Up @@ -360,6 +380,21 @@
255,
65535
]
},
"IntegerOrTemperature": {
"type": [
"integer",
"object"
],
"additionalProperties": false,
"properties": {
"celsius": {
"type": "integer"
},
"fahrenheit": {
"type": "integer"
}
}
}
}
}
Loading

0 comments on commit 0795afa

Please sign in to comment.