Skip to content

Commit f30bc86

Browse files
committed
Added entity migration and fixed a few bugs #28
1 parent e9dc8d2 commit f30bc86

File tree

10 files changed

+133
-134
lines changed

10 files changed

+133
-134
lines changed

custom_components/wemportal/__init__.py

+70-69
Original file line numberDiff line numberDiff line change
@@ -13,30 +13,19 @@
1313
from homeassistant.core import HomeAssistant
1414
from homeassistant.helpers.typing import ConfigType
1515
from homeassistant.config_entries import ConfigEntry
16-
from .const import CONF_LANGUAGE, CONF_MODE, CONF_SCAN_INTERVAL_API, DOMAIN, PLATFORMS
16+
from .const import (
17+
CONF_LANGUAGE,
18+
CONF_MODE,
19+
CONF_SCAN_INTERVAL_API,
20+
DOMAIN,
21+
PLATFORMS,
22+
_LOGGER,
23+
DEFAULT_CONF_SCAN_INTERVAL_API_VALUE,
24+
DEFAULT_CONF_SCAN_INTERVAL_VALUE,
25+
)
1726
from .coordinator import WemPortalDataUpdateCoordinator
1827
from .wemportalapi import WemPortalApi
19-
20-
21-
# CONFIG_SCHEMA = vol.Schema(
22-
# {
23-
# DOMAIN: vol.Schema(
24-
# {
25-
# vol.Optional(
26-
# CONF_SCAN_INTERVAL, default=timedelta(minutes=30)
27-
# ): config_validation.time_period,
28-
# vol.Optional(
29-
# CONF_SCAN_INTERVAL_API, default=timedelta(minutes=5)
30-
# ): config_validation.time_period,
31-
# vol.Optional(CONF_LANGUAGE, default="en"): config_validation.string,
32-
# vol.Optional(CONF_MODE, default="api"): config_validation.string,
33-
# vol.Required(CONF_USERNAME): config_validation.string,
34-
# vol.Required(CONF_PASSWORD): config_validation.string,
35-
# }
36-
# )
37-
# },
38-
# extra=vol.ALLOW_EXTRA,
39-
# )
28+
import homeassistant.helpers.entity_registry as entity_registry
4029

4130

4231
def get_wemportal_unique_id(config_entry_id: str, device_id: str, name: str):
@@ -50,82 +39,94 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
5039
return True
5140

5241

53-
# # Set proper update_interval, based on selected mode
54-
# if config[DOMAIN].get(CONF_MODE) == "web":
55-
# update_interval = config[DOMAIN].get(CONF_SCAN_INTERVAL)
56-
57-
# elif config[DOMAIN].get(CONF_MODE) == "api":
58-
# update_interval = config[DOMAIN].get(CONF_SCAN_INTERVAL_API)
59-
# else:
60-
# update_interval = min(
61-
# config[DOMAIN].get(CONF_SCAN_INTERVAL),
62-
# config[DOMAIN].get(CONF_SCAN_INTERVAL_API),
63-
# )
64-
# # Creatie API object
65-
# api = WemPortalApi(config[DOMAIN])
66-
# # Create custom coordinator
67-
# coordinator = WemPortalDataUpdateCoordinator(hass, api, update_interval)
68-
69-
# hass.data[DOMAIN] = {
70-
# "api": api,
71-
# "coordinator": coordinator,
72-
# }
73-
74-
# await coordinator.async_config_entry_first_refresh()
75-
76-
# # Initialize platforms
77-
# for platform in PLATFORMS:
78-
# hass.helpers.discovery.load_platform(platform, DOMAIN, {}, config)
79-
# return True
42+
# Migrate values from previous versions
43+
async def migrate_unique_ids(
44+
hass: HomeAssistant, config_entry: ConfigEntry, coordinator
45+
):
46+
er = await entity_registry.async_get_registry(hass)
47+
# Do migration for first device if we have multiple
48+
device_id = list(coordinator.data.keys())[0]
49+
data = coordinator.data[device_id]
50+
51+
change = False
52+
for unique_id, values in data.items():
53+
name_id = er.async_get_entity_id(values["platform"], DOMAIN, unique_id)
54+
new_id = get_wemportal_unique_id(config_entry.entry_id, device_id, unique_id)
55+
if name_id is not None:
56+
_LOGGER.info(
57+
f"Found entity with old id ({name_id}). Updating to new unique_id ({new_id})."
58+
)
59+
_LOGGER.error(unique_id)
60+
# check if there already is a new one
61+
new_entity_id = er.async_get_entity_id(values["platform"], DOMAIN, new_id)
62+
if new_entity_id is not None:
63+
_LOGGER.info(
64+
"Found entity with old id and an entity with a new unique_id. Preserving old entity..."
65+
)
66+
er.async_remove(new_entity_id)
67+
er.async_update_entity(
68+
name_id,
69+
new_unique_id=new_id,
70+
)
71+
change = True
72+
if change:
73+
await coordinator.async_config_entry_first_refresh()
8074

