Skip to content

Commit

Permalink
Bump vivintpy to 2024.1.0, use typed ConfigEntry, remove pickle file
Browse files Browse the repository at this point in the history
  • Loading branch information
natekspencer committed Oct 7, 2024
1 parent 17dfc6c commit c245522
Show file tree
Hide file tree
Showing 20 changed files with 134 additions and 120 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
![Release](https://img.shields.io/github/v/release/natekspencer/hacs-vivint?style=for-the-badge)
![Downloads](https://img.shields.io/github/downloads/natekspencer/hacs-vivint/total?style=for-the-badge)
![Latest Downloads](https://img.shields.io/github/downloads/natekspencer/hacs-vivint/latest/total?style=for-the-badge)
[![Buy Me A Coffee/Beer](https://img.shields.io/badge/Buy_Me_A_☕/🍺-F16061?style=for-the-badge&logo=ko-fi&logoColor=white&labelColor=grey)](https://ko-fi.com/natekspencer)

<picture>
Expand Down
70 changes: 54 additions & 16 deletions custom_components/vivint/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""The Vivint integration."""

import logging
import os

from aiohttp import ClientResponseError
from aiohttp.client_exceptions import ClientConnectorError
Expand All @@ -15,15 +17,23 @@
)

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_DEVICE_ID, ATTR_DOMAIN, Platform
from homeassistant.core import HomeAssistant, callback
from homeassistant.const import (
ATTR_DEVICE_ID,
ATTR_DOMAIN,
EVENT_HOMEASSISTANT_STOP,
Platform,
)
from homeassistant.core import Event, HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers import device_registry
from homeassistant.helpers.dispatcher import async_dispatcher_send

from .const import DOMAIN, EVENT_TYPE
from .const import CONF_REFRESH_TOKEN, DOMAIN, EVENT_TYPE
from .hub import VivintHub, get_device_id

type VivintConfigEntry = ConfigEntry[VivintHub]


_LOGGER = logging.getLogger(__name__)

PLATFORMS = [
Expand All @@ -43,12 +53,12 @@
ATTR_TYPE = "type"


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def async_setup_entry(hass: HomeAssistant, entry: VivintConfigEntry) -> bool:
"""Set up Vivint from a config entry."""
undo_listener = entry.add_update_listener(update_listener)

hub = VivintHub(hass, entry.data, undo_listener)
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = hub
entry.runtime_data = hub

try:
await hub.login(load_devices=True, subscribe_for_realtime_updates=True)
Expand Down Expand Up @@ -144,26 +154,54 @@ def async_on_device_event(event_type: str, viv_device: VivintDevice) -> None:
if device not in known_devices:
dev_reg.async_remove_device(device.id)

@callback
def _async_save_tokens(ev: Event) -> None:
"""Save tokens to the config entry data."""
undo_listener()
hass.config_entries.async_update_entry(
entry, data=entry.data | {CONF_REFRESH_TOKEN: hub.account.refresh_token}
)

hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _async_save_tokens)

return True


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def async_unload_entry(hass: HomeAssistant, entry: VivintConfigEntry) -> bool:
"""Unload config entry."""
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
await entry.runtime_data.disconnect()
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)


async def async_migrate_entry(hass: HomeAssistant, entry: VivintConfigEntry) -> bool:
"""Migrate old entry."""
_LOGGER.debug(
"Migrating configuration from version %s.%s", entry.version, entry.minor_version
)

hub: VivintHub = hass.data[DOMAIN][entry.entry_id]
await hub.disconnect()
if entry.version > 1:
# This means the user has downgraded from a future version
return False

return unload_ok
if entry.version == 1:
if entry.minor_version < 2:
for filename in (".vivintpy_cache.pickle", ".vivintpy_cache_1.pickle"):
try:
os.remove(hass.config.path(filename))
except Exception:
pass

hass.config_entries.async_update_entry(entry, minor_version=2)

async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Handle removal of an entry."""
hub: VivintHub = hass.data[DOMAIN][entry.entry_id]
await hub.disconnect(remove_cache=True)
hass.data[DOMAIN].pop(entry.entry_id)
_LOGGER.debug(
"Migration to version %s.%s successful",
entry.version,
entry.minor_version,
)

return True


async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
async def update_listener(hass: HomeAssistant, entry: VivintConfigEntry) -> None:
"""Handle options update."""
await hass.config_entries.async_reload(entry.entry_id)
17 changes: 7 additions & 10 deletions custom_components/vivint/alarm_control_panel.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Support for Vivint alarm control panel."""

from __future__ import annotations

from vivintpy.devices.alarm_panel import AlarmPanel
Expand All @@ -9,7 +10,6 @@
AlarmControlPanelEntityFeature as Feature,
CodeFormat,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
STATE_ALARM_ARMED_AWAY,
STATE_ALARM_ARMED_HOME,
Expand All @@ -22,7 +22,8 @@
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType

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

ARMED_STATE_MAP = {
Expand All @@ -42,13 +43,13 @@

async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
entry: VivintConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up Vivint alarm control panel using config entry."""
entities = []
hub: VivintHub = hass.data[DOMAIN][config_entry.entry_id]
disarm_code = config_entry.options.get(CONF_DISARM_CODE)
hub: VivintHub = entry.runtime_data
disarm_code = entry.options.get(CONF_DISARM_CODE)

for system in hub.account.systems:
for device in system.alarm_panels:
Expand Down Expand Up @@ -78,15 +79,11 @@ def __init__(
) -> None:
"""Create the entity."""
super().__init__(device, hub)
self._attr_unique_id = str(self.device.id)
if disarm_code:
self._attr_code_format = CodeFormat.NUMBER
self._disarm_code = disarm_code

@property
def unique_id(self) -> str:
"""Return a unique ID."""
return self.device.id

@property
def state(self) -> StateType:
"""Return the state of the alarm control panel."""
Expand Down
11 changes: 6 additions & 5 deletions custom_components/vivint/binary_sensor.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Support for Vivint binary sensors."""

from __future__ import annotations

from collections.abc import Callable
Expand All @@ -16,14 +17,14 @@
BinarySensorEntity,
BinarySensorEntityDescription,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import EntityCategory
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.event import async_call_later
from homeassistant.util.dt import utcnow

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

Expand All @@ -36,12 +37,12 @@

async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
entry: VivintConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up Vivint binary sensors using config entry."""
entities = []
hub: VivintHub = hass.data[DOMAIN][config_entry.entry_id]
hub: VivintHub = entry.runtime_data

for system in hub.account.systems:
for alarm_panel in system.alarm_panels:
Expand Down Expand Up @@ -87,10 +88,10 @@ def async_add_sensor(device: VivintDevice) -> None:

async_add_entities(entities)

config_entry.async_on_unload(
entry.async_on_unload(
async_dispatcher_connect(
hass,
f"{DOMAIN}_{config_entry.entry_id}_add_{PLATFORM_DOMAIN}",
f"{DOMAIN}_{entry.entry_id}_add_{PLATFORM_DOMAIN}",
async_add_sensor,
)
)
Expand Down
7 changes: 3 additions & 4 deletions custom_components/vivint/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,10 @@
ButtonEntity,
ButtonEntityDescription,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from .const import DOMAIN
from . import VivintConfigEntry
from .hub import VivintBaseEntity, VivintHub, has_capability

REBOOT_ENTITY = ButtonEntityDescription(
Expand All @@ -29,11 +28,11 @@

async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
entry: VivintConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up Vivint button platform."""
hub: VivintHub = hass.data[DOMAIN][entry.entry_id]
hub: VivintHub = entry.runtime_data
entities = [
VivintButtonEntity(
device=alarm_panel, hub=hub, entity_description=REBOOT_ENTITY
Expand Down
13 changes: 6 additions & 7 deletions custom_components/vivint/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,18 @@

from homeassistant.components.camera import Camera, CameraEntityFeature
from homeassistant.components.ffmpeg import async_get_image
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, format_mac
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from . import VivintConfigEntry
from .const import (
CONF_HD_STREAM,
CONF_RTSP_STREAM,
CONF_RTSP_URL_LOGGING,
DEFAULT_HD_STREAM,
DEFAULT_RTSP_STREAM,
DEFAULT_RTSP_URL_LOGGING,
DOMAIN,
RTSP_STREAM_DIRECT,
RTSP_STREAM_INTERNAL,
)
Expand All @@ -31,16 +30,16 @@

async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
entry: VivintConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up Vivint cameras using config entry."""
entities = []
hub: VivintHub = hass.data[DOMAIN][config_entry.entry_id]
hub: VivintHub = entry.runtime_data

hd_stream = config_entry.options.get(CONF_HD_STREAM, DEFAULT_HD_STREAM)
rtsp_stream = config_entry.options.get(CONF_RTSP_STREAM, DEFAULT_RTSP_STREAM)
rtsp_url_logging = config_entry.options.get(
hd_stream = entry.options.get(CONF_HD_STREAM, DEFAULT_HD_STREAM)
rtsp_stream = entry.options.get(CONF_RTSP_STREAM, DEFAULT_RTSP_STREAM)
rtsp_url_logging = entry.options.get(
CONF_RTSP_URL_LOGGING, DEFAULT_RTSP_URL_LOGGING
)

Expand Down
8 changes: 4 additions & 4 deletions custom_components/vivint/climate.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Support for Vivint thermostats."""

from __future__ import annotations

from typing import Any
Expand All @@ -23,12 +24,11 @@
HVACAction,
HVACMode,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback

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

# Map Vivint HVAC Mode to Home Assistant value
Expand Down Expand Up @@ -93,12 +93,12 @@

async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
entry: VivintConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up Vivint climate using config entry."""
entities = []
hub: VivintHub = hass.data[DOMAIN][config_entry.entry_id]
hub: VivintHub = entry.runtime_data

for system in hub.account.systems:
for alarm_panel in system.alarm_panels:
Expand Down
10 changes: 7 additions & 3 deletions custom_components/vivint/config_flow.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Config flow for Vivint integration."""

from __future__ import annotations

import logging
Expand Down Expand Up @@ -29,6 +30,7 @@
CONF_DISARM_CODE,
CONF_HD_STREAM,
CONF_MFA,
CONF_REFRESH_TOKEN,
CONF_RTSP_STREAM,
CONF_RTSP_URL_LOGGING,
DEFAULT_HD_STREAM,
Expand Down Expand Up @@ -77,6 +79,7 @@ class VivintConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Vivint."""

VERSION = 1
MINOR_VERSION = 2

def __init__(self) -> None:
"""Initialize a config flow."""
Expand All @@ -90,6 +93,7 @@ async def _async_create_entry(self) -> FlowResult:
config_data = {
CONF_USERNAME: self._hub._data[CONF_USERNAME],
CONF_PASSWORD: self._hub._data[CONF_PASSWORD],
CONF_REFRESH_TOKEN: self._hub.account.refresh_token,
}

await self._hub.disconnect()
Expand All @@ -112,7 +116,7 @@ async def async_vivint_login(

self._hub = VivintHub(self.hass, user_input)
try:
await self._hub.login(load_devices=True, use_cache=False)
await self._hub.login(load_devices=True)
except VivintSkyApiMfaRequiredError:
return await self.async_step_mfa()
except VivintSkyApiAuthenticationError:
Expand Down Expand Up @@ -197,6 +201,6 @@ async def async_step_reauth_confirm(

@staticmethod
@callback
def async_get_options_flow(config_entry: ConfigEntry) -> SchemaOptionsFlowHandler:
def async_get_options_flow(entry: ConfigEntry) -> SchemaOptionsFlowHandler:
"""Get the options flow for this handler."""
return SchemaOptionsFlowHandler(config_entry, OPTIONS_FLOW)
return SchemaOptionsFlowHandler(entry, OPTIONS_FLOW)
2 changes: 2 additions & 0 deletions custom_components/vivint/const.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Constants for the Vivint integration."""

DOMAIN = "vivint"
EVENT_TYPE = f"{DOMAIN}_event"

Expand All @@ -12,6 +13,7 @@
}

CONF_MFA = "code"
CONF_REFRESH_TOKEN = "refresh_token"
CONF_DISARM_CODE = "disarm_code"
CONF_HD_STREAM = "hd_stream"
CONF_RTSP_STREAM = "rtsp_stream"
Expand Down
Loading

0 comments on commit c245522

Please sign in to comment.