diff --git a/homeassistant/components/august/binary_sensor.py b/homeassistant/components/august/binary_sensor.py index bb2bcda39e6..e72d4b186a5 100644 --- a/homeassistant/components/august/binary_sensor.py +++ b/homeassistant/components/august/binary_sensor.py @@ -2,7 +2,7 @@ from datetime import datetime, timedelta import logging -from yalexs.activity import ACTION_DOORBELL_CALL_MISSED, ActivityType +from yalexs.activity import ACTION_DOORBELL_CALL_MISSED, SOURCE_PUBNUB, ActivityType from yalexs.lock import LockDoorStatus from yalexs.util import update_lock_detail_from_activity @@ -97,7 +97,7 @@ SENSOR_TYPES_DOORBELL = { async def async_setup_entry(hass, config_entry, async_add_entities): """Set up the August binary sensors.""" data = hass.data[DOMAIN][config_entry.entry_id][DATA_AUGUST] - devices = [] + entities = [] for door in data.locks: detail = data.get_device_detail(door.device_id) @@ -109,7 +109,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): continue _LOGGER.debug("Adding sensor class door for %s", door.device_name) - devices.append(AugustDoorBinarySensor(data, "door_open", door)) + entities.append(AugustDoorBinarySensor(data, "door_open", door)) for doorbell in data.doorbells: for sensor_type in SENSOR_TYPES_DOORBELL: @@ -118,9 +118,9 @@ async def async_setup_entry(hass, config_entry, async_add_entities): SENSOR_TYPES_DOORBELL[sensor_type][SENSOR_DEVICE_CLASS], doorbell.device_name, ) - devices.append(AugustDoorbellBinarySensor(data, sensor_type, doorbell)) + entities.append(AugustDoorbellBinarySensor(data, sensor_type, doorbell)) - async_add_entities(devices, True) + async_add_entities(entities) class AugustDoorBinarySensor(AugustEntityMixin, BinarySensorEntity): @@ -163,6 +163,9 @@ class AugustDoorBinarySensor(AugustEntityMixin, BinarySensorEntity): if door_activity is not None: update_lock_detail_from_activity(self._detail, door_activity) + # If the source is pubnub the lock must be online since its a live update + if door_activity.source == SOURCE_PUBNUB: + self._detail.set_online(True) bridge_activity = self._data.activity_stream.get_latest_device_activity( self._device_id, {ActivityType.BRIDGE_OPERATION} diff --git a/homeassistant/components/august/camera.py b/homeassistant/components/august/camera.py index e002e0b2517..daaa7624aa3 100644 --- a/homeassistant/components/august/camera.py +++ b/homeassistant/components/august/camera.py @@ -14,23 +14,25 @@ from .entity import AugustEntityMixin async def async_setup_entry(hass, config_entry, async_add_entities): """Set up August cameras.""" data = hass.data[DOMAIN][config_entry.entry_id][DATA_AUGUST] - devices = [] - - for doorbell in data.doorbells: - devices.append(AugustCamera(data, doorbell, DEFAULT_TIMEOUT)) - - async_add_entities(devices, True) + session = aiohttp_client.async_get_clientsession(hass) + async_add_entities( + [ + AugustCamera(data, doorbell, session, DEFAULT_TIMEOUT) + for doorbell in data.doorbells + ] + ) class AugustCamera(AugustEntityMixin, Camera): """An implementation of a August security camera.""" - def __init__(self, data, device, timeout): + def __init__(self, data, device, session, timeout): """Initialize a August security camera.""" super().__init__(data, device) self._data = data self._device = device self._timeout = timeout + self._session = session self._image_url = None self._image_content = None @@ -76,7 +78,7 @@ class AugustCamera(AugustEntityMixin, Camera): if self._image_url is not self._detail.image_url: self._image_url = self._detail.image_url self._image_content = await self._detail.async_get_doorbell_image( - aiohttp_client.async_get_clientsession(self.hass), timeout=self._timeout + self._session, timeout=self._timeout ) return self._image_content diff --git a/homeassistant/components/august/lock.py b/homeassistant/components/august/lock.py index 59c97190d7f..6e4ee7e6f5c 100644 --- a/homeassistant/components/august/lock.py +++ b/homeassistant/components/august/lock.py @@ -1,7 +1,7 @@ """Support for August lock.""" import logging -from yalexs.activity import ActivityType +from yalexs.activity import SOURCE_PUBNUB, ActivityType from yalexs.lock import LockStatus from yalexs.util import update_lock_detail_from_activity @@ -19,13 +19,7 @@ _LOGGER = logging.getLogger(__name__) async def async_setup_entry(hass, config_entry, async_add_entities): """Set up August locks.""" data = hass.data[DOMAIN][config_entry.entry_id][DATA_AUGUST] - devices = [] - - for lock in data.locks: - _LOGGER.debug("Adding lock for %s", lock.device_name) - devices.append(AugustLock(data, lock)) - - async_add_entities(devices, True) + async_add_entities([AugustLock(data, lock) for lock in data.locks]) class AugustLock(AugustEntityMixin, RestoreEntity, LockEntity): @@ -80,6 +74,9 @@ class AugustLock(AugustEntityMixin, RestoreEntity, LockEntity): if lock_activity is not None: self._changed_by = lock_activity.operated_by update_lock_detail_from_activity(self._detail, lock_activity) + # If the source is pubnub the lock must be online since its a live update + if lock_activity.source == SOURCE_PUBNUB: + self._detail.set_online(True) bridge_activity = self._data.activity_stream.get_latest_device_activity( self._device_id, {ActivityType.BRIDGE_OPERATION} diff --git a/homeassistant/components/august/sensor.py b/homeassistant/components/august/sensor.py index 44597a6485e..1d973a83fc3 100644 --- a/homeassistant/components/august/sensor.py +++ b/homeassistant/components/august/sensor.py @@ -45,7 +45,7 @@ SENSOR_TYPES_BATTERY = { 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 = [] + entities = [] migrate_unique_id_devices = [] operation_sensors = [] batteries = { @@ -72,7 +72,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): "Adding battery sensor for %s", device.device_name, ) - devices.append(AugustBatterySensor(data, "device_battery", device, device)) + entities.append(AugustBatterySensor(data, "device_battery", device, device)) for device in batteries["linked_keypad_battery"]: detail = data.get_device_detail(device.device_id) @@ -90,15 +90,15 @@ async def async_setup_entry(hass, config_entry, async_add_entities): keypad_battery_sensor = AugustBatterySensor( data, "linked_keypad_battery", detail.keypad, device ) - devices.append(keypad_battery_sensor) + entities.append(keypad_battery_sensor) migrate_unique_id_devices.append(keypad_battery_sensor) for device in operation_sensors: - devices.append(AugustOperatorSensor(data, device)) + entities.append(AugustOperatorSensor(data, device)) await _async_migrate_old_unique_ids(hass, migrate_unique_id_devices) - async_add_entities(devices, True) + async_add_entities(entities) async def _async_migrate_old_unique_ids(hass, devices): diff --git a/tests/components/august/test_binary_sensor.py b/tests/components/august/test_binary_sensor.py index 0912b05bec1..26c824e5842 100644 --- a/tests/components/august/test_binary_sensor.py +++ b/tests/components/august/test_binary_sensor.py @@ -21,6 +21,7 @@ from tests.components.august.mocks import ( _create_august_with_devices, _mock_activities_from_fixture, _mock_doorbell_from_fixture, + _mock_doorsense_enabled_august_lock_detail, _mock_lock_from_fixture, ) @@ -251,3 +252,97 @@ async def test_doorbell_device_registry(hass): assert reg_device.name == "tmt100 Name" assert reg_device.manufacturer == "August Home Inc." assert reg_device.sw_version == "3.1.0-HYDRC75+201909251139" + + +async def test_door_sense_update_via_pubnub(hass): + """Test creation of a lock with doorsense and bridge.""" + lock_one = await _mock_doorsense_enabled_august_lock_detail(hass) + assert lock_one.pubsub_channel == "pubsub" + pubnub = AugustPubNub() + + activities = await _mock_activities_from_fixture(hass, "get_activity.lock.json") + config_entry = await _create_august_with_devices( + hass, [lock_one], activities=activities, pubnub=pubnub + ) + + binary_sensor_online_with_doorsense_name = hass.states.get( + "binary_sensor.online_with_doorsense_name_open" + ) + assert binary_sensor_online_with_doorsense_name.state == STATE_ON + + pubnub.message( + pubnub, + Mock( + channel=lock_one.pubsub_channel, + timetoken=dt_util.utcnow().timestamp() * 10000000, + message={"status": "kAugLockState_Unlocking", "doorState": "closed"}, + ), + ) + + await hass.async_block_till_done() + binary_sensor_online_with_doorsense_name = hass.states.get( + "binary_sensor.online_with_doorsense_name_open" + ) + assert binary_sensor_online_with_doorsense_name.state == STATE_OFF + + pubnub.message( + pubnub, + Mock( + channel=lock_one.pubsub_channel, + timetoken=dt_util.utcnow().timestamp() * 10000000, + message={"status": "kAugLockState_Locking", "doorState": "open"}, + ), + ) + + await hass.async_block_till_done() + binary_sensor_online_with_doorsense_name = hass.states.get( + "binary_sensor.online_with_doorsense_name_open" + ) + assert binary_sensor_online_with_doorsense_name.state == STATE_ON + + async_fire_time_changed(hass, dt_util.utcnow() + datetime.timedelta(seconds=30)) + await hass.async_block_till_done() + binary_sensor_online_with_doorsense_name = hass.states.get( + "binary_sensor.online_with_doorsense_name_open" + ) + assert binary_sensor_online_with_doorsense_name.state == STATE_ON + + pubnub.connected = True + async_fire_time_changed(hass, dt_util.utcnow() + datetime.timedelta(seconds=30)) + await hass.async_block_till_done() + binary_sensor_online_with_doorsense_name = hass.states.get( + "binary_sensor.online_with_doorsense_name_open" + ) + assert binary_sensor_online_with_doorsense_name.state == STATE_ON + + # Ensure pubnub status is always preserved + async_fire_time_changed(hass, dt_util.utcnow() + datetime.timedelta(hours=2)) + await hass.async_block_till_done() + binary_sensor_online_with_doorsense_name = hass.states.get( + "binary_sensor.online_with_doorsense_name_open" + ) + assert binary_sensor_online_with_doorsense_name.state == STATE_ON + + pubnub.message( + pubnub, + Mock( + channel=lock_one.pubsub_channel, + timetoken=dt_util.utcnow().timestamp() * 10000000, + message={"status": "kAugLockState_Unlocking", "doorState": "open"}, + ), + ) + await hass.async_block_till_done() + binary_sensor_online_with_doorsense_name = hass.states.get( + "binary_sensor.online_with_doorsense_name_open" + ) + assert binary_sensor_online_with_doorsense_name.state == STATE_ON + + async_fire_time_changed(hass, dt_util.utcnow() + datetime.timedelta(hours=4)) + await hass.async_block_till_done() + binary_sensor_online_with_doorsense_name = hass.states.get( + "binary_sensor.online_with_doorsense_name_open" + ) + assert binary_sensor_online_with_doorsense_name.state == STATE_ON + + await hass.config_entries.async_unload(config_entry.entry_id) + await hass.async_block_till_done()