Add Carbon Monoxide HomeKit Sensor (#16664)

This commit is contained in:
cdce8p 2018-09-21 12:51:02 +02:00 committed by GitHub
parent 9fdf123a2f
commit 3bfe9e757e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 105 additions and 21 deletions

View File

@ -22,9 +22,9 @@ from homeassistant.util import get_local_ip
from homeassistant.util.decorator import Registry
from .const import (
BRIDGE_NAME, CONF_AUTO_START, CONF_ENTITY_CONFIG, CONF_FEATURE_LIST,
CONF_FILTER, DEFAULT_AUTO_START, DEFAULT_PORT, DEVICE_CLASS_CO2,
DEVICE_CLASS_PM25, DOMAIN, HOMEKIT_FILE, SERVICE_HOMEKIT_START,
TYPE_OUTLET, TYPE_SWITCH)
CONF_FILTER, DEFAULT_AUTO_START, DEFAULT_PORT, DEVICE_CLASS_CO,
DEVICE_CLASS_CO2, DEVICE_CLASS_PM25, DOMAIN, HOMEKIT_FILE,
SERVICE_HOMEKIT_START, TYPE_OUTLET, TYPE_SWITCH)
from .util import (
show_setup_message, validate_entity_config, validate_media_player_features)
@ -150,6 +150,8 @@ def get_accessory(hass, driver, state, aid, config):
elif device_class == DEVICE_CLASS_PM25 \
or DEVICE_CLASS_PM25 in state.entity_id:
a_type = 'AirQualitySensor'
elif device_class == DEVICE_CLASS_CO:
a_type = 'CarbonMonoxideSensor'
elif device_class == DEVICE_CLASS_CO2 \
or DEVICE_CLASS_CO2 in state.entity_id:
a_type = 'CarbonDioxideSensor'

View File

@ -69,6 +69,8 @@ CHAR_CARBON_DIOXIDE_DETECTED = 'CarbonDioxideDetected'
CHAR_CARBON_DIOXIDE_LEVEL = 'CarbonDioxideLevel'
CHAR_CARBON_DIOXIDE_PEAK_LEVEL = 'CarbonDioxidePeakLevel'
CHAR_CARBON_MONOXIDE_DETECTED = 'CarbonMonoxideDetected'
CHAR_CARBON_MONOXIDE_LEVEL = 'CarbonMonoxideLevel'
CHAR_CARBON_MONOXIDE_PEAK_LEVEL = 'CarbonMonoxidePeakLevel'
CHAR_CHARGING_STATE = 'ChargingState'
CHAR_COLOR_TEMPERATURE = 'ColorTemperature'
CHAR_CONTACT_SENSOR_STATE = 'ContactSensorState'
@ -114,6 +116,7 @@ PROP_MIN_VALUE = 'minValue'
PROP_CELSIUS = {'minValue': -273, 'maxValue': 999}
# #### Device Classes ####
DEVICE_CLASS_CO = 'co'
DEVICE_CLASS_CO2 = 'co2'
DEVICE_CLASS_DOOR = 'door'
DEVICE_CLASS_GARAGE_DOOR = 'garage_door'
@ -125,3 +128,7 @@ DEVICE_CLASS_OPENING = 'opening'
DEVICE_CLASS_PM25 = 'pm25'
DEVICE_CLASS_SMOKE = 'smoke'
DEVICE_CLASS_WINDOW = 'window'
# #### Thresholds ####
THRESHOLD_CO = 25
THRESHOLD_CO2 = 1000

View File

