Support binary_sensor and device_tracker for HomeKit#13701
Support binary_sensor and device_tracker for HomeKit#13701Yonsm wants to merge 31 commits intohome-assistant:devfrom Yonsm:Yonsm_dev
Conversation
Map binary_sensor and device_tracker to HomeKit BinarySensor, based on ‘homekit_device_class’ or ‘device_class’ attributes: 'gas': 'CarbonMonoxideSensor' 'co2': 'CarbonDioxideSensor' 'occupancy': 'OccupancySensor' 'opening': 'ContactSensor' 'motion': 'MotionSensor' 'moisture': 'LeakSensor' 'smoke': 'SmokeSensor' Default device_class is 'occupancy'.
| service_characteristic = service_map[device_class] if (device_class in service_map) else service_map['occupancy'] | ||
|
|
||
| service = add_preload_service(self, service_characteristic[0]) | ||
| self.char_detected = service.get_characteristic(service_characteristic[1]) |
|
|
||
| device_class_key = 'homekit_device_class' if ('homekit_device_class' in state.attributes) else 'device_class' | ||
| device_class = state.attributes.get(device_class_key) | ||
| service_characteristic = service_map[device_class] if (device_class in service_map) else service_map['occupancy'] |
There was a problem hiding this comment.
line too long (121 > 79 characters)
| 'moisture': ('LeakSensor', 'LeakDetected'), | ||
| 'smoke': ('SmokeSensor', 'SmokeDetected')} | ||
|
|
||
| device_class_key = 'homekit_device_class' if ('homekit_device_class' in state.attributes) else 'device_class' |
There was a problem hiding this comment.
line too long (117 > 79 characters)
|
|
||
| def __init__(self, hass, state, **kwargs): | ||
| """Initialize a BinarySensor accessory object.""" | ||
| super().__init__(state.name, state.entity_id, CATEGORY_SENSOR, **kwargs) |
|
This is my first pull request. And if success, I'll update more HomeKit support for: |
cdce8p
left a comment
There was a problem hiding this comment.
Great first Home Assistant PR! Looking forward for more to come.
I've left some comments.
|
|
||
| elif state.domain == 'binary_sensor' or state.domain == 'device_tracker': | ||
| _LOGGER.debug('Add "%s" as "%s"', state.entity_id, 'BinarySensor') | ||
| return TYPES['BinarySensor'](hass, state, aid=aid) |
There was a problem hiding this comment.
You don't need to pass in the state, only the entity_id: state.entity_id.
There was a problem hiding this comment.
Only the entity_id is not enough, we need state.attributes to check binary sensor type. I'll follow your style to pass name, entity_id and one more param state.attributes.
| class BinarySensor(HomeAccessory): | ||
| """Generate a BinarySensor accessory as binary sensor.""" | ||
|
|
||
| def __init__(self, hass, state, **kwargs): |
|
|
||
| def __init__(self, hass, state, **kwargs): | ||
| """Initialize a BinarySensor accessory object.""" | ||
| entity_id = state.entity_id |
There was a problem hiding this comment.
Isn't necessary if entity_id is passed to constructor.
| 'opening': ('ContactSensor', 'ContactSensorState'), | ||
| 'motion': ('MotionSensor', 'MotionDetected'), | ||
| 'moisture': ('LeakSensor', 'LeakDetected'), | ||
| 'smoke': ('SmokeSensor', 'SmokeDetected')} |
There was a problem hiding this comment.
Those should be constants defined in ./const.py. E.g.:
DEVICE_CLASS_GAS = 'gas'SERV_GAS_SENSOR = CarbonMonoxideSensorCHAR_GAS_DETECTED = CarbonMonoxideDetected- ...
service_map a constant in type_sensors.py outside any class, like so:
SERVICE_MAP = {
DEVICE_CLASS_GAS: {SERV_GAS_SENSOR, CHAR_GAS_DETECTED),
# ...
}There was a problem hiding this comment.
Good coding style. I'll follow it.
|
|
||
| device_class_key = 'homekit_device_class' \ | ||
| if ('homekit_device_class' in state.attributes) \ | ||
| else 'device_class' |
There was a problem hiding this comment.
Currently there is no homekit_device_class attribute. For this PR we should concentrate just on the device_class.
There was a problem hiding this comment.
homekit_device_class it just like homebridge_outlet_type, etc in HomeBridge/homegbridge+homeassistant. It's preserved for user to assign this param in customization.yaml manually. Because sometimes there is no device_class in binary_sensor's attributes, and user can assign it to gas co/co2, or any other binary_sensor.
There will be more homekit_xxxx key for CO2/Light/AirQuality sensor to get correct sensor type instead of guessing by unit or entity id. I'll document it in PR comment.
There was a problem hiding this comment.
We shouldn't write those in the state machine. I would prefer to leave it at device_class only for the moment. If you really want to implement this, use the entity_config dictionary from the homekit component. A good example is the alarm_code for type_security_systems.py.
| device_class_key = 'homekit_device_class' \ | ||
| if ('homekit_device_class' in state.attributes) \ | ||
| else 'device_class' | ||
| device_class = state.attributes.get(device_class_key) |
There was a problem hiding this comment.
Use ATTR_DEVICE_CLASS imported from homeassistant.const as key
| (SERV_MOTION_SENSOR, CHAR_MOTION_DETECTED), | ||
| DEVICE_CLASS_MOISTURE: | ||
| (SERV_LEAK_SENSOR, CHAR_LEAK_DETECTED), | ||
| DEVICE_CLASS_SMOKE: |
| (SERV_CONTACT_SENSOR, CHAR_CONTACT_SENSOR_STATE), | ||
| DEVICE_CLASS_MOTION: | ||
| (SERV_MOTION_SENSOR, CHAR_MOTION_DETECTED), | ||
| DEVICE_CLASS_MOISTURE: |
| (SERV_OCCUPANCY_SENSOR, CHAR_OCCUPANCY_DETECTED), | ||
| DEVICE_CLASS_OPENING: | ||
| (SERV_CONTACT_SENSOR, CHAR_CONTACT_SENSOR_STATE), | ||
| DEVICE_CLASS_MOTION: |
| (SERV_CARBON_DIOXIDE_SENSOR, CHAR_CARBON_DIOXIDE_DETECTED), | ||
| DEVICE_CLASS_OCCUPANCY: | ||
| (SERV_OCCUPANCY_SENSOR, CHAR_OCCUPANCY_DETECTED), | ||
| DEVICE_CLASS_OPENING: |
| (SERV_CARBON_MONOXIDE_SENSOR, CHAR_CARBON_MONOXIDE_DETECTED), | ||
| DEVICE_CLASS_CO2: | ||
| (SERV_CARBON_DIOXIDE_SENSOR, CHAR_CARBON_DIOXIDE_DETECTED), | ||
| DEVICE_CLASS_OCCUPANCY: |
| service_map = { | ||
| DEVICE_CLASS_GAS: | ||
| (SERV_CARBON_MONOXIDE_SENSOR, CHAR_CARBON_MONOXIDE_DETECTED), | ||
| DEVICE_CLASS_CO2: |
| self.entity_id = entity_id | ||
|
|
||
| service_map = { | ||
| DEVICE_CLASS_GAS: |
| CHAR_CURRENT_HUMIDITY, CHAR_CURRENT_TEMPERATURE, PROP_CELSIUS) | ||
| CHAR_CURRENT_HUMIDITY, CHAR_CURRENT_TEMPERATURE, PROP_CELSIUS, | ||
| DEVICE_CLASS_GAS, SERV_CARBON_MONOXIDE_SENSOR, | ||
| CHAR_CARBON_MONOXIDE_DETECTED, |
There was a problem hiding this comment.
continuation line unaligned for hanging indent
| CATEGORY_SENSOR, SERV_HUMIDITY_SENSOR, SERV_TEMPERATURE_SENSOR, | ||
| CHAR_CURRENT_HUMIDITY, CHAR_CURRENT_TEMPERATURE, PROP_CELSIUS) | ||
| CHAR_CURRENT_HUMIDITY, CHAR_CURRENT_TEMPERATURE, PROP_CELSIUS, | ||
| DEVICE_CLASS_GAS, SERV_CARBON_MONOXIDE_SENSOR, |
cdce8p
left a comment
There was a problem hiding this comment.
Please take a look at: #13701 (comment) as well if you haven't seen it already.
| class BinarySensor(HomeAccessory): | ||
| """Generate a BinarySensor accessory as binary sensor.""" | ||
|
|
||
| def __init__(self, hass, entity_id, name, attributes, **kwargs): |
There was a problem hiding this comment.
Don't pass attributes. Use hass instead, like so:
self.hass.states.get(self.entity_id).attributes
| self.hass = hass | ||
| self.entity_id = entity_id | ||
|
|
||
| service_map = { |
There was a problem hiding this comment.
Please move this between _LOGGER and @TYPES.register('TemperatureSensor') and rename it to something like BINARY_SENSOR_SERVICE_MAP
| DEVICE_CLASS_SMOKE = 'smoke' | ||
|
|
||
| # #### Attributes #### | ||
| ATTR_DEVICE_CLASS = 'device_class' |
There was a problem hiding this comment.
ATTR_DEVICE_CLASS is already defined in homeassistant.const
| SERV_CONTACT_SENSOR = 'ContactSensor' | ||
| SERV_MOTION_SENSOR = 'MotionSensor' | ||
| SERV_LEAK_SENSOR = 'LeakSensor' | ||
| SERV_SMOKE_SENSOR = 'SmokeSensor' |
There was a problem hiding this comment.
Please sort them alphabetically. The chars and device_classes as well.
There was a problem hiding this comment.
Follow it.
Furthermore, I also sort CHAR_CURRENT_HUMIDITY and CHAR_CURRENT_POSITION, and this cause a conflict. Should I revert it, or would you resolve it?
There was a problem hiding this comment.
Try rebaseing it against dev: https://www.home-assistant.io/developers/development_catching_up
1. Remove state param;
2. Sort const;
3. Import ATTR_DEVICE_CLASS from homeassistant.const;
4. Remove device_class_key:
device_class_key = ATTR_HOMEKIT_DEVICE_CLASS \
if (ATTR_HOMEKIT_DEVICE_CLASS in attributes) \
else ATTR_DEVICE_CLASS
| DEVICE_CLASS_SMOKE: | ||
| (SERV_SMOKE_SENSOR, CHAR_SMOKE_DETECTED)} | ||
|
|
||
| @TYPES.register('BinarySensor') |
| CHAR_CURRENT_HUMIDITY, CHAR_CURRENT_TEMPERATURE, PROP_CELSIUS) | ||
| CHAR_CURRENT_HUMIDITY, CHAR_CURRENT_TEMPERATURE, PROP_CELSIUS, | ||
| DEVICE_CLASS_CO2, SERV_CARBON_DIOXIDE_SENSOR, CHAR_CARBON_DIOXIDE_DETECTED, | ||
| DEVICE_CLASS_GAS, SERV_CARBON_MONOXIDE_SENSOR, CHAR_CARBON_MONOXIDE_DETECTED, |
| 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, | ||
| ATTR_HOMEKIT_DEVICE_CLASS) |
There was a problem hiding this comment.
ATTR_HOMEKIT_DEVICE_CLASS is an unused import.
| DEVICE_CLASS_OPENING: | ||
| (SERV_CONTACT_SENSOR, CHAR_CONTACT_SENSOR_STATE), | ||
| DEVICE_CLASS_SMOKE: | ||
| (SERV_SMOKE_SENSOR, CHAR_SMOKE_DETECTED)} |
There was a problem hiding this comment.
Is that an option?
BINARY_SENSOR_SERVICE_MAP = {
DEVICE_CLASS_CO2: (SERV_CARBON_DIOXIDE_SENSOR,
CHAR_CARBON_DIOXIDE_DETECTED),
DEVICE_CLASS_GAS: (SERV_CARBON_MONOXIDE_SENSOR,
CHAR_CARBON_MONOXIDE_DETECTED),
DEVICE_CLASS_MOISTURE: (SERV_LEAK_SENSOR, CHAR_LEAK_DETECTED),
DEVICE_CLASS_MOTION: (SERV_MOTION_SENSOR, CHAR_MOTION_DETECTED),
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)}There was a problem hiding this comment.
Can you move the BINARY_SENSOR_SERVICE_MAP above the TemperatureSensor class. All constants should be defined belove the imports.
| self.entity_id, humidity) | ||
|
|
||
|
|
||
| # Binary sensor service map |
| self.entity_id = entity_id | ||
|
|
||
| attributes = hass.states.get(entity_id).attributes | ||
| device_class = attributes.get(ATTR_DEVICE_CLASS) |
There was a problem hiding this comment.
You can combine those two lines and please use self.hass and self.entity_id:
device_class = self.hass.states.get(self.entity_id).attributes.get(ATTR_DEVICE_CLASS)There was a problem hiding this comment.
- Use attributes for homekit_device_class checking in future. But I'll follow your advice to remove it at this time.
- I don't think self.hass is better then hass. Actually, local variable is better performance then member variable, and it's good style for reading (less text).
There was a problem hiding this comment.
It might be a little. However this will change with #13707. This PR will require to use self.hass and self.entity_id.
Update:
IMO it's a style question. At the moment all other types use self.hass.
| attributes = hass.states.get(entity_id).attributes | ||
| device_class = attributes.get(ATTR_DEVICE_CLASS) | ||
| service_char = BINARY_SENSOR_SERVICE_MAP[device_class] \ | ||
| if (device_class in BINARY_SENSOR_SERVICE_MAP) \ |
There was a problem hiding this comment.
You don't need brackets here. if device_class in BINARY_SENSOR_SERVICE_MAP \ is enough.
# Conflicts: # homeassistant/components/homekit/const.py
* Version bump to HAP-python==1.1.9 * Updated types and tests
cdce8p
left a comment
There was a problem hiding this comment.
The BinarySensor class is looking good. Just on issue relate to an update of HAP-python. Can you add tests and the documentation? The tests should go in 'test_get_accessory' and 'test_type_sensor'.
|
|
||
| state = new_state.state | ||
| detected = (state == STATE_ON) or (state == STATE_HOME) | ||
| self.char_detected.set_value(detected, should_callback=False) |
There was a problem hiding this comment.
We just updated HAP-python to 1.1.9. 'Should_callback=False' will no longer be necessary. You probably need to rebase your branch, take take advantage of the update.
* Allow use of date_string in service call * Add stricter validation, fix descriptions
* Add async timeout feature * Decorator for setter methods to limit service calls to HA * Changed to async * Use async_call_later * Use lastargs, async_add_job * Use dict for lastargs * Updated tests to stop patch
* added support for smappee water sensors * fixed lint error and wrong location_id * fixed lint error * Use string formatting
* Initialise filter with historical values Added get_last_state_changes() * fix test * Major changes to accommodate history + time_SMA # Conflicts: # homeassistant/components/sensor/filter.py * hail the hound! * lint fixed * less debug * ups * get state from the proper entity * sensible default * No defaults in get_last_state_changes * list_reverseiterator instead of list * prev_state to state * Initialise filter with historical values Added get_last_state_changes() * fix test * Major changes to accommodate history + time_SMA # Conflicts: # homeassistant/components/sensor/filter.py * hail the hound! * lint fixed * less debug * ups * get state from the proper entity * sensible default * No defaults in get_last_state_changes * list_reverseiterator instead of list * prev_state to state * update * added window_unit * replace isinstance with window_unit
* Fixed bug - unable to set base readaonly property * PR fixes * Added line
Map binary_sensor and device_tracker to HomeKit BinarySensor, based on ‘homekit_device_class’ or ‘device_class’ attributes: 'gas': 'CarbonMonoxideSensor' 'co2': 'CarbonDioxideSensor' 'occupancy': 'OccupancySensor' 'opening': 'ContactSensor' 'motion': 'MotionSensor' 'moisture': 'LeakSensor' 'smoke': 'SmokeSensor' Default device_class is 'occupancy'.
1. Remove state param;
2. Sort const;
3. Import ATTR_DEVICE_CLASS from homeassistant.const;
4. Remove device_class_key:
device_class_key = ATTR_HOMEKIT_DEVICE_CLASS \
if (ATTR_HOMEKIT_DEVICE_CLASS in attributes) \
else ATTR_DEVICE_CLASS
And refine BINARY_SENSOR_SERVICE_MAP.
# Conflicts: # homeassistant/components/homekit/type_sensors.py
|
Close this PR since it has more details. I'll re-PR later in a new branch. |
Description:
Support binary_sensor and device_tracker for HomeKit.
Map binary_sensor and device_tracker to HomeKit BinarySensor, based on
‘device_class’ attributes:
If there is no any matched class (e.g. device_tracker), the default sensor type is OccupancySensor.
Checklist:
tox.