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,