@ -13,6 +13,7 @@ from .const import (
CHAR_AIR_PARTICULATE_DENSITY, CHAR_AIR_QUALITY,
CHAR_CARBON_DIOXIDE_DETECTED, CHAR_CARBON_DIOXIDE_LEVEL,
CHAR_CARBON_DIOXIDE_PEAK_LEVEL, CHAR_CARBON_MONOXIDE_DETECTED,
CHAR_CARBON_MONOXIDE_LEVEL, CHAR_CARBON_MONOXIDE_PEAK_LEVEL,
CHAR_CONTACT_SENSOR_STATE, CHAR_CURRENT_AMBIENT_LIGHT_LEVEL,
CHAR_CURRENT_HUMIDITY, CHAR_CURRENT_TEMPERATURE, CHAR_LEAK_DETECTED,
CHAR_MOTION_DETECTED, CHAR_OCCUPANCY_DETECTED, CHAR_SMOKE_DETECTED,
@ -23,7 +24,7 @@ from .const import (
SERV_CARBON_DIOXIDE_SENSOR, SERV_CARBON_MONOXIDE_SENSOR,
SERV_CONTACT_SENSOR, SERV_HUMIDITY_SENSOR, SERV_LEAK_SENSOR,
SERV_LIGHT_SENSOR, SERV_MOTION_SENSOR, SERV_OCCUPANCY_SENSOR,
SERV_SMOKE_SENSOR, SERV_TEMPERATURE_SENSOR)
SERV_SMOKE_SENSOR, SERV_TEMPERATURE_SENSOR, THRESHOLD_CO, THRESHOLD_CO2)
from .util import (
convert_to_float, temperature_to_homekit, density_to_air_quality)
@ -114,6 +115,34 @@ class AirQualitySensor(HomeAccessory):
_LOGGER.debug('%s: Set to %d', self.entity_id, density)
@TYPES.register('CarbonMonoxideSensor')
class CarbonMonoxideSensor(HomeAccessory):
"""Generate a CarbonMonoxidSensor accessory as CO sensor."""
def __init__(self, *args):
"""Initialize a CarbonMonoxideSensor accessory object."""
super().__init__(*args, category=CATEGORY_SENSOR)
serv_co = self.add_preload_service(SERV_CARBON_MONOXIDE_SENSOR, [
CHAR_CARBON_MONOXIDE_LEVEL, CHAR_CARBON_MONOXIDE_PEAK_LEVEL])
self.char_level = serv_co.configure_char(
CHAR_CARBON_MONOXIDE_LEVEL, value=0)
self.char_peak = serv_co.configure_char(
CHAR_CARBON_MONOXIDE_PEAK_LEVEL, value=0)
self.char_detected = serv_co.configure_char(
CHAR_CARBON_MONOXIDE_DETECTED, value=0)
def update_state(self, new_state):
"""Update accessory after state change."""
value = convert_to_float(new_state.state)
if value:
self.char_level.set_value(value)
if value > self.char_peak.value:
self.char_peak.set_value(value)
self.char_detected.set_value(value > THRESHOLD_CO)
_LOGGER.debug('%s: Set to %d', self.entity_id, value)
@TYPES.register('CarbonDioxideSensor')
class CarbonDioxideSensor(HomeAccessory):
"""Generate a CarbonDioxideSensor accessory as CO2 sensor."""
@ -124,7 +153,7 @@ class CarbonDioxideSensor(HomeAccessory):
serv_co2 = self.add_preload_service(SERV_CARBON_DIOXIDE_SENSOR, [
CHAR_CARBON_DIOXIDE_LEVEL, CHAR_CARBON_DIOXIDE_PEAK_LEVEL])
self.char_co2 = serv_co2.configure_char(
self.char_level = serv_co2.configure_char(
CHAR_CARBON_DIOXIDE_LEVEL, value=0)
self.char_peak = serv_co2.configure_char(
CHAR_CARBON_DIOXIDE_PEAK_LEVEL, value=0)
@ -133,13 +162,13 @@ class CarbonDioxideSensor(HomeAccessory):
def update_state(self, new_state):
"""Update accessory after state change."""
co2 = convert_to_float(new_state.state)
if co2:
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)
value = convert_to_float(new_state.state)
if value:
self.char_level.set_value(value)
if value > self.char_peak.value:
self.char_peak.set_value(value)
self.char_detected.set_value(value > THRESHOLD_CO2)
_LOGGER.debug('%s: Set to %d', self.entity_id, value)
@TYPES.register('LightSensor')

View File

