From 328a04af814ae5f8d532ff4e64993f5ecee89103 Mon Sep 17 00:00:00 2001 From: Evan Moses Date: Sun, 26 Mar 2023 23:03:10 -0700 Subject: [PATCH 1/7] Add a granularity property to round to nearest 0.5C --- aircon/aircon.py | 22 +++++++++++++--------- aircon/properties.py | 12 ++++++++++++ 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/aircon/aircon.py b/aircon/aircon.py index 855c825..fe88bd0 100644 --- a/aircon/aircon.py +++ b/aircon/aircon.py @@ -157,10 +157,19 @@ def queue_command(self, name: str, value) -> None: # Device mode is set using t_control_value if issubclass(data_type, enum.Enum): data_value = data_type[value] - elif data_type is int and type(value) is str and '.' in value: - # Round rather than fail if the input is a float. - # This is commonly the case for temperatures converted by HA from Celsius. - data_value = round(float(value)) + elif data_type is int: + # incoming data may be an int or float; it's likely to be a float for + # temperatures converted by HA to/from Celsius. + float_val = float(value) + + # Granularity may be 0.5, in which case we round to the nearest 0.5, then apply precision + granularity = self._properties.get_granularity(name) + precision = self._properties.get_precision(name) + float_val = (round(float_val / granularity) * granularity) / precision + + # Update value precision for value to be sent to the A/C + # We assume that only int types have a precision value + data_value = round(float_val) else: data_value = data_type(value) @@ -174,11 +183,6 @@ def queue_command(self, name: str, value) -> None: data_value = data_value.value typed_value = data_type[value] - # Update value precision for value to be sent to the A/C - precision = self._properties.get_precision(name) - if precision != 1: - data_value = round(data_value / precision) - command = self._build_command(name, data_value) # There are (usually) no acks on commands, so also queue an update to the # property, to be run once the command is sent. diff --git a/aircon/properties.py b/aircon/properties.py index 278fdec..4d49713 100644 --- a/aircon/properties.py +++ b/aircon/properties.py @@ -153,8 +153,19 @@ def get_base_type(cls, attr: str): @classmethod def get_precision(cls, attr: str): + """Precision affects int values; they will be divided by the precision to get a result that's + sent to the device. A typical value is 0.1, which means that the value is multiplied by 10. + """ return cls._get_metadata(attr).get('precision', 1) + @classmethod + def get_granularity(cls, attr: str): + """Granularity affects int values. Any incoming float value v will be rounded to the nearest + granularity g by round(v/g) * g. A typical value is 0.5. Combined with precision of 0.1, this + can convert an incoming value of 20.1 to 200, or 20.4 to 205. + """ + return cls._get_metadata(attr).get('granularity', 1) + @classmethod def get_read_only(cls, attr: str): return cls._get_metadata(attr)['read_only'] @@ -404,6 +415,7 @@ class FglProperties(Properties): metadata={ 'base_type': 'integer', 'precision': 0.1, + 'granularity': 0.5, 'read_only': False }) af_vertical_direction: int = field(default=3, From bd82d19502c6df5eee112405c150210376972bf8 Mon Sep 17 00:00:00 2001 From: Evan Moses Date: Thu, 30 Mar 2023 22:21:23 -0700 Subject: [PATCH 2/7] rename precision -> scale, precision is now actual precision Scale is the scale factor for converting messages from the device to temperature readings, and precision is the precision of those readings (1.0 or 0.5 for known devices). Publish the precision on the mqtt discovery topic for HA. --- aircon/__main__.py | 2 +- aircon/aircon.py | 22 ++++++++++++++-------- aircon/properties.py | 22 +++++++++++----------- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/aircon/__main__.py b/aircon/__main__.py index 2d0d0e2..1fb2e30 100644 --- a/aircon/__main__.py +++ b/aircon/__main__.py @@ -211,7 +211,7 @@ async def run(parsed_args): 'topic': mqtt_topics['pub'].format(device.mac_address, 'available') }, ], - 'precision': 1.0, + 'precision': device.get_temp_precision(), 'temperature_unit': 'F' if device.is_fahrenheit else 'C' } topics = device.topics diff --git a/aircon/aircon.py b/aircon/aircon.py index fe88bd0..d2fe543 100644 --- a/aircon/aircon.py +++ b/aircon/aircon.py @@ -111,12 +111,18 @@ def get_property(self, name: str): def get_property_type(self, name: str): return self._properties.get_type(name) + def get_temp_precision(self) -> float: + """ This must be 1.0, 0.5, or 0.1 for HA MQTT precision """ + return float(self._properties.get_precision(self.topics['temp'])) + def update_property(self, name: str, value, notify_value=None) -> None: """Update the stored properties, if changed.""" - # Update value precision for value sent from the A/C + # Update value scale for value sent from the A/C + scale = self._properties.get_scale(name) precision = self._properties.get_precision(name) - if precision != 1: - value = round(value * precision) + # Scale by scale, then round to precision + value = round(value * scale / precision) * precision + if notify_value is None: notify_value = value @@ -162,13 +168,13 @@ def queue_command(self, name: str, value) -> None: # temperatures converted by HA to/from Celsius. float_val = float(value) - # Granularity may be 0.5, in which case we round to the nearest 0.5, then apply precision - granularity = self._properties.get_granularity(name) + # Precision may be 0.5, in which case we round to the nearest 0.5, then apply scale precision = self._properties.get_precision(name) - float_val = (round(float_val / granularity) * granularity) / precision + scale = self._properties.get_scale(name) + float_val = (round(float_val / precision) * precision) / scale - # Update value precision for value to be sent to the A/C - # We assume that only int types have a precision value + # Update value scale for value to be sent to the A/C + # We assume that only int types have a scale value data_value = round(float_val) else: data_value = data_type(value) diff --git a/aircon/properties.py b/aircon/properties.py index 4d49713..71bb345 100644 --- a/aircon/properties.py +++ b/aircon/properties.py @@ -152,19 +152,19 @@ def get_base_type(cls, attr: str): return cls._get_metadata(attr)['base_type'] @classmethod - def get_precision(cls, attr: str): - """Precision affects int values; they will be divided by the precision to get a result that's - sent to the device. A typical value is 0.1, which means that the value is multiplied by 10. + def get_scale(cls, attr: str) -> float: + """Scale affects int values; they will be divided by the scale to get a result that's + sent to the device, or multiplied when updating from the device. A typical value is 0.1. """ - return cls._get_metadata(attr).get('precision', 1) + return cls._get_metadata(attr).get('scale', 1) @classmethod - def get_granularity(cls, attr: str): - """Granularity affects int values. Any incoming float value v will be rounded to the nearest - granularity g by round(v/g) * g. A typical value is 0.5. Combined with precision of 0.1, this + def get_precision(cls, attr: str) -> float: + """Precision affects int values. Any incoming float value v will be rounded to the nearest + precision g by round(v/g) * g. A typical value is 0.5. Combined with a scale of 0.1, this can convert an incoming value of 20.1 to 200, or 20.4 to 205. """ - return cls._get_metadata(attr).get('granularity', 1) + return cls._get_metadata(attr).get('precision', 1) @classmethod def get_read_only(cls, attr: str): @@ -414,8 +414,8 @@ class FglProperties(Properties): adjust_temperature: int = field(default=25, metadata={ 'base_type': 'integer', - 'precision': 0.1, - 'granularity': 0.5, + 'scale': 0.1, + 'precision': 0.5, 'read_only': False }) af_vertical_direction: int = field(default=3, @@ -481,7 +481,7 @@ class FglBProperties(Properties): adjust_temperature: int = field(default=25, metadata={ 'base_type': 'integer', - 'precision': 0.1, + 'scale': 0.1, 'read_only': False }) af_vertical_move_step1: int = field(default=3, From 4e266835c24b50ff4c07e51f7ff2e0867d5c51dd Mon Sep 17 00:00:00 2001 From: Evan Moses Date: Thu, 30 Mar 2023 22:35:59 -0700 Subject: [PATCH 3/7] precision -> scale for upstream change --- aircon/properties.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aircon/properties.py b/aircon/properties.py index 1c46eb4..a92aa92 100644 --- a/aircon/properties.py +++ b/aircon/properties.py @@ -421,7 +421,7 @@ class FglProperties(Properties): display_temperature: int = field(default=25, metadata={ 'base_type': 'integer', - 'precision': 0.1, + 'scale': 0.1, 'read_only': True }) af_vertical_direction: int = field(default=3, @@ -493,7 +493,7 @@ class FglBProperties(Properties): display_temperature: int = field(default=25, metadata={ 'base_type': 'integer', - 'precision': 0.1, + 'scale': 0.1, 'read_only': True }) af_vertical_move_step1: int = field(default=3, From 6b92aa6402359c775551252ca88440c0c1c5bba0 Mon Sep 17 00:00:00 2001 From: Evan Moses Date: Thu, 30 Mar 2023 23:49:48 -0700 Subject: [PATCH 4/7] Only try to convert value if it's a number --- aircon/aircon.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/aircon/aircon.py b/aircon/aircon.py index d2fe543..f4f04f0 100644 --- a/aircon/aircon.py +++ b/aircon/aircon.py @@ -2,6 +2,7 @@ from dataclasses import dataclass, field, fields import enum import logging +import numbers import random import re import string @@ -118,10 +119,12 @@ def get_temp_precision(self) -> float: def update_property(self, name: str, value, notify_value=None) -> None: """Update the stored properties, if changed.""" # Update value scale for value sent from the A/C - scale = self._properties.get_scale(name) - precision = self._properties.get_precision(name) - # Scale by scale, then round to precision - value = round(value * scale / precision) * precision + data_type = self._properties.get_type(name) + if data_type is int: + scale = self._properties.get_scale(name) + precision = self._properties.get_precision(name) + # Scale by scale, then round to precision + value = round(value * scale / precision) * precision if notify_value is None: From 8a9fbdad2c36f33c420848ea1549f2361863593a Mon Sep 17 00:00:00 2001 From: Evan Moses Date: Wed, 7 Jun 2023 15:38:08 -0700 Subject: [PATCH 5/7] Update addon url and image name --- hassio/config.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hassio/config.json b/hassio/config.json index ddaab6d..0c13514 100644 --- a/hassio/config.json +++ b/hassio/config.json @@ -3,8 +3,8 @@ "version": "0.3.13", "slug": "hisense_ac", "description": "Interface for controlling Air Conditioners, e.g. with HiSense modules.", - "url": "https://github.com/deiger/AirCon", - "image": "deiger/aircon", + "url": "https://github.com/emoses/AirCon", + "image": "emoses/aircon", "arch": ["armhf", "armv7", "aarch64", "amd64", "i386"], "startup": "application", "boot": "auto", From 676d244f8e7d8d77329316f22a39a1e5d7a9defd Mon Sep 17 00:00:00 2001 From: Evan Moses Date: Sun, 6 Aug 2023 07:10:26 -0700 Subject: [PATCH 6/7] deiger -> emoses for fork --- README.md | 8 ++++---- docker-compose.yaml | 2 +- hassio/DOCS.md | 2 +- hassio/README.md | 2 +- hassio/config.json | 2 +- new_version.sh | 2 +- repository.json | 2 +- setup.py | 2 +- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index e372705..c3c2b43 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ If using [HomeAssistant], this is the preferred method. 1. In the HomeAssistant UI, enter **Supervisor → Add-on Store**. 1. Click **⋮ menu → Repositories**. -1. Add `https://github.com/deiger/AirCon` to the list. +1. Add `https://github.com/emoses/AirCon` to the list. 1. Choose **HiSense Air Conditioner** and install it. 1. Update the configuration as detailed within the add-on. 1. Start the add-on. Do not forget to enable **Start on boot** and **Watchdog**. @@ -71,10 +71,10 @@ Use this method if not using HomeAssistant, or if you prefer to set it up outsid journalctl CONTAINER_NAME=hisense_ac ``` -1. Profit! +1. Profit! The A/Cs should now be auto-discovered by [HomeAssistant] or [openHAB] (using the [HomeAssistant MQTT Components Binding](https://www.openhab.org/addons/bindings/mqtt.homeassistant/)). - [SmartThings] requires manual setup, using the [groovy file](devicetypes/deiger/hisense-air-conditioner.src/hisense-air-conditioner.groovy), see below. + [SmartThings] requires manual setup, using the [groovy file](devicetypes/emoses/hisense-air-conditioner.src/hisense-air-conditioner.groovy), see below. ## Run the A/C control server manually @@ -218,7 +218,7 @@ Listed here are the properties available through the API for standard A/Cs You will need a groovy script to enable SmartThings integration with the Air Conditioner, through the control server above. It currently implements the main functionality (turn on/off, AC mode, fan speed, dimmer etc.). -The groovy file is available [here](devicetypes/deiger/hisense-air-conditioner.src/hisense-air-conditioner.groovy), for download and installation through the [Groovy IDE](https://graph.api.smartthings.com). As I'm continuously improving this script, it would be more efficient to use the IDE's github integration, in order to stay up-to-date. +The groovy file is available [here](devicetypes/emoses/hisense-air-conditioner.src/hisense-air-conditioner.groovy), for download and installation through the [Groovy IDE](https://graph.api.smartthings.com). As I'm continuously improving this script, it would be more efficient to use the IDE's github integration, in order to stay up-to-date. HomeAsststant is now fully supported through [MQTT Discovery]. Properly configured devices are auto-configured and populated in the Lovelace dashboard. diff --git a/docker-compose.yaml b/docker-compose.yaml index 00c5a52..eafd379 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -10,7 +10,7 @@ services: hisense_ac: depends_on: - copy_config - image: deiger/aircon:0.3.15 + image: emoses/aircon:0.3.15 container_name: hisense_ac healthcheck: disable: true diff --git a/hassio/DOCS.md b/hassio/DOCS.md index 9134144..d27099b 100644 --- a/hassio/DOCS.md +++ b/hassio/DOCS.md @@ -13,7 +13,7 @@ # Configuration 1. Find your application code from the list - [here](https://github.com/deiger/AirCon#prerequisites). + [here](https://github.com/emoses/AirCon#prerequisites). 1. Set the configuration as follows: ```yaml app: diff --git a/hassio/README.md b/hassio/README.md index 7482289..2fa74c6 100644 --- a/hassio/README.md +++ b/hassio/README.md @@ -4,7 +4,7 @@ ## About -Use this add-on to add support for Hisense Air Conditioners. See [here](https://github.com/deiger/AirCon) for more details. +Use this add-on to add support for Hisense Air Conditioners. See [here](https://github.com/emoses/AirCon) for more details. [aarch64-shield]: https://img.shields.io/badge/aarch64-yes-green.svg [amd64-shield]: https://img.shields.io/badge/amd64-yes-green.svg diff --git a/hassio/config.json b/hassio/config.json index 34d62c5..a63225e 100644 --- a/hassio/config.json +++ b/hassio/config.json @@ -3,7 +3,7 @@ "version": "0.3.15", "slug": "hisense_ac", "description": "Interface for controlling Air Conditioners, e.g. with HiSense modules.", - "url": "https://github.com/emoses/AirCon", + "url": "https://github.com/emoses/Aircon", "image": "emoses/aircon", "arch": ["armhf", "armv7", "aarch64", "amd64", "i386"], "startup": "application", diff --git a/new_version.sh b/new_version.sh index 1828453..f279fe5 100755 --- a/new_version.sh +++ b/new_version.sh @@ -24,5 +24,5 @@ git tag -a $NEW_VERSION -m "$NEW_VERSION_MSG" git push docker buildx rm --all-inactive --force docker buildx create --name multiarch --driver docker-container --use || true -docker buildx build --platform linux/arm/v7,linux/arm64,linux/amd64,linux/386 -t deiger/aircon:$NEW_VERSION --push . +docker buildx build --platform linux/arm/v7,linux/arm64,linux/amd64,linux/386 -t emoses/aircon:$NEW_VERSION --push . git push --tags diff --git a/repository.json b/repository.json index 8b470e1..0d4c23c 100644 --- a/repository.json +++ b/repository.json @@ -1,5 +1,5 @@ { "name": "Home Assistant Add-on: HiSense Air Conditioners", - "url": "https://github.com/deiger/AirCon", + "url": "https://github.com/emoses/AirCon", "maintainer": "Dror Eiger " } diff --git a/setup.py b/setup.py index 30cd5dc..7dd15af 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ description='Interface for controlling Air Conditioners, e.g. with HiSense modules.', long_description=long_description, long_description_content_type='text/markdown', - url='https://github.com/deiger/AirCon', + url='https://github.com/emoses/AirCon', author='Dror Eiger', author_email='droreiger@gmail.com', license='GPL 3.0', From e7ec88c2b4467f485ff2ab0a55476d5582cf0841 Mon Sep 17 00:00:00 2001 From: Evan Moses Date: Sun, 6 Aug 2023 07:15:03 -0700 Subject: [PATCH 7/7] 0.3.15.1 --- aircon/__init__.py | 2 +- docker-compose.yaml | 2 +- hassio/config.json | 2 +- new_version.sh | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/aircon/__init__.py b/aircon/__init__.py index 47a01d8..8dd8467 100644 --- a/aircon/__init__.py +++ b/aircon/__init__.py @@ -1,2 +1,2 @@ from . import * -__version__ = '0.3.15' +__version__ = '0.3.15.1' diff --git a/docker-compose.yaml b/docker-compose.yaml index eafd379..f460b45 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -10,7 +10,7 @@ services: hisense_ac: depends_on: - copy_config - image: emoses/aircon:0.3.15 + image: emoses/aircon:0.3.15.1 container_name: hisense_ac healthcheck: disable: true diff --git a/hassio/config.json b/hassio/config.json index a63225e..73d5af4 100644 --- a/hassio/config.json +++ b/hassio/config.json @@ -1,6 +1,6 @@ { "name": "HiSense Air Conditioners", - "version": "0.3.15", + "version": "0.3.15.1", "slug": "hisense_ac", "description": "Interface for controlling Air Conditioners, e.g. with HiSense modules.", "url": "https://github.com/emoses/Aircon", diff --git a/new_version.sh b/new_version.sh index f279fe5..1b4793e 100755 --- a/new_version.sh +++ b/new_version.sh @@ -1,5 +1,5 @@ #!/bin/bash -set -e +set -xeo pipefail git pull @@ -12,7 +12,7 @@ else fi git tag -a $NEW_VERSION -m "$NEW_VERSION_MSG" -auto-changelog +#auto-changelog for f in aircon/__init__.py hassio/config.json docker-compose.yaml; do sed -i "" -e "s/$OLD_VERSION/$NEW_VERSION/" $f