8175

8276
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
8377
"""Set up the wemportal component."""
8478
# Set proper update_interval, based on selected mode
85-
if entry.data.get(CONF_MODE) == "web":
86-
update_interval = entry.data.get(CONF_SCAN_INTERVAL)
79+
if entry.options.get(CONF_MODE) == "web":
80+
update_interval = entry.options.get(
81+
CONF_SCAN_INTERVAL, DEFAULT_CONF_SCAN_INTERVAL_VALUE
82+
)
8783

88-
elif entry.data.get(CONF_MODE) == "api":
89-
update_interval = entry.data.get(CONF_SCAN_INTERVAL_API)
84+
elif entry.options.get(CONF_MODE) == "api":
85+
update_interval = entry.options.get(
86+
CONF_SCAN_INTERVAL_API, DEFAULT_CONF_SCAN_INTERVAL_API_VALUE
87+
)
9088
else:
9189
update_interval = min(
92-
entry.data.get(CONF_SCAN_INTERVAL),
93-
entry.data.get(CONF_SCAN_INTERVAL_API),
90+
entry.options.get(CONF_SCAN_INTERVAL, DEFAULT_CONF_SCAN_INTERVAL_VALUE),
91+
entry.options.get(
92+
CONF_SCAN_INTERVAL_API, DEFAULT_CONF_SCAN_INTERVAL_API_VALUE
93+
),
9494
)
9595
# Creatie API object
96-
api = WemPortalApi(entry.data)
96+
97+
api = WemPortalApi(
98+
entry.data.get(CONF_USERNAME), entry.data.get(CONF_PASSWORD), entry.options
99+
)
97100
# Create custom coordinator
98101
coordinator = WemPortalDataUpdateCoordinator(
99102
hass, api, timedelta(seconds=update_interval)
100103
)
101104

102105
await coordinator.async_config_entry_first_refresh()
103106

107+
try:
108+
version = entry.version
109+
if version < 2:
110+
await migrate_unique_ids(hass, entry, coordinator)
111+
except Exception:
112+
await migrate_unique_ids(hass, entry, coordinator)
113+
104114
hass.data[DOMAIN][entry.entry_id] = {
105115
"api": api,
106116
# "config": entry.data,
107117
"coordinator": coordinator,
108118
}
109119

110-
# TODO: Implement removal of outdated entries
111-
# current_devices: set[tuple[str, str]] = set({(DOMAIN, entry.entry_id)})
112-
113-
# device_registry = dr.async_get(hass)
114-
# for device_entry in dr.async_entries_for_config_entry(
115-
# device_registry, entry.entry_id
116-
# ):
117-
# for identifier in device_entry.identifiers:
118-
# if identifier in current_devices:
119-
# break
120-
# else:
121-
# device_registry.async_remove_device(device_entry.id)
122-
123120
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
124121
entry.async_on_unload(entry.add_update_listener(_async_entry_updated))
125122

126123
return True
127124

128125

126+
async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
127+
return True
128+
129+
129130
async def _async_entry_updated(hass: HomeAssistant, config_entry: ConfigEntry) -> None:
130131
"""Handle entry updates."""
131132
await hass.config_entries.async_reload(config_entry.entry_id)

custom_components/wemportal/config_flow.py

