Skip to content

Commit b3ddce3

Browse files
authored
Merge pull request #144 from natekspencer/dev
Bump vivintpy to 2024.1.0, use typed ConfigEntry, remove pickle file
2 parents 17dfc6c + c245522 commit b3ddce3

20 files changed

+134
-120
lines changed

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
![Release](https://img.shields.io/github/v/release/natekspencer/hacs-vivint?style=for-the-badge)
2+
![Downloads](https://img.shields.io/github/downloads/natekspencer/hacs-vivint/total?style=for-the-badge)
3+
![Latest Downloads](https://img.shields.io/github/downloads/natekspencer/hacs-vivint/latest/total?style=for-the-badge)
24
[![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)
35

46
<picture>

custom_components/vivint/__init__.py

+54-16
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""The Vivint integration."""
2+
23
import logging
4+
import os
35

46
from aiohttp import ClientResponseError
57
from aiohttp.client_exceptions import ClientConnectorError
@@ -15,15 +17,23 @@
1517
)
1618

1719
from homeassistant.config_entries import ConfigEntry
18-
from homeassistant.const import ATTR_DEVICE_ID, ATTR_DOMAIN, Platform
19-
from homeassistant.core import HomeAssistant, callback
20+
from homeassistant.const import (
21+
ATTR_DEVICE_ID,
22+
ATTR_DOMAIN,
23+
EVENT_HOMEASSISTANT_STOP,
24+
Platform,
25+
)
26+
from homeassistant.core import Event, HomeAssistant, callback
2027
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
2128
from homeassistant.helpers import device_registry
2229
from homeassistant.helpers.dispatcher import async_dispatcher_send
2330

24-
from .const import DOMAIN, EVENT_TYPE
31+
from .const import CONF_REFRESH_TOKEN, DOMAIN, EVENT_TYPE
2532
from .hub import VivintHub, get_device_id
2633

34+
type VivintConfigEntry = ConfigEntry[VivintHub]
35+
36+
2737
_LOGGER = logging.getLogger(__name__)
2838

2939
PLATFORMS = [
@@ -43,12 +53,12 @@
4353
ATTR_TYPE = "type"
4454

4555

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

5060
hub = VivintHub(hass, entry.data, undo_listener)
51-
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = hub
61+
entry.runtime_data = hub
5262

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

157+
@callback
158+
def _async_save_tokens(ev: Event) -> None:
159+
"""Save tokens to the config entry data."""
160+
undo_listener()
161+
hass.config_entries.async_update_entry(
162+
entry, data=entry.data | {CONF_REFRESH_TOKEN: hub.account.refresh_token}
163+
)
164+
165+
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _async_save_tokens)
166+
147167
return True
148168

149169

150-
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
170+
async def async_unload_entry(hass: HomeAssistant, entry: VivintConfigEntry) -> bool:
151171
"""Unload config entry."""
152-
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
172+
await entry.runtime_data.disconnect()
173+
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
174+
175+
176+
async def async_migrate_entry(hass: HomeAssistant, entry: VivintConfigEntry) -> bool:
177+
"""Migrate old entry."""
178+
_LOGGER.debug(
179+
"Migrating configuration from version %s.%s", entry.version, entry.minor_version
180+
)
153181

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

157-
return unload_ok
186+
if entry.version == 1:
187+
if entry.minor_version < 2:
188+
for filename in (".vivintpy_cache.pickle", ".vivintpy_cache_1.pickle"):
189+
try:
190+
os.remove(hass.config.path(filename))
191+
except Exception:
192+
pass
158193

194+
hass.config_entries.async_update_entry(entry, minor_version=2)
159195

160-
async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
161-
"""Handle removal of an entry."""
162-
hub: VivintHub = hass.data[DOMAIN][entry.entry_id]
163-
await hub.disconnect(remove_cache=True)
164-
hass.data[DOMAIN].pop(entry.entry_id)
196+
_LOGGER.debug(
197+
"Migration to version %s.%s successful",
198+
entry.version,
199+
entry.minor_version,
200+
)
201+
202+
return True
165203

166204

167-
async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
205+
async def update_listener(hass: HomeAssistant, entry: VivintConfigEntry) -> None:
168206
"""Handle options update."""
169207
await hass.config_entries.async_reload(entry.entry_id)

custom_components/vivint/alarm_control_panel.py

+7-10
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Support for Vivint alarm control panel."""
2+
23
from __future__ import annotations
34

45
from vivintpy.devices.alarm_panel import AlarmPanel
@@ -9,7 +10,6 @@
910
AlarmControlPanelEntityFeature as Feature,
1011
CodeFormat,
1112
)
12-
from homeassistant.config_entries import ConfigEntry
1313
from homeassistant.const import (
1414
STATE_ALARM_ARMED_AWAY,
1515
STATE_ALARM_ARMED_HOME,
@@ -22,7 +22,8 @@
2222
from homeassistant.helpers.entity_platform import AddEntitiesCallback
2323
from homeassistant.helpers.typing import StateType
2424

25-
from .const import CONF_DISARM_CODE, DOMAIN
25+
from . import VivintConfigEntry
26+
from .const import CONF_DISARM_CODE
2627
from .hub import VivintEntity, VivintHub
2728

2829
ARMED_STATE_MAP = {
@@ -42,13 +43,13 @@
4243

4344
async def async_setup_entry(
4445
hass: HomeAssistant,
45-
config_entry: ConfigEntry,
46+
entry: VivintConfigEntry,
4647
async_add_entities: AddEntitiesCallback,
4748
) -> None:
4849
"""Set up Vivint alarm control panel using config entry."""
4950
entities = []
50-
hub: VivintHub = hass.data[DOMAIN][config_entry.entry_id]
51-
disarm_code = config_entry.options.get(CONF_DISARM_CODE)
51+
hub: VivintHub = entry.runtime_data
52+
disarm_code = entry.options.get(CONF_DISARM_CODE)
5253

5354
for system in hub.account.systems:
5455
for device in system.alarm_panels:
@@ -78,15 +79,11 @@ def __init__(
7879
) -> None:
7980
"""Create the entity."""
8081
super().__init__(device, hub)
82+
self._attr_unique_id = str(self.device.id)
8183
if disarm_code:
8284
self._attr_code_format = CodeFormat.NUMBER
8385
self._disarm_code = disarm_code
8486

85-
@property
86-
def unique_id(self) -> str:
87-
"""Return a unique ID."""
88-
return self.device.id
89-
9087
@property
9188
def state(self) -> StateType:
9289
"""Return the state of the alarm control panel."""

custom_components/vivint/binary_sensor.py

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Support for Vivint binary sensors."""
2+
23
from __future__ import annotations
34

45
from collections.abc import Callable
@@ -16,14 +17,14 @@
1617
BinarySensorEntity,
1718
BinarySensorEntityDescription,
1819
)
19-
from homeassistant.config_entries import ConfigEntry
2020
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
2121
from homeassistant.helpers.dispatcher import async_dispatcher_connect
2222
from homeassistant.helpers.entity import EntityCategory
2323
from homeassistant.helpers.entity_platform import AddEntitiesCallback
2424
from homeassistant.helpers.event import async_call_later
2525
from homeassistant.util.dt import utcnow
2626

27+
from . import VivintConfigEntry
2728
from .const import DOMAIN
2829
from .hub import VivintBaseEntity, VivintEntity, VivintHub
2930

@@ -36,12 +37,12 @@
3637

3738
async def async_setup_entry(
3839
hass: HomeAssistant,
39-
config_entry: ConfigEntry,
40+
entry: VivintConfigEntry,
4041
async_add_entities: AddEntitiesCallback,
4142
) -> None:
4243
"""Set up Vivint binary sensors using config entry."""
4344
entities = []
44-
hub: VivintHub = hass.data[DOMAIN][config_entry.entry_id]
45+
hub: VivintHub = entry.runtime_data
4546

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

8889
async_add_entities(entities)
8990

90-
config_entry.async_on_unload(
91+
entry.async_on_unload(
9192
async_dispatcher_connect(
9293
hass,
93-
f"{DOMAIN}_{config_entry.entry_id}_add_{PLATFORM_DOMAIN}",
94+
f"{DOMAIN}_{entry.entry_id}_add_{PLATFORM_DOMAIN}",
9495
async_add_sensor,
9596
)
9697
)

custom_components/vivint/button.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,10 @@
1515
ButtonEntity,
1616
ButtonEntityDescription,
1717
)
18-
from homeassistant.config_entries import ConfigEntry
1918
from homeassistant.core import HomeAssistant
2019
from homeassistant.helpers.entity_platform import AddEntitiesCallback
2120

22-
from .const import DOMAIN
21+
from . import VivintConfigEntry
2322
from .hub import VivintBaseEntity, VivintHub, has_capability
2423

2524
REBOOT_ENTITY = ButtonEntityDescription(
@@ -29,11 +28,11 @@
2928

3029
async def async_setup_entry(
3130
hass: HomeAssistant,
32-
entry: ConfigEntry,
31+
entry: VivintConfigEntry,
3332
async_add_entities: AddEntitiesCallback,
3433
) -> None:
3534
"""Set up Vivint button platform."""
36-
hub: VivintHub = hass.data[DOMAIN][entry.entry_id]
35+
hub: VivintHub = entry.runtime_data
3736
entities = [
3837
VivintButtonEntity(
3938
device=alarm_panel, hub=hub, entity_description=REBOOT_ENTITY

custom_components/vivint/camera.py

+6-7
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,18 @@
88

99
from homeassistant.components.camera import Camera, CameraEntityFeature
1010
from homeassistant.components.ffmpeg import async_get_image
11-
from homeassistant.config_entries import ConfigEntry
1211
from homeassistant.core import HomeAssistant
1312
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, format_mac
1413
from homeassistant.helpers.entity_platform import AddEntitiesCallback
1514

15+
from . import VivintConfigEntry
1616
from .const import (
1717
CONF_HD_STREAM,
1818
CONF_RTSP_STREAM,
1919
CONF_RTSP_URL_LOGGING,
2020
DEFAULT_HD_STREAM,
2121
DEFAULT_RTSP_STREAM,
2222
DEFAULT_RTSP_URL_LOGGING,
23-
DOMAIN,
2423
RTSP_STREAM_DIRECT,
2524
RTSP_STREAM_INTERNAL,
2625
)
@@ -31,16 +30,16 @@
3130

3231
async def async_setup_entry(
3332
hass: HomeAssistant,
34-
config_entry: ConfigEntry,
33+
entry: VivintConfigEntry,
3534
async_add_entities: AddEntitiesCallback,
3635
) -> None:
3736
"""Set up Vivint cameras using config entry."""
3837
entities = []
39-
hub: VivintHub = hass.data[DOMAIN][config_entry.entry_id]
38+
hub: VivintHub = entry.runtime_data
4039

41-
hd_stream = config_entry.options.get(CONF_HD_STREAM, DEFAULT_HD_STREAM)
42-
rtsp_stream = config_entry.options.get(CONF_RTSP_STREAM, DEFAULT_RTSP_STREAM)
43-
rtsp_url_logging = config_entry.options.get(
40+
hd_stream = entry.options.get(CONF_HD_STREAM, DEFAULT_HD_STREAM)
41+
rtsp_stream = entry.options.get(CONF_RTSP_STREAM, DEFAULT_RTSP_STREAM)
42+
rtsp_url_logging = entry.options.get(
4443
CONF_RTSP_URL_LOGGING, DEFAULT_RTSP_URL_LOGGING
4544
)
4645

custom_components/vivint/climate.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Support for Vivint thermostats."""
2+
23
from __future__ import annotations
34

45
from typing import Any
@@ -23,12 +24,11 @@
2324
HVACAction,
2425
HVACMode,
2526
)
26-
from homeassistant.config_entries import ConfigEntry
2727
from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
2828
from homeassistant.core import HomeAssistant
2929
from homeassistant.helpers.entity_platform import AddEntitiesCallback
3030

31-
from .const import DOMAIN
31+
from . import VivintConfigEntry
3232
from .hub import VivintEntity, VivintHub
3333

3434
# Map Vivint HVAC Mode to Home Assistant value
@@ -93,12 +93,12 @@
9393

9494
async def async_setup_entry(
9595
hass: HomeAssistant,
96-
config_entry: ConfigEntry,
96+
entry: VivintConfigEntry,
9797
async_add_entities: AddEntitiesCallback,
9898
) -> None:
9999
"""Set up Vivint climate using config entry."""
100100
entities = []
101-
hub: VivintHub = hass.data[DOMAIN][config_entry.entry_id]
101+
hub: VivintHub = entry.runtime_data
102102

103103
for system in hub.account.systems:
104104
for alarm_panel in system.alarm_panels:

custom_components/vivint/config_flow.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Config flow for Vivint integration."""
2+
23
from __future__ import annotations
34

45
import logging
@@ -29,6 +30,7 @@
2930
CONF_DISARM_CODE,
3031
CONF_HD_STREAM,
3132
CONF_MFA,
33+
CONF_REFRESH_TOKEN,
3234
CONF_RTSP_STREAM,
3335
CONF_RTSP_URL_LOGGING,
3436
DEFAULT_HD_STREAM,
@@ -77,6 +79,7 @@ class VivintConfigFlow(ConfigFlow, domain=DOMAIN):
7779
"""Handle a config flow for Vivint."""
7880

7981
VERSION = 1
82+
MINOR_VERSION = 2
8083

8184
def __init__(self) -> None:
8285
"""Initialize a config flow."""
@@ -90,6 +93,7 @@ async def _async_create_entry(self) -> FlowResult:
9093
config_data = {
9194
CONF_USERNAME: self._hub._data[CONF_USERNAME],
9295
CONF_PASSWORD: self._hub._data[CONF_PASSWORD],
96+
CONF_REFRESH_TOKEN: self._hub.account.refresh_token,
9397
}
9498

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

113117
self._hub = VivintHub(self.hass, user_input)
114118
try:
115-
await self._hub.login(load_devices=True, use_cache=False)
119+
await self._hub.login(load_devices=True)
116120
except VivintSkyApiMfaRequiredError:
117121
return await self.async_step_mfa()
118122
except VivintSkyApiAuthenticationError:
@@ -197,6 +201,6 @@ async def async_step_reauth_confirm(
197201

198202
@staticmethod
199203
@callback
200-
def async_get_options_flow(config_entry: ConfigEntry) -> SchemaOptionsFlowHandler:
204+
def async_get_options_flow(entry: ConfigEntry) -> SchemaOptionsFlowHandler:
201205
"""Get the options flow for this handler."""
202-
return SchemaOptionsFlowHandler(config_entry, OPTIONS_FLOW)
206+
return SchemaOptionsFlowHandler(entry, OPTIONS_FLOW)

custom_components/vivint/const.py

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Constants for the Vivint integration."""
2+
23
DOMAIN = "vivint"
34
EVENT_TYPE = f"{DOMAIN}_event"
45

@@ -12,6 +13,7 @@
1213
}
1314

1415
CONF_MFA = "code"
16+
CONF_REFRESH_TOKEN = "refresh_token"
1517
CONF_DISARM_CODE = "disarm_code"
1618
CONF_HD_STREAM = "hd_stream"
1719
CONF_RTSP_STREAM = "rtsp_stream"

0 commit comments

Comments
 (0)