Separate august keypads into their own device (#33665)

The keypad has its own unique id so its better
represented as its own device.  This fixes
showing the keypad battery state for the lock
in the UI.
This commit is contained in:
J. Nick Koston 2020-04-08 14:57:27 -05:00 committed by GitHub
parent b09b5729a3
commit 7dd42bc32d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 79 additions and 61 deletions

View File

@ -262,6 +262,13 @@ class AugustData(AugustSubscriberMixin):
await self._async_update_device_detail(
self._locks_by_id[device_id], self._api.async_get_lock_detail
)
# keypads are always attached to locks
if (
device_id in self._device_detail_by_id
and self._device_detail_by_id[device_id].keypad is not None
):
keypad = self._device_detail_by_id[device_id].keypad
self._device_detail_by_id[keypad.device_id] = keypad
elif device_id in self._doorbells_by_id:
await self._async_update_device_detail(
self._doorbells_by_id[device_id],

View File

@ -7,6 +7,7 @@ from homeassistant.components.sensor import DEVICE_CLASS_BATTERY
from homeassistant.const import ATTR_ENTITY_PICTURE, UNIT_PERCENTAGE
from homeassistant.core import callback
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_registry import async_get_registry
from homeassistant.helpers.restore_state import RestoreEntity
from .const import (
@ -33,21 +34,12 @@ def _retrieve_device_battery_state(detail):
def _retrieve_linked_keypad_battery_state(detail):
"""Get the latest state of the sensor."""
if detail.keypad is None:
return None
return detail.keypad.battery_percentage
return detail.battery_percentage
SENSOR_TYPES_BATTERY = {
"device_battery": {
"name": "Battery",
"state_provider": _retrieve_device_battery_state,
},
"linked_keypad_battery": {
"name": "Keypad Battery",
"state_provider": _retrieve_linked_keypad_battery_state,
},
"device_battery": {"state_provider": _retrieve_device_battery_state},
"linked_keypad_battery": {"state_provider": _retrieve_linked_keypad_battery_state},
}
@ -55,7 +47,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up the August sensors."""
data = hass.data[DOMAIN][config_entry.entry_id][DATA_AUGUST]
devices = []
migrate_unique_id_devices = []
operation_sensors = []
batteries = {
"device_battery": [],
@ -68,30 +60,62 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
batteries["linked_keypad_battery"].append(device)
operation_sensors.append(device)
for sensor_type in SENSOR_TYPES_BATTERY:
for device in batteries[sensor_type]:
state_provider = SENSOR_TYPES_BATTERY[sensor_type]["state_provider"]
detail = data.get_device_detail(device.device_id)
state = state_provider(detail)
sensor_name = SENSOR_TYPES_BATTERY[sensor_type]["name"]
if state is None:
_LOGGER.debug(
"Not adding battery sensor %s for %s because it is not present",
sensor_name,
device.device_name,
)
else:
_LOGGER.debug(
"Adding battery sensor %s for %s", sensor_name, device.device_name,
)
devices.append(AugustBatterySensor(data, sensor_type, device))
for device in batteries["device_battery"]:
state_provider = SENSOR_TYPES_BATTERY["device_battery"]["state_provider"]
detail = data.get_device_detail(device.device_id)
if detail is None or state_provider(detail) is None:
_LOGGER.debug(
"Not adding battery sensor for %s because it is not present",
device.device_name,
)
continue
_LOGGER.debug(
"Adding battery sensor for %s", device.device_name,
)
devices.append(AugustBatterySensor(data, "device_battery", device, device))
for device in batteries["linked_keypad_battery"]:
detail = data.get_device_detail(device.device_id)
if detail.keypad is None:
_LOGGER.debug(
"Not adding keypad battery sensor for %s because it is not present",
device.device_name,
)
continue
_LOGGER.debug(
"Adding keypad battery sensor for %s", device.device_name,
)
keypad_battery_sensor = AugustBatterySensor(
data, "linked_keypad_battery", detail.keypad, device
)
devices.append(keypad_battery_sensor)
migrate_unique_id_devices.append(keypad_battery_sensor)
for device in operation_sensors:
devices.append(AugustOperatorSensor(data, device))
await _async_migrate_old_unique_ids(hass, migrate_unique_id_devices)
async_add_entities(devices, True)
async def _async_migrate_old_unique_ids(hass, devices):
"""Keypads now have their own serial number."""
registry = await async_get_registry(hass)
for device in devices:
old_entity_id = registry.async_get_entity_id(
"sensor", DOMAIN, device.old_unique_id
)
if old_entity_id is not None:
_LOGGER.debug(
"Migrating unique_id from [%s] to [%s]",
device.old_unique_id,
device.unique_id,
)
registry.async_update_entity(old_entity_id, new_unique_id=device.unique_id)
class AugustOperatorSensor(AugustEntityMixin, RestoreEntity, Entity):
"""Representation of an August lock operation sensor."""
@ -194,12 +218,13 @@ class AugustOperatorSensor(AugustEntityMixin, RestoreEntity, Entity):
class AugustBatterySensor(AugustEntityMixin, Entity):
"""Representation of an August sensor."""
def __init__(self, data, sensor_type, device):
def __init__(self, data, sensor_type, device, old_device):
"""Initialize the sensor."""
super().__init__(data, device)
self._data = data
self._sensor_type = sensor_type
self._device = device
self._old_device = old_device
self._state = None
self._available = False
self._update_from_data()
@ -228,8 +253,7 @@ class AugustBatterySensor(AugustEntityMixin, Entity):
def name(self):
"""Return the name of the sensor."""
device_name = self._device.device_name
sensor_name = SENSOR_TYPES_BATTERY[self._sensor_type]["name"]
return f"{device_name} {sensor_name}"
return f"{device_name} Battery"
@callback
def _update_from_data(self):
@ -242,3 +266,8 @@ class AugustBatterySensor(AugustEntityMixin, Entity):
def unique_id(self) -> str:
"""Get the unique id of the device sensor."""
return f"{self._device_id}_{self._sensor_type}"
@property
def old_unique_id(self) -> str:
"""Get the old unique id of the device sensor."""
return f"{self._old_device.device_id}_{self._sensor_type}"

View File

@ -71,21 +71,12 @@ async def test_create_lock_with_linked_keypad(hass):
assert entry
assert entry.unique_id == "A6697750D607098BAE8D6BAA11EF8063_device_battery"
sensor_a6697750d607098bae8d6baa11ef8063_name_keypad_battery = hass.states.get(
"sensor.a6697750d607098bae8d6baa11ef8063_name_keypad_battery"
)
assert sensor_a6697750d607098bae8d6baa11ef8063_name_keypad_battery.state == "60"
assert (
sensor_a6697750d607098bae8d6baa11ef8063_name_keypad_battery.attributes[
"unit_of_measurement"
]
== "%"
)
entry = entity_registry.async_get(
"sensor.a6697750d607098bae8d6baa11ef8063_name_keypad_battery"
)
state = hass.states.get("sensor.front_door_lock_keypad_battery")
assert state.state == "60"
assert state.attributes["unit_of_measurement"] == "%"
entry = entity_registry.async_get("sensor.front_door_lock_keypad_battery")
assert entry
assert entry.unique_id == "A6697750D607098BAE8D6BAA11EF8063_linked_keypad_battery"
assert entry.unique_id == "5bc65c24e6ef2a263e1450a8_linked_keypad_battery"
async def test_create_lock_with_low_battery_linked_keypad(hass):
@ -110,21 +101,12 @@ async def test_create_lock_with_low_battery_linked_keypad(hass):
assert entry
assert entry.unique_id == "A6697750D607098BAE8D6BAA11EF8063_device_battery"
sensor_a6697750d607098bae8d6baa11ef8063_name_keypad_battery = hass.states.get(
"sensor.a6697750d607098bae8d6baa11ef8063_name_keypad_battery"
)
assert sensor_a6697750d607098bae8d6baa11ef8063_name_keypad_battery.state == "10"
assert (
sensor_a6697750d607098bae8d6baa11ef8063_name_keypad_battery.attributes[
"unit_of_measurement"
]
== "%"
)
entry = entity_registry.async_get(
"sensor.a6697750d607098bae8d6baa11ef8063_name_keypad_battery"
)
state = hass.states.get("sensor.front_door_lock_keypad_battery")
assert state.state == "10"
assert state.attributes["unit_of_measurement"] == "%"
entry = entity_registry.async_get("sensor.front_door_lock_keypad_battery")
assert entry
assert entry.unique_id == "A6697750D607098BAE8D6BAA11EF8063_linked_keypad_battery"
assert entry.unique_id == "5bc65c24e6ef2a263e1450a8_linked_keypad_battery"
# No activity means it will be unavailable until someone unlocks/locks it
lock_operator_sensor = entity_registry.async_get(