+18-15
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,9 @@
3030

3131
async def validate_input(hass: core.HomeAssistant, data):
3232
"""Validate the user input allows us to connect."""
33-
og_data = data
34-
# Populate with default values
35-
# Should be fixed, but for now, this should work.
36-
data[CONF_MODE] = "api"
37-
data[CONF_LANGUAGE] = "en"
38-
data[CONF_SCAN_INTERVAL_API] = 300
39-
data[CONF_SCAN_INTERVAL] = 1800
4033

4134
# Create API object
42-
api = WemPortalApi(data)
35+
api = WemPortalApi(data[CONF_USERNAME], data[CONF_PASSWORD])
4336

4437
# Try to login
4538
try:
@@ -49,13 +42,13 @@ async def validate_input(hass: core.HomeAssistant, data):
4942
except UnknownAuthError:
5043
raise CannotConnect from UnknownAuthError
5144

52-
return og_data
45+
return data
5346

5447

5548
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
56-
"""Handle a config flow for kmtronic."""
49+
"""Handle a config flow for wemportal."""
5750

58-
VERSION = 1
51+
VERSION = 2
5952

6053
@staticmethod
6154
@callback
@@ -117,13 +110,23 @@ async def async_step_init(self, user_input=None):
117110
data_schema=vol.Schema(
118111
{
119112
vol.Optional(
120-
CONF_SCAN_INTERVAL, default=1800
113+
CONF_SCAN_INTERVAL,
114+
default=self.config_entry.options.get(CONF_SCAN_INTERVAL, 1800),
121115
): config_validation.positive_int,
122116
vol.Optional(
123-
CONF_SCAN_INTERVAL_API, default=300
117+
CONF_SCAN_INTERVAL_API,
118+
default=self.config_entry.options.get(
119+
CONF_SCAN_INTERVAL_API, 300
120+
),
124121
): config_validation.positive_int,
125-
vol.Optional(CONF_LANGUAGE, default="en"): config_validation.string,
126-
vol.Optional(CONF_MODE, default="api"): config_validation.string,
122+
vol.Optional(
123+
CONF_LANGUAGE,
124+
default=self.config_entry.options.get(CONF_LANGUAGE, "en"),
125+
): config_validation.string,
126+
vol.Optional(
127+
CONF_MODE,
128+
default=self.config_entry.options.get(CONF_MODE, "api"),
129+
): config_validation.string,
127130
}
128131
),
129132
)

custom_components/wemportal/const.py

+9-5
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,19 @@
33
from typing import Final
44

55
_LOGGER = logging.getLogger("custom_components.wemportal")
6-
DOMAIN = "wemportal"
7-
DEFAULT_NAME = "Weishaupt WEM Portal"
8-
DEFAULT_TIMEOUT = 360
9-
START_URLS = ["https://www.wemportal.com/Web/login.aspx"]
6+
DOMAIN: Final = "wemportal"
7+
DEFAULT_NAME: Final = "Weishaupt WEM Portal"
8+
DEFAULT_TIMEOUT: Final = 360
9+
START_URLS: Final = ["https://www.wemportal.com/Web/login.aspx"]
1010
CONF_SCAN_INTERVAL_API: Final = "api_scan_interval"
1111
CONF_LANGUAGE: Final = "language"
1212
CONF_MODE: Final = "mode"
1313
PLATFORMS = ["sensor", "number", "select", "switch"]
14-
REFRESH_WAIT_TIME: int = 360
14+
REFRESH_WAIT_TIME: Final = 360
1515
DATA_GATHERING_ERROR: Final = "An error occurred while gathering parameters data. \
1616
This issue should resolve by itself. If this problem persists, \
1717
open an issue at https://github.com/erikkastelec/hass-WEM-Portal/issues "
18+
DEFAULT_CONF_SCAN_INTERVAL_API_VALUE: Final = 300
19+
DEFAULT_CONF_SCAN_INTERVAL_VALUE: Final = 1800
20+
DEFAULT_CONF_LANGUAGE_VALUE: Final = "en"
21+
DEFAULT_CONF_MODE_VALUE: Final = "api"

