diff --git a/homeassistant/components/homekit/__init__.py b/homeassistant/components/homekit/__init__.py index 02d21889f6b7..1092cea0c6e7 100644 --- a/homeassistant/components/homekit/__init__.py +++ b/homeassistant/components/homekit/__init__.py @@ -11,7 +11,7 @@ from homeassistant.components.cover import SUPPORT_SET_POSITION from homeassistant.const import ( ATTR_SUPPORTED_FEATURES, ATTR_UNIT_OF_MEASUREMENT, - CONF_PORT, TEMP_CELSIUS, TEMP_FAHRENHEIT, + ATTR_DEVICE_CLASS, CONF_PORT, TEMP_CELSIUS, TEMP_FAHRENHEIT, EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP) import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entityfilter import FILTER_SCHEMA @@ -19,7 +19,9 @@ from homeassistant.util.decorator import Registry from .const import ( DOMAIN, HOMEKIT_FILE, CONF_AUTO_START, CONF_ENTITY_CONFIG, CONF_FILTER, - DEFAULT_PORT, DEFAULT_AUTO_START, SERVICE_HOMEKIT_START) + DEFAULT_PORT, DEFAULT_AUTO_START, SERVICE_HOMEKIT_START, + DEVICE_CLASS_CO2, DEVICE_CLASS_LIGHT, DEVICE_CLASS_HUMIDITY, + DEVICE_CLASS_PM25, DEVICE_CLASS_TEMPERATURE) from .util import ( validate_entity_config, show_setup_message) @@ -103,10 +105,22 @@ def get_accessory(hass, state, aid, config): elif state.domain == 'sensor': unit = state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) - if unit == TEMP_CELSIUS or unit == TEMP_FAHRENHEIT: + device_class = state.attributes.get(ATTR_DEVICE_CLASS) + + if device_class == DEVICE_CLASS_TEMPERATURE or unit == TEMP_CELSIUS \ + or unit == TEMP_FAHRENHEIT: a_type = 'TemperatureSensor' - elif unit == '%': + elif device_class == DEVICE_CLASS_HUMIDITY or unit == '%': a_type = 'HumiditySensor' + elif device_class == DEVICE_CLASS_PM25 \ + or DEVICE_CLASS_PM25 in state.entity_id: + a_type = 'AirQualitySensor' + elif device_class == DEVICE_CLASS_CO2 \ + or DEVICE_CLASS_CO2 in state.entity_id: + a_type = 'CarbonDioxideSensor' + elif device_class == DEVICE_CLASS_LIGHT or unit == 'lm' or \ + unit == 'lux': + a_type = 'LightSensor' elif state.domain == 'switch' or state.domain == 'remote' \ or state.domain == 'input_boolean' or state.domain == 'script': diff --git a/homeassistant/components/homekit/const.py b/homeassistant/components/homekit/const.py index 37ee9722bc4f..7cde51b54168 100644 --- a/homeassistant/components/homekit/const.py +++ b/homeassistant/components/homekit/const.py @@ -34,13 +34,13 @@ # #### Services #### SERV_ACCESSORY_INFO = 'AccessoryInformation' +SERV_AIR_QUALITY_SENSOR = 'AirQualitySensor' SERV_CARBON_DIOXIDE_SENSOR = 'CarbonDioxideSensor' SERV_CARBON_MONOXIDE_SENSOR = 'CarbonMonoxideSensor' SERV_CONTACT_SENSOR = 'ContactSensor' -SERV_HUMIDITY_SENSOR = 'HumiditySensor' -# CurrentRelativeHumidity | StatusActive, StatusFault, StatusTampered, -# StatusLowBattery, Name +SERV_HUMIDITY_SENSOR = 'HumiditySensor' # CurrentRelativeHumidity SERV_LEAK_SENSOR = 'LeakSensor' +SERV_LIGHT_SENSOR = 'LightSensor' SERV_LIGHTBULB = 'Lightbulb' # On | Brightness, Hue, Saturation, Name SERV_LOCK = 'LockMechanism' SERV_MOTION_SENSOR = 'MotionSensor' @@ -50,17 +50,21 @@ SERV_SWITCH = 'Switch' SERV_TEMPERATURE_SENSOR = 'TemperatureSensor' SERV_THERMOSTAT = 'Thermostat' -SERV_WINDOW_COVERING = 'WindowCovering' -# CurrentPosition, TargetPosition, PositionState +SERV_WINDOW_COVERING = 'WindowCovering' # CurrentPosition, TargetPosition # #### Characteristics #### +CHAR_AIR_PARTICULATE_DENSITY = 'AirParticulateDensity' +CHAR_AIR_QUALITY = 'AirQuality' CHAR_BRIGHTNESS = 'Brightness' # Int | [0, 100] CHAR_CARBON_DIOXIDE_DETECTED = 'CarbonDioxideDetected' +CHAR_CARBON_DIOXIDE_LEVEL = 'CarbonDioxideLevel' +CHAR_CARBON_DIOXIDE_PEAK_LEVEL = 'CarbonDioxidePeakLevel' CHAR_CARBON_MONOXIDE_DETECTED = 'CarbonMonoxideDetected' CHAR_COLOR_TEMPERATURE = 'ColorTemperature' CHAR_CONTACT_SENSOR_STATE = 'ContactSensorState' CHAR_COOLING_THRESHOLD_TEMPERATURE = 'CoolingThresholdTemperature' +CHAR_CURRENT_AMBIENT_LIGHT_LEVEL = 'CurrentAmbientLightLevel' CHAR_CURRENT_HEATING_COOLING = 'CurrentHeatingCoolingState' CHAR_CURRENT_POSITION = 'CurrentPosition' # Int | [0, 100] CHAR_CURRENT_HUMIDITY = 'CurrentRelativeHumidity' # percent @@ -93,8 +97,12 @@ # #### Device Class #### DEVICE_CLASS_CO2 = 'co2' DEVICE_CLASS_GAS = 'gas' +DEVICE_CLASS_HUMIDITY = 'humidity' +DEVICE_CLASS_LIGHT = 'light' DEVICE_CLASS_MOISTURE = 'moisture' DEVICE_CLASS_MOTION = 'motion' DEVICE_CLASS_OCCUPANCY = 'occupancy' DEVICE_CLASS_OPENING = 'opening' +DEVICE_CLASS_PM25 = 'pm25' DEVICE_CLASS_SMOKE = 'smoke' +DEVICE_CLASS_TEMPERATURE = 'temperature' diff --git a/homeassistant/components/homekit/type_sensors.py b/homeassistant/components/homekit/type_sensors.py old mode 100755 new mode 100644 index 790f0de61033..6aa8d92c0afc --- a/homeassistant/components/homekit/type_sensors.py +++ b/homeassistant/components/homekit/type_sensors.py @@ -10,6 +10,9 @@ from .const import ( CATEGORY_SENSOR, SERV_HUMIDITY_SENSOR, SERV_TEMPERATURE_SENSOR, CHAR_CURRENT_HUMIDITY, CHAR_CURRENT_TEMPERATURE, PROP_CELSIUS, + SERV_AIR_QUALITY_SENSOR, CHAR_AIR_QUALITY, CHAR_AIR_PARTICULATE_DENSITY, + CHAR_CARBON_DIOXIDE_LEVEL, CHAR_CARBON_DIOXIDE_PEAK_LEVEL, + SERV_LIGHT_SENSOR, CHAR_CURRENT_AMBIENT_LIGHT_LEVEL, DEVICE_CLASS_CO2, SERV_CARBON_DIOXIDE_SENSOR, CHAR_CARBON_DIOXIDE_DETECTED, DEVICE_CLASS_GAS, SERV_CARBON_MONOXIDE_SENSOR, CHAR_CARBON_MONOXIDE_DETECTED, @@ -18,7 +21,8 @@ DEVICE_CLASS_OCCUPANCY, SERV_OCCUPANCY_SENSOR, CHAR_OCCUPANCY_DETECTED, DEVICE_CLASS_OPENING, SERV_CONTACT_SENSOR, CHAR_CONTACT_SENSOR_STATE, DEVICE_CLASS_SMOKE, SERV_SMOKE_SENSOR, CHAR_SMOKE_DETECTED) -from .util import convert_to_float, temperature_to_homekit +from .util import ( + convert_to_float, temperature_to_homekit, density_to_air_quality) _LOGGER = logging.getLogger(__name__) @@ -81,6 +85,78 @@ def update_state(self, new_state): self.entity_id, humidity) +@TYPES.register('AirQualitySensor') +class AirQualitySensor(HomeAccessory): + """Generate a AirQualitySensor accessory as air quality sensor.""" + + def __init__(self, *args, config): + """Initialize a AirQualitySensor accessory object.""" + super().__init__(*args, category=CATEGORY_SENSOR) + + serv_air_quality = add_preload_service(self, SERV_AIR_QUALITY_SENSOR, + [CHAR_AIR_PARTICULATE_DENSITY]) + self.char_quality = setup_char( + CHAR_AIR_QUALITY, serv_air_quality, value=0) + self.char_density = setup_char( + CHAR_AIR_PARTICULATE_DENSITY, serv_air_quality, value=0) + + def update_state(self, new_state): + """Update accessory after state change.""" + density = convert_to_float(new_state.state) + if density is not None: + self.char_density.set_value(density) + self.char_quality.set_value(density_to_air_quality(density)) + _LOGGER.debug('%s: Set to %d', self.entity_id, density) + + +@TYPES.register('CarbonDioxideSensor') +class CarbonDioxideSensor(HomeAccessory): + """Generate a CarbonDioxideSensor accessory as CO2 sensor.""" + + def __init__(self, *args, config): + """Initialize a CarbonDioxideSensor accessory object.""" + super().__init__(*args, category=CATEGORY_SENSOR) + + serv_co2 = add_preload_service(self, SERV_CARBON_DIOXIDE_SENSOR, [ + CHAR_CARBON_DIOXIDE_LEVEL, CHAR_CARBON_DIOXIDE_PEAK_LEVEL]) + self.char_co2 = setup_char( + CHAR_CARBON_DIOXIDE_LEVEL, serv_co2, value=0) + self.char_peak = setup_char( + CHAR_CARBON_DIOXIDE_PEAK_LEVEL, serv_co2, value=0) + self.char_detected = setup_char( + CHAR_CARBON_DIOXIDE_DETECTED, serv_co2, value=0) + + def update_state(self, new_state): + """Update accessory after state change.""" + co2 = convert_to_float(new_state.state) + if co2 is not None: + self.char_co2.set_value(co2) + if co2 > self.char_peak.value: + self.char_peak.set_value(co2) + self.char_detected.set_value(co2 > 1000) + _LOGGER.debug('%s: Set to %d', self.entity_id, co2) + + +@TYPES.register('LightSensor') +class LightSensor(HomeAccessory): + """Generate a LightSensor accessory as light sensor.""" + + def __init__(self, *args, config): + """Initialize a LightSensor accessory object.""" + super().__init__(*args, category=CATEGORY_SENSOR) + + serv_light = add_preload_service(self, SERV_LIGHT_SENSOR) + self.char_light = setup_char( + CHAR_CURRENT_AMBIENT_LIGHT_LEVEL, serv_light, value=0) + + def update_state(self, new_state): + """Update accessory after state change.""" + luminance = convert_to_float(new_state.state) + if luminance is not None: + self.char_light.set_value(luminance) + _LOGGER.debug('%s: Set to %d', self.entity_id, luminance) + + @TYPES.register('BinarySensor') class BinarySensor(HomeAccessory): """Generate a BinarySensor accessory as binary sensor.""" diff --git a/homeassistant/components/homekit/util.py b/homeassistant/components/homekit/util.py index e14b6c47bc88..29fe3c8f2656 100644 --- a/homeassistant/components/homekit/util.py +++ b/homeassistant/components/homekit/util.py @@ -64,3 +64,16 @@ def temperature_to_homekit(temperature, unit): def temperature_to_states(temperature, unit): """Convert temperature back from Celsius to Home Assistant unit.""" return round(temp_util.convert(temperature, TEMP_CELSIUS, unit), 1) + + +def density_to_air_quality(density): + """Map PM2.5 density to HomeKit AirQuality level.""" + if density <= 35: + return 1 + elif density <= 75: + return 2 + elif density <= 115: + return 3 + elif density <= 150: + return 4 + return 5 diff --git a/tests/components/homekit/test_get_accessories.py b/tests/components/homekit/test_get_accessories.py index 6f2521fc4e5f..052b7557c117 100644 --- a/tests/components/homekit/test_get_accessories.py +++ b/tests/components/homekit/test_get_accessories.py @@ -41,6 +41,13 @@ def tearDown(self): """Test if mock type was called.""" self.assertTrue(self.mock_type.called) + def test_sensor_temperature(self): + """Test temperature sensor with device class temperature.""" + with patch.dict(TYPES, {'TemperatureSensor': self.mock_type}): + state = State('sensor.temperature', '23', + {ATTR_DEVICE_CLASS: 'temperature'}) + get_accessory(None, state, 2, {}) + def test_sensor_temperature_celsius(self): """Test temperature sensor with Celsius as unit.""" with patch.dict(TYPES, {'TemperatureSensor': self.mock_type}): @@ -56,12 +63,66 @@ def test_sensor_temperature_fahrenheit(self): get_accessory(None, state, 2, {}) def test_sensor_humidity(self): + """Test humidity sensor with device class humidity.""" + with patch.dict(TYPES, {'HumiditySensor': self.mock_type}): + state = State('sensor.humidity', '20', + {ATTR_DEVICE_CLASS: 'humidity'}) + get_accessory(None, state, 2, {}) + + def test_sensor_humidity_unit(self): """Test humidity sensor with % as unit.""" with patch.dict(TYPES, {'HumiditySensor': self.mock_type}): state = State('sensor.humidity', '20', {ATTR_UNIT_OF_MEASUREMENT: '%'}) get_accessory(None, state, 2, {}) + def test_air_quality_sensor(self): + """Test air quality sensor with pm25 class.""" + with patch.dict(TYPES, {'AirQualitySensor': self.mock_type}): + state = State('sensor.air_quality', '40', + {ATTR_DEVICE_CLASS: 'pm25'}) + get_accessory(None, state, 2, {}) + + def test_air_quality_sensor_entity_id(self): + """Test air quality sensor with entity_id contains pm25.""" + with patch.dict(TYPES, {'AirQualitySensor': self.mock_type}): + state = State('sensor.air_quality_pm25', '40', {}) + get_accessory(None, state, 2, {}) + + def test_co2_sensor(self): + """Test co2 sensor with device class co2.""" + with patch.dict(TYPES, {'CarbonDioxideSensor': self.mock_type}): + state = State('sensor.airmeter', '500', + {ATTR_DEVICE_CLASS: 'co2'}) + get_accessory(None, state, 2, {}) + + def test_co2_sensor_entity_id(self): + """Test co2 sensor with entity_id contains co2.""" + with patch.dict(TYPES, {'CarbonDioxideSensor': self.mock_type}): + state = State('sensor.airmeter_co2', '500', {}) + get_accessory(None, state, 2, {}) + + def test_light_sensor(self): + """Test light sensor with device class lux.""" + with patch.dict(TYPES, {'LightSensor': self.mock_type}): + state = State('sensor.light', '900', + {ATTR_DEVICE_CLASS: 'light'}) + get_accessory(None, state, 2, {}) + + def test_light_sensor_unit_lm(self): + """Test light sensor with lm as unit.""" + with patch.dict(TYPES, {'LightSensor': self.mock_type}): + state = State('sensor.light', '900', + {ATTR_UNIT_OF_MEASUREMENT: 'lm'}) + get_accessory(None, state, 2, {}) + + def test_light_sensor_unit_lux(self): + """Test light sensor with lux as unit.""" + with patch.dict(TYPES, {'LightSensor': self.mock_type}): + state = State('sensor.light', '900', + {ATTR_UNIT_OF_MEASUREMENT: 'lux'}) + get_accessory(None, state, 2, {}) + def test_binary_sensor(self): """Test binary sensor with opening class.""" with patch.dict(TYPES, {'BinarySensor': self.mock_type}): diff --git a/tests/components/homekit/test_type_sensors.py b/tests/components/homekit/test_type_sensors.py index f9dfb04b37c8..77bfc0c89012 100644 --- a/tests/components/homekit/test_type_sensors.py +++ b/tests/components/homekit/test_type_sensors.py @@ -3,7 +3,8 @@ from homeassistant.components.homekit.const import PROP_CELSIUS from homeassistant.components.homekit.type_sensors import ( - TemperatureSensor, HumiditySensor, BinarySensor, BINARY_SENSOR_SERVICE_MAP) + TemperatureSensor, HumiditySensor, AirQualitySensor, CarbonDioxideSensor, + LightSensor, BinarySensor, BINARY_SENSOR_SERVICE_MAP) from homeassistant.const import ( ATTR_UNIT_OF_MEASUREMENT, ATTR_DEVICE_CLASS, STATE_UNKNOWN, STATE_ON, STATE_OFF, STATE_HOME, STATE_NOT_HOME, TEMP_CELSIUS, TEMP_FAHRENHEIT) @@ -40,6 +41,7 @@ def test_temperature(self): self.hass.states.set(entity_id, STATE_UNKNOWN, {ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS}) self.hass.block_till_done() + self.assertEqual(acc.char_temp.value, 0.0) self.hass.states.set(entity_id, '20', {ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS}) @@ -63,14 +65,95 @@ def test_humidity(self): self.assertEqual(acc.char_humidity.value, 0) - self.hass.states.set(entity_id, STATE_UNKNOWN, - {ATTR_UNIT_OF_MEASUREMENT: "%"}) + self.hass.states.set(entity_id, STATE_UNKNOWN) self.hass.block_till_done() + self.assertEqual(acc.char_humidity.value, 0) - self.hass.states.set(entity_id, '20', {ATTR_UNIT_OF_MEASUREMENT: "%"}) + self.hass.states.set(entity_id, '20') self.hass.block_till_done() self.assertEqual(acc.char_humidity.value, 20) + def test_air_quality(self): + """Test if accessory is updated after state change.""" + entity_id = 'sensor.air_quality' + + acc = AirQualitySensor(self.hass, 'Air Quality', entity_id, + 2, config=None) + acc.run() + + self.assertEqual(acc.aid, 2) + self.assertEqual(acc.category, 10) # Sensor + + self.assertEqual(acc.char_density.value, 0) + self.assertEqual(acc.char_quality.value, 0) + + self.hass.states.set(entity_id, STATE_UNKNOWN) + self.hass.block_till_done() + self.assertEqual(acc.char_density.value, 0) + self.assertEqual(acc.char_quality.value, 0) + + self.hass.states.set(entity_id, '34') + self.hass.block_till_done() + self.assertEqual(acc.char_density.value, 34) + self.assertEqual(acc.char_quality.value, 1) + + self.hass.states.set(entity_id, '200') + self.hass.block_till_done() + self.assertEqual(acc.char_density.value, 200) + self.assertEqual(acc.char_quality.value, 5) + + def test_co2(self): + """Test if accessory is updated after state change.""" + entity_id = 'sensor.co2' + + acc = CarbonDioxideSensor(self.hass, 'CO2', entity_id, 2, config=None) + acc.run() + + self.assertEqual(acc.aid, 2) + self.assertEqual(acc.category, 10) # Sensor + + self.assertEqual(acc.char_co2.value, 0) + self.assertEqual(acc.char_peak.value, 0) + self.assertEqual(acc.char_detected.value, 0) + + self.hass.states.set(entity_id, STATE_UNKNOWN) + self.hass.block_till_done() + self.assertEqual(acc.char_co2.value, 0) + self.assertEqual(acc.char_peak.value, 0) + self.assertEqual(acc.char_detected.value, 0) + + self.hass.states.set(entity_id, '1100') + self.hass.block_till_done() + self.assertEqual(acc.char_co2.value, 1100) + self.assertEqual(acc.char_peak.value, 1100) + self.assertEqual(acc.char_detected.value, 1) + + self.hass.states.set(entity_id, '800') + self.hass.block_till_done() + self.assertEqual(acc.char_co2.value, 800) + self.assertEqual(acc.char_peak.value, 1100) + self.assertEqual(acc.char_detected.value, 0) + + def test_light(self): + """Test if accessory is updated after state change.""" + entity_id = 'sensor.light' + + acc = LightSensor(self.hass, 'Light', entity_id, 2, config=None) + acc.run() + + self.assertEqual(acc.aid, 2) + self.assertEqual(acc.category, 10) # Sensor + + self.assertEqual(acc.char_light.value, 0.0001) + + self.hass.states.set(entity_id, STATE_UNKNOWN) + self.hass.block_till_done() + self.assertEqual(acc.char_light.value, 0.0001) + + self.hass.states.set(entity_id, '300') + self.hass.block_till_done() + self.assertEqual(acc.char_light.value, 300) + def test_binary(self): """Test if accessory is updated after state change.""" entity_id = 'binary_sensor.opening' diff --git a/tests/components/homekit/test_util.py b/tests/components/homekit/test_util.py index 7465e9affab9..4a9521384bd5 100644 --- a/tests/components/homekit/test_util.py +++ b/tests/components/homekit/test_util.py @@ -2,13 +2,15 @@ import unittest import voluptuous as vol +import pytest from homeassistant.core import callback from homeassistant.components.homekit.accessories import HomeBridge from homeassistant.components.homekit.const import HOMEKIT_NOTIFY_ID from homeassistant.components.homekit.util import ( show_setup_message, dismiss_setup_message, convert_to_float, - temperature_to_homekit, temperature_to_states, ATTR_CODE) + temperature_to_homekit, temperature_to_states, ATTR_CODE, + density_to_air_quality) from homeassistant.components.homekit.util import validate_entity_config \ as vec from homeassistant.components.persistent_notification import ( @@ -20,6 +22,52 @@ from tests.common import get_test_home_assistant +def test_validate_entity_config(): + """Test validate entities.""" + configs = [{'invalid_entity_id': {}}, {'demo.test': 1}, + {'demo.test': 'test'}, {'demo.test': [1, 2]}, + {'demo.test': None}] + + for conf in configs: + with pytest.raises(vol.Invalid): + vec(conf) + + assert vec({}) == {} + assert vec({'alarm_control_panel.demo': {ATTR_CODE: '1234'}}) == \ + {'alarm_control_panel.demo': {ATTR_CODE: '1234'}} + + +def test_convert_to_float(): + """Test convert_to_float method.""" + assert convert_to_float(12) == 12 + assert convert_to_float(12.4) == 12.4 + assert convert_to_float(STATE_UNKNOWN) is None + assert convert_to_float(None) is None + + +def test_temperature_to_homekit(): + """Test temperature conversion from HA to HomeKit.""" + assert temperature_to_homekit(20.46, TEMP_CELSIUS) == 20.5 + assert temperature_to_homekit(92.1, TEMP_FAHRENHEIT) == 33.4 + + +def test_temperature_to_states(): + """Test temperature conversion from HomeKit to HA.""" + assert temperature_to_states(20, TEMP_CELSIUS) == 20.0 + assert temperature_to_states(20.2, TEMP_FAHRENHEIT) == 68.4 + + +def test_density_to_air_quality(): + """Test map PM2.5 density to HomeKit AirQuality level.""" + assert density_to_air_quality(0) == 1 + assert density_to_air_quality(35) == 1 + assert density_to_air_quality(35.1) == 2 + assert density_to_air_quality(75) == 2 + assert density_to_air_quality(115) == 3 + assert density_to_air_quality(150) == 4 + assert density_to_air_quality(300) == 5 + + class TestUtil(unittest.TestCase): """Test all HomeKit util methods.""" @@ -39,21 +87,6 @@ def tearDown(self): """Stop down everything that was started.""" self.hass.stop() - def test_validate_entity_config(self): - """Test validate entities.""" - configs = [{'invalid_entity_id': {}}, {'demo.test': 1}, - {'demo.test': 'test'}, {'demo.test': [1, 2]}, - {'demo.test': None}] - - for conf in configs: - with self.assertRaises(vol.Invalid): - vec(conf) - - self.assertEqual(vec({}), {}) - self.assertEqual( - vec({'alarm_control_panel.demo': {ATTR_CODE: '1234'}}), - {'alarm_control_panel.demo': {ATTR_CODE: '1234'}}) - def test_show_setup_msg(self): """Test show setup message as persistence notification.""" bridge = HomeBridge(self.hass) @@ -83,20 +116,3 @@ def test_dismiss_setup_msg(self): self.assertEqual( data[ATTR_SERVICE_DATA].get(ATTR_NOTIFICATION_ID, None), HOMEKIT_NOTIFY_ID) - - def test_convert_to_float(self): - """Test convert_to_float method.""" - self.assertEqual(convert_to_float(12), 12) - self.assertEqual(convert_to_float(12.4), 12.4) - self.assertIsNone(convert_to_float(STATE_UNKNOWN)) - self.assertIsNone(convert_to_float(None)) - - def test_temperature_to_homekit(self): - """Test temperature conversion from HA to HomeKit.""" - self.assertEqual(temperature_to_homekit(20.46, TEMP_CELSIUS), 20.5) - self.assertEqual(temperature_to_homekit(92.1, TEMP_FAHRENHEIT), 33.4) - - def test_temperature_to_states(self): - """Test temperature conversion from HomeKit to HA.""" - self.assertEqual(temperature_to_states(20, TEMP_CELSIUS), 20.0) - self.assertEqual(temperature_to_states(20.2, TEMP_FAHRENHEIT), 68.4)