Skip to content

Commit

Permalink
Handle race condition and alarm panel unique_id change
Browse files Browse the repository at this point in the history
  • Loading branch information
natekspencer committed Oct 8, 2024
1 parent b3ddce3 commit 4f2b75f
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 11 deletions.
4 changes: 2 additions & 2 deletions custom_components/vivint/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,9 @@ def async_on_device_event(event_type: str, viv_device: VivintDevice) -> None:
dev_reg.async_remove_device(device.id)

@callback
def _async_save_tokens(ev: Event) -> None:
async def _async_save_tokens(ev: Event) -> None:
"""Save tokens to the config entry data."""
undo_listener()
await entry.runtime_data.disconnect()
hass.config_entries.async_update_entry(
entry, data=entry.data | {CONF_REFRESH_TOKEN: hub.account.refresh_token}
)
Expand Down
25 changes: 24 additions & 1 deletion custom_components/vivint/alarm_control_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@

from __future__ import annotations

from typing import Iterable

from vivintpy.devices.alarm_panel import AlarmPanel
from vivintpy.enums import ArmedState

from homeassistant.components.alarm_control_panel import (
DOMAIN as PLATFORM,
AlarmControlPanelEntity,
AlarmControlPanelEntityFeature as Feature,
CodeFormat,
Expand All @@ -19,11 +22,12 @@
STATE_ALARM_TRIGGERED,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType

from . import VivintConfigEntry
from .const import CONF_DISARM_CODE
from .const import CONF_DISARM_CODE, DOMAIN
from .hub import VivintEntity, VivintHub

ARMED_STATE_MAP = {
Expand Down Expand Up @@ -62,6 +66,9 @@ async def async_setup_entry(
if not entities:
return

# Migrate unique ids
async_update_unique_id(hass, PLATFORM, entities)

async_add_entities(entities)


Expand Down Expand Up @@ -105,3 +112,19 @@ async def async_alarm_arm_away(self, code: str | None = None) -> None:
async def async_alarm_trigger(self, code: str | None = None) -> None:
"""Send alarm trigger command."""
await self.device.trigger_alarm()


# to be removed 2025-01
def async_update_unique_id(
hass: HomeAssistant, domain: str, entities: Iterable[VivintAlarmControlPanelEntity]
) -> None:
"""Update unique ID to be based on VIN and entity description key instead of name."""
ent_reg = er.async_get(hass)
for entity in entities:
old_unique_id = int(entity.unique_id)
if entity_id := ent_reg.async_get_entity_id(domain, DOMAIN, old_unique_id):
if existing_entity_id := ent_reg.async_get_entity_id(
domain, DOMAIN, entity.unique_id
):
ent_reg.async_remove(existing_entity_id)
ent_reg.async_update_entity(entity_id, new_unique_id=entity.unique_id)
19 changes: 11 additions & 8 deletions custom_components/vivint/hub.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from __future__ import annotations

import asyncio
from collections.abc import Callable
from datetime import timedelta
import logging
Expand Down Expand Up @@ -72,6 +73,7 @@ def __init__(
self.account: Account = None
self.logged_in = False
self.session = ClientSession()
self._lock = asyncio.Lock()

async def _async_update_data() -> None:
"""Update all device states from the Vivint API."""
Expand Down Expand Up @@ -113,14 +115,15 @@ async def login(
raise ex

async def disconnect(self) -> None:
"""Disconnect from Vivint, close the session and optionally remove cache."""
if self.account.connected:
await self.account.disconnect()
if not self.session.closed:
await self.session.close()
if self.__undo_listener:
self.__undo_listener()
self.__undo_listener = None
"""Disconnect from Vivint, close the session and stop listener."""
async with self._lock:
if self.account.connected:
await self.account.disconnect()
if not self.session.closed:
await self.session.close()
if self.__undo_listener:
self.__undo_listener()
self.__undo_listener = None

async def verify_mfa(self, code: str) -> bool:
"""Verify MFA."""
Expand Down

0 comments on commit 4f2b75f

Please sign in to comment.