custom_components/wemportal/coordinator.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
class WemPortalDataUpdateCoordinator(DataUpdateCoordinator):
1616
"""DataUpdateCoordinator for wemportal component"""
1717

18-
def __init__(self, hass: HomeAssistant, api: WemPortalApi, update_interval):
18+
def __init__(self, hass: HomeAssistant, api: WemPortalApi, update_interval) -> None:
1919
"""Initialize DataUpdateCoordinator for the wemportal component"""
2020
super().__init__(
2121
hass,

custom_components/wemportal/manifest.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"documentation": "https://github.com/erikkastelec/hass-WEM-Portal",
55
"issue_tracker": "https://github.com/erikkastelec/hass-WEM-Portal/issues",
66
"dependencies": [],
7-
"version": "1.5.0",
7+
"version": "1.5.1",
88
"codeowners": [
99
"@erikkastelec"
1010
],

custom_components/wemportal/number.py

+3-5
Original file line numberDiff line numberDiff line change
@@ -119,11 +119,9 @@ def available(self):
119119

120120
async def async_added_to_hass(self):
121121
"""When entity is added to hass."""
122-
self.coordinator.async_add_listener(self.async_write_ha_state)
123-
124-
async def async_will_remove_from_hass(self):
125-
"""When entity will be removed from hass."""
126-
self.coordinator.async_remove_listener(self.async_write_ha_state)
122+
self.async_on_remove(
123+
self.coordinator.async_add_listener(self.async_write_ha_state)
124+
)
127125

128126
@property
129127
def name(self):

custom_components/wemportal/select.py

+3-5
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,9 @@ def current_option(self) -> str:
123123

124124
async def async_added_to_hass(self):
125125
"""When entity is added to hass."""
126-
self.coordinator.async_add_listener(self.async_write_ha_state)
127-
128-
async def async_will_remove_from_hass(self):
129-
"""When entity will be removed from hass."""
130-
self.coordinator.async_remove_listener(self.async_write_ha_state)
126+
self.async_on_remove(
127+
self.coordinator.async_add_listener(self.async_write_ha_state)
128+
)
131129

132130
@property
133131
def name(self):

custom_components/wemportal/sensor.py

+3-5
Original file line numberDiff line numberDiff line change
@@ -107,11 +107,9 @@ def available(self):
107107

108108
async def async_added_to_hass(self):
109109
"""When entity is added to hass."""
110-
self.coordinator.async_add_listener(self.async_write_ha_state)
111-
112-
async def async_will_remove_from_hass(self):
113-
"""When entity will be removed from hass."""
114-
self.coordinator.async_remove_listener(self.async_write_ha_state)
110+
self.async_on_remove(
111+
self.coordinator.async_add_listener(self.async_write_ha_state)
112+
)
115113

116114
@property
117115
def name(self):

custom_components/wemportal/switch.py

+3-15
Original file line numberDiff line numberDiff line change
@@ -137,11 +137,9 @@ def available(self):
137137

138138
async def async_added_to_hass(self):
139139
"""When entity is added to hass."""
140-
self.coordinator.async_add_listener(self.async_write_ha_state)
141-
142-
async def async_will_remove_from_hass(self):
143-
"""When entity will be removed from hass."""
144-
self.coordinator.async_remove_listener(self.async_write_ha_state)
140+
self.async_on_remove(
141+
self.coordinator.async_add_listener(self.async_write_ha_state)
142+
)
145143

146144
@property
147145
def name(self):
@@ -176,16 +174,6 @@ def state(self):
176174
_LOGGER.debug("Sensor data %s", self.coordinator.data)
177175
return None
178176

179-
# @property
180-
# def state_class(self):
181-
# """Return the state class of this entity, if any."""
182-
# if self._unit in ("°C", "kW", "W", "%"):
183-
# return STATE_CLASS_MEASUREMENT
184-
# elif self._unit in ("kWh", "Wh"):
185-
# return STATE_CLASS_TOTAL_INCREASING
186-
# else:
187-
# return None
188-
189177
@property
190178
def device_class(self):
191179
return "switch"

0 commit comments

Comments
 (0)