""" Implementation of KNX 2 byte Float-values. They correspond to the the following KDN DPT 9 class. """ from __future__ import annotations from xknx.exceptions import ConversionError from .dpt import DPTNumeric from .payload import DPTArray, DPTBinary class DPT2ByteFloat(DPTNumeric): """ Abstraction for KNX 2 Octet Floating Point Numbers. DPT 9.*** """ dpt_main_number = 9 dpt_sub_number: int | None = None value_type = "2byte_float" payload_length = 2 value_min = -671088.64 value_max = 670760.96 resolution = 0.01 @classmethod def from_knx(cls, payload: DPTArray | DPTBinary) -> float: """Parse/deserialize from KNX/IP raw data.""" raw = cls.validate_payload(payload) data = (raw[0] * 256) + raw[1] exponent = (data >> 11) & 0x0F significand = data & 0x7FF sign = data >> 15 if sign == 1: significand = significand - 2048 value = float(significand << exponent) / 100 if not cls._test_boundaries(value): raise ConversionError(f"Could not parse {cls.dpt_name()}", value=value) return value @classmethod def to_knx(cls, value: float) -> DPTArray: """Serialize to KNX/IP raw data.""" try: value = float(value) if not cls._test_boundaries(value): raise ValueError("Value out of range") knx_value = value * 100 if round(knx_value) == 0: # ETS converts values near 0 like this # -0.0051 -> 0x87FF # -0.005 -> 0x8000 ... we don't want this since its equivalent to -20.48 # 0.005 -> 0x0000 # 0.0051 -> 0x0001 return DPTArray((0x00, 0x00)) exponent = 0 while not -2048 <= knx_value <= 2047: exponent += 1 knx_value /= 2 mantisse = round(knx_value) & 0x7FF msb = exponent << 3 | mantisse >> 8 if knx_value < 0: msb |= 0x80 return DPTArray((msb, mantisse & 0xFF)) except ValueError as err: raise ConversionError( f"Could not serialize {cls.dpt_name()}", value=value ) from err @classmethod def _test_boundaries(cls, value: float) -> bool: """Test if value is within defined range for this object.""" return cls.value_min <= value <= cls.value_max class DPTTemperature(DPT2ByteFloat): """DPT 9.001 DPT_Value_Temp.""" dpt_main_number = 9 dpt_sub_number = 1 value_type = "temperature" unit = "°C" ha_device_class = "temperature" value_min = -273 value_max = 670760 class DPTTemperatureDifference2Byte(DPT2ByteFloat): """DPT 9.002 DPT_Value_Tempd.""" dpt_main_number = 9 dpt_sub_number = 2 value_type = "temperature_difference_2byte" unit = "K" ha_device_class = "temperature" value_min = -670760 value_max = 670760 class DPTTemperatureA(DPT2ByteFloat): """DPT 9.003 DPT_Value_Tempa.""" dpt_main_number = 9 dpt_sub_number = 3 value_type = "temperature_a" unit = "K/h" value_min = -670760 value_max = 670760 class DPTLux(DPT2ByteFloat): """DPT 9.004 DPT_Value_Lux.""" dpt_main_number = 9 dpt_sub_number = 4 value_type = "illuminance" unit = "lx" ha_device_class = "illuminance" value_min = 0 value_max = 670760 class DPTWsp(DPT2ByteFloat): """DPT 9.005 DPT_Value_Ws Speed (m/s).""" dpt_main_number = 9 dpt_sub_number = 5 value_type = "wind_speed_ms" unit = "m/s" ha_device_class = "wind_speed" value_min = 0 value_max = 670760 class DPTPressure2Byte(DPT2ByteFloat): """DPT 9.006 DPT_Value_Pres (Pa).""" dpt_main_number = 9 dpt_sub_number = 6 value_type = "pressure_2byte" unit = "Pa" ha_device_class = "pressure" value_min = 0 value_max = 670760 class DPTHumidity(DPT2ByteFloat): """DPT 9.007 DPT_Value_Humidity.""" dpt_main_number = 9 dpt_sub_number = 7 value_type = "humidity" unit = "%" ha_device_class = "humidity" value_min = 0 value_max = 670760 class DPTPartsPerMillion(DPT2ByteFloat): """DPT 9.008 DPT_Value_parts/million.""" dpt_main_number = 9 dpt_sub_number = 8 value_type = "ppm" unit = "ppm" class DPTAirFlow(DPT2ByteFloat): """DPT 9.009 DPT_Value_AirFlow.""" dpt_main_number = 9 dpt_sub_number = 9 value_type = "air_flow" unit = "m³/h" class DPTTime1(DPT2ByteFloat): """DPT 9.010 DPT_Value_Time1 (s).""" dpt_main_number = 9 dpt_sub_number = 10 value_type = "time_1" unit = "s" value_min = -670760 value_max = 670760 class DPTTime2(DPT2ByteFloat): """DPT 9.011 DPT_Value_Time2 (ms).""" dpt_main_number = 9 dpt_sub_number = 11 value_type = "time_2" unit = "ms" value_min = -670760 value_max = 670760 class DPTVoltage(DPT2ByteFloat): """DPT 9.020 DPT_Value_Voltage.""" dpt_main_number = 9 dpt_sub_number = 20 value_type = "voltage" unit = "mV" ha_device_class = "voltage" class DPTCurrent(DPT2ByteFloat): """DPT 9.021 DPT_Value_Curr (mA).""" dpt_main_number = 9 dpt_sub_number = 21 value_type = "curr" unit = "mA" ha_device_class = "current" class DPTPowerDensity(DPT2ByteFloat): """DPT 9.022 DPT_PowerDensity (W/m²).""" dpt_main_number = 9 dpt_sub_number = 22 value_type = "power_density" unit = "W/m²" class DPTKelvinPerPercent(DPT2ByteFloat): """DPT 9.023 DPT_KelvinPerPercent (K/%).""" dpt_main_number = 9 dpt_sub_number = 23 value_type = "kelvin_per_percent" unit = "K/%" class DPTPower2Byte(DPT2ByteFloat): """DPT 9.024 DPT_Power (kW).""" dpt_main_number = 9 dpt_sub_number = 24 value_type = "power_2byte" unit = "kW" ha_device_class = "power" class DPTVolumeFlow(DPT2ByteFloat): """DPT 9.025 DPT_Value_Volume_Flow (L/h).""" dpt_main_number = 9 dpt_sub_number = 25 value_type = "volume_flow" unit = "L/h" class DPTRainAmount(DPT2ByteFloat): """DPT 9.026 DPT_Rain_Amount (L/m²).""" dpt_main_number = 9 dpt_sub_number = 26 value_type = "rain_amount" unit = "L/m²" value_min = -671088.64 value_max = 670760.96 class DPTTemperatureF(DPT2ByteFloat): """DPT 9.027 DPT_Value_Temp_F.""" dpt_main_number = 9 dpt_sub_number = 27 value_type = "temperature_f" unit = "°F" ha_device_class = "temperature" value_min = -459.6 value_max = 670760 class DPTWspKmh(DPT2ByteFloat): """DPT 9.028 DPT_Value_Wsp_kmh Speed (km/h).""" dpt_main_number = 9 dpt_sub_number = 28 value_type = "wind_speed_kmh" unit = "km/h" ha_device_class = "wind_speed" value_min = 0 value_max = 670760 class DPTAbsoluteHumidity(DPT2ByteFloat): """DPT 9.029 DPT_Value_Absolute_Humidity.""" dpt_main_number = 9 dpt_sub_number = 29 value_type = "absolute_humidity" unit = "g/m³" value_min = 0 class DPTConcentrationUGM3(DPT2ByteFloat): """DPT 9.030 DPT_Value_Concentration_μgm3.""" dpt_main_number = 9 dpt_sub_number = 30 value_type = "concentration_ugm3" unit = "μg/m³" value_min = 0 class DPTEnthalpy(DPT2ByteFloat): """DPT 9.? 2-byte float value (with unit).""" dpt_main_number = 9 # this is here for backwards compatibility dpt_sub_number = 60000 value_type = "enthalpy" unit = "H"