Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disable long-term statistics for unknown properties of known devices #48

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 51 additions & 27 deletions custom_components/connectlife/data_dictionaries/README.md
Original file line number Diff line number Diff line change
@@ -1,37 +1,61 @@
# Data dictionaries

Data dictionaries for known appliances are located in this directory. Appliances without data dictionary will be still
be loaded, but with a warning in the log. Also, all unknown properties are mapped to hidden status entities.
be loaded, but with a warning in the log. Their properties will all be mapped to [sensor](#type-sensor) entities,
with `hidden` set to `true` and `state_class` set to `measurement` (to enable
[long-term statistics](https://developers.home-assistant.io/docs/core/entity/sensor/#long-term-statistics)).

To make a property visible by default, just add the property to the list (without setting `hide`).
## Create your own mapping file

File name: `<deviceTypeCode>-<deviceFeatureCode>.yaml`
To map you device, create a file with the name `<deviceTypeCode>-<deviceFeatureCode>.yaml` in this directory. When done,
or if you need help with the mapping, please open a PR on GitHub with the file!

The file contains two top level items:
- `device_type`: string
- `properties`: list of [`Property`](#property)

Each property is mapped to _one_ entity or (in the cases of `climate` and `humidifier`) _one_ target property.
To make a property visible by default, just add the property to the list. Note that properties you do not map are still
mapped to [sensor](#type-sensor) entities, but _without_ `state_class`. This is done as some devices supports a lot
of properties, which will take up processing time and storage. Makes sure to include all properties of interest when
mapping a device!

Each property is mapped to _one_ entity or _one_ target property.

If you change the type of mapping, the old entity or state attribute will change to unavailable in Home Assistant.
You can bulk remove the old entities in on the [entities page](https://my.home-assistant.io/redirect/entities/)
by filtering on the device and status.

If you change unit or state class for sensors, you will need to fix the history in
[Home Assistant - Statistics](https://my.home-assistant.io/redirect/developer_statistics/).

You need to restart Home Assistant to load mapping changes.

### Mapping tips and tricks:

- Inspect the existing mappings files in this directory.
- Change settings in the ConnectLife app while monitoring value changes in Home Assistant. Take a note of which
property is changes, what the value is, and what the button or action is named in the ConnectLife app.
- Be aware that `true`, `false`, `yes`, `no`, `on`, and `off` are all interpreted as boolean values in YAML,
and must be quoted (e.g. `"off"`) to be interpreted as a string, e.g. in option lists. Note that some options
expects boolean (unquoted) values.
- Validate your mapping file with the [JSON schema](properties-schema.json).
- Remember to add translation strings.

## Property

| Item | Type | Description |
|-----------------|------------------------------------|---------------------------------------------------------------------------------------------------|
| `property` | string | Name of status/property. |
| `hide` | `true`, `false` | If Home Assistant should initially hide the sensor entity for this property. Defaults to `false`. |
| `icon` | `mdi:eye`, etc. | Icon to use for the entity. |
| `binary_sensor` | [BinarySensor](#type-binarysensor) | Create a binary sensor of the property. |
| `climate` | [Climate](#type-climate) | Map the property to a climate entity for the device. |
| `humidifier` | [Humidifier](#type-humidifier) | Map the property to a humidifier entity for the device. |
| `number` | [Number](#type-number) | Create a number entity of the property. |
| `select` | [Select](#type-select) | Create a selector of the property. |
| `sensor` | [Sensor](#type-sensor) | Create a sensor of the property. This is the default. |
| `switch` | [Switch](#type-switch) | Create a switch of the property. |
| `water_heater` | [WaterHeater](#type-waterheater) | Map the property to a water heater entity for the device. |
| Item | Type | Description |
|-----------------|------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------|
| `property` | string | Name of status/property. |
| `hide` | `true`, `false` | If Home Assistant should initially hide the sensor entity for this property. Defaults to `false`, but it set to `true` for unknown properties. |
| `icon` | `mdi:eye`, etc. | Icon to use for the entity. |
| `binary_sensor` | [BinarySensor](#type-binarysensor) | Create a binary sensor of the property. |
| `climate` | [Climate](#type-climate) | Map the property to a climate entity for the device. |
| `humidifier` | [Humidifier](#type-humidifier) | Map the property to a humidifier entity for the device. |
| `number` | [Number](#type-number) | Create a number entity of the property. |
| `select` | [Select](#type-select) | Create a selector of the property. |
| `sensor` | [Sensor](#type-sensor) | Create a sensor of the property. This is the default. |
| `switch` | [Switch](#type-switch) | Create a switch of the property. |
| `water_heater` | [WaterHeater](#type-waterheater) | Map the property to a water heater entity for the device. |

If an entity mapping is not given, the property is mapped to a sensor entity.

Expand Down Expand Up @@ -101,8 +125,8 @@ Number entities can be set by the user.

| Item | Type | Description |
|-----------------|-------------------------------------|-------------------------------------------------------------------------------------------------------------------------------|
| `min_value` | integer | Minimum value. |
| `max_value` | integer | Maximum value. |
| `min_value` | integer | Minimum value. |
| `max_value` | integer | Maximum value. |
| `device_class` | `duration`, `energy`, `water`, etc. | Name of any [NumberDeviceClass enum](https://developers.home-assistant.io/docs/core/entity/number/#available-device-classes). |
| `unit` | `min`, `°C`, `°F`, etc. | Required if `device_class` is set, except not allowed when `device_class` is `aqi` or `ph`. |

Expand All @@ -119,14 +143,14 @@ Remember to add options to [translation strings](#translation-strings).
Sensor entities are usually read-only, but this integration provides a `set_value` service that can be applied on
the `sensor.connectlife` entities, unless the sensor is set to `read_only: true`.

| Item | Type | Description |
|-----------------|--------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `read_only` | `true`, `false` | If this property is known to be read-only (prevents `set_value` service). |
| `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 `aqi`, `ph` or `enum`. |
| `options` | dictionary of integer to string | Required if `device_class` is set to `enum`. |
| `unknown_value` | integer | The value used by the API to signal unknown value. |
| Item | Type | Description |
|-----------------|--------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `read_only` | `true`, `false` | If this property is known to be read-only (prevents `set_value` service). |
| `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. 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 `aqi`, `ph` or `enum`. |
| `options` | dictionary of integer to string | Required if `device_class` is set to `enum`. |
| `unknown_value` | integer | The value used by the API to signal unknown value. |

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

Expand Down
11 changes: 10 additions & 1 deletion custom_components/connectlife/dictionaries.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,13 +285,22 @@ def get_dictionary(cls, appliance: ConnectLifeAppliance) -> dict[str, Property]:
key = f"{appliance.device_type_code}-{appliance.device_feature_code}"
if key in Dictionaries.dictionaries:
return Dictionaries.dictionaries[key]
dictionary = defaultdict(lambda: Property({PROPERTY: "default", HIDE: True}))
try:
dictionary = defaultdict(lambda: Property({PROPERTY: "unknown_property", HIDE: True}))
data = pkgutil.get_data(__name__, f"data_dictionaries/{key}.yaml")
parsed = yaml.safe_load(data)
for prop in parsed[PROPERTIES]:
dictionary[prop[PROPERTY]] = Property(prop)
except FileNotFoundError:
_LOGGER.warning("No data dictionary found for %s (%s)", appliance.device_nickname, key)
dictionary = defaultdict(
lambda: Property(
{
PROPERTY: "unknown_device",
HIDE: True,
Platform.SENSOR: {STATE_CLASS: SensorStateClass.MEASUREMENT}
}
)
)
Dictionaries.dictionaries[key] = dictionary
return dictionary
11 changes: 6 additions & 5 deletions custom_components/connectlife/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,12 @@ def __init__(
elif (device_class is None
and isinstance(self.coordinator.appliances[self.device_id].status_list[status], datetime.datetime)):
device_class = SensorDeviceClass.TIMESTAMP
state_class = dd_entry.sensor.state_class
if (state_class is None
and isinstance(self.coordinator.appliances[self.device_id].status_list[status], int)
and device_class != SensorDeviceClass.ENUM):
state_class = SensorStateClass.MEASUREMENT
state_class = (
dd_entry.sensor.state_class
if isinstance(self.coordinator.appliances[self.device_id].status_list[status], int)
and device_class != SensorDeviceClass.ENUM
else None
)
self.entity_description = SensorEntityDescription(
key=self._attr_unique_id,
device_class=device_class,
Expand Down