mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 20:57:21 +00:00
Add Carbon Monoxide HomeKit Sensor (#16664)
This commit is contained in:
parent
9fdf123a2f
commit
3bfe9e757e
@ -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'
|
||||
|
@ -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
|
||||
|
@ -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')
|
||||
|
@ -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'}),
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user