@ -106,6 +106,8 @@ def test_type_covers(type_name, entity_id, state, attrs):
('AirQualitySensor', 'sensor.air_quality_pm25', '40', {}),
('AirQualitySensor', 'sensor.air_quality', '40',
{ATTR_DEVICE_CLASS: 'pm25'}),
('CarbonMonoxideSensor', 'sensor.airmeter', '2',
{ATTR_DEVICE_CLASS: 'co'}),
('CarbonDioxideSensor', 'sensor.airmeter_co2', '500', {}),
('CarbonDioxideSensor', 'sensor.airmeter', '500',
{ATTR_DEVICE_CLASS: 'co2'}),

View File

@ -1,8 +1,9 @@
"""Test different accessory types: Sensors."""
from homeassistant.components.homekit.const import PROP_CELSIUS
from homeassistant.components.homekit.const import (
PROP_CELSIUS, THRESHOLD_CO, THRESHOLD_CO2)
from homeassistant.components.homekit.type_sensors import (
AirQualitySensor, BinarySensor, CarbonDioxideSensor, HumiditySensor,
LightSensor, TemperatureSensor, BINARY_SENSOR_SERVICE_MAP)
AirQualitySensor, BinarySensor, CarbonMonoxideSensor, CarbonDioxideSensor,
HumiditySensor, LightSensor, TemperatureSensor, BINARY_SENSOR_SERVICE_MAP)
from homeassistant.const import (
ATTR_DEVICE_CLASS, ATTR_UNIT_OF_MEASUREMENT, STATE_HOME, STATE_NOT_HOME,
STATE_OFF, STATE_ON, STATE_UNKNOWN, TEMP_CELSIUS, TEMP_FAHRENHEIT)
@ -94,6 +95,45 @@ async def test_air_quality(hass, hk_driver):
assert acc.char_quality.value == 5
async def test_co(hass, hk_driver):
"""Test if accessory is updated after state change."""
entity_id = 'sensor.co'
hass.states.async_set(entity_id, None)
await hass.async_block_till_done()
acc = CarbonMonoxideSensor(hass, hk_driver, 'CO', entity_id, 2, None)
await hass.async_add_job(acc.run)
assert acc.aid == 2
assert acc.category == 10 # Sensor
assert acc.char_level.value == 0
assert acc.char_peak.value == 0
assert acc.char_detected.value == 0
hass.states.async_set(entity_id, STATE_UNKNOWN)
await hass.async_block_till_done()
assert acc.char_level.value == 0
assert acc.char_peak.value == 0
assert acc.char_detected.value == 0
value = 32
assert value > THRESHOLD_CO
hass.states.async_set(entity_id, str(value))
await hass.async_block_till_done()
assert acc.char_level.value == 32
assert acc.char_peak.value == 32
assert acc.char_detected.value == 1
value = 10
assert value < THRESHOLD_CO
hass.states.async_set(entity_id, str(value))
await hass.async_block_till_done()
assert acc.char_level.value == 10
assert acc.char_peak.value == 32
assert acc.char_detected.value == 0
async def test_co2(hass, hk_driver):
"""Test if accessory is updated after state change."""
entity_id = 'sensor.co2'
@ -106,25 +146,29 @@ async def test_co2(hass, hk_driver):
assert acc.aid == 2
assert acc.category == 10 # Sensor
assert acc.char_co2.value == 0
assert acc.char_level.value == 0
assert acc.char_peak.value == 0
assert acc.char_detected.value == 0
hass.states.async_set(entity_id, STATE_UNKNOWN)
await hass.async_block_till_done()
assert acc.char_co2.value == 0
assert acc.char_level.value == 0
assert acc.char_peak.value == 0
assert acc.char_detected.value == 0
hass.states.async_set(entity_id, '1100')
value = 1100
assert value > THRESHOLD_CO2
hass.states.async_set(entity_id, str(value))
await hass.async_block_till_done()
assert acc.char_co2.value == 1100
assert acc.char_level.value == 1100
assert acc.char_peak.value == 1100
assert acc.char_detected.value == 1
hass.states.async_set(entity_id, '800')
value = 800
assert value < THRESHOLD_CO2
hass.states.async_set(entity_id, str(value))
await hass.async_block_till_done()
assert acc.char_co2.value == 800
assert acc.char_level.value == 800
assert acc.char_peak.value == 1100
assert acc.char_detected.value == 0