Skip to content

Commit e737bd4

Browse files
authored
Merge pull request #734 from plugwise/better-timing
Set timeout based on discovered version
2 parents 7ce5e95 + 77b115c commit e737bd4

File tree

14 files changed

+167
-35
lines changed

14 files changed

+167
-35
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22

33
## Versions from 0.40 and up
44

5-
## Ongoing
5+
## v0.53.3
66

77
- Test-code improvements.
88
- Fix errors showing after Core 2023.9.3 release.
9+
- Set the connection-timeout based on the device discovered, 10s for actual devices, legacy devices require a 30s timeout.
910

1011
## v0.53.2
1112

custom_components/plugwise/__init__.py

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import voluptuous as vol # pw-beta delete_notification
99

1010
from homeassistant.config_entries import ConfigEntry
11-
from homeassistant.const import Platform
11+
from homeassistant.const import CONF_TIMEOUT, Platform
1212
from homeassistant.core import (
1313
HomeAssistant,
1414
ServiceCall, # pw-beta delete_notification
@@ -24,6 +24,7 @@
2424
SERVICE_DELETE, # pw-beta delete_notifications
2525
)
2626
from .coordinator import PlugwiseDataUpdateCoordinator
27+
from .util import get_timeout_for_version
2728

2829
type PlugwiseConfigEntry = ConfigEntry[PlugwiseDataUpdateCoordinator]
2930

@@ -44,7 +45,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: PlugwiseConfigEntry) ->
4445
) # pw-beta - cooldown, update_interval as extra
4546
await coordinator.async_config_entry_first_refresh()
4647

47-
migrate_sensor_entities(hass, coordinator)
48+
await async_migrate_sensor_entities(hass, coordinator)
49+
await async_migrate_plugwise_entry(hass, coordinator, entry)
4850

4951
entry.runtime_data = coordinator
5052

@@ -123,7 +125,7 @@ def async_migrate_entity_entry(entry: er.RegistryEntry) -> dict[str, Any] | None
123125
# No migration needed
124126
return None
125127

126-
def migrate_sensor_entities(
128+
async def async_migrate_sensor_entities(
127129
hass: HomeAssistant,
128130
coordinator: PlugwiseDataUpdateCoordinator,
129131
) -> None:
@@ -143,3 +145,27 @@ def migrate_sensor_entities(
143145
new_unique_id = f"{device_id}-outdoor_air_temperature"
144146
# Upstream remove LOGGER debug
145147
ent_reg.async_update_entity(entity_id, new_unique_id=new_unique_id)
148+
149+
async def async_migrate_plugwise_entry(
150+
hass: HomeAssistant,
151+
coordinator: PlugwiseDataUpdateCoordinator,
152+
entry: ConfigEntry
153+
) -> bool:
154+
"""Migrate to new config entry."""
155+
if entry.version > 1:
156+
return False
157+
158+
if entry.version == 1 and entry.minor_version < 2:
159+
new_data = {**entry.data}
160+
new_data[CONF_TIMEOUT] = get_timeout_for_version(coordinator.api.smile_version)
161+
hass.config_entries.async_update_entry(
162+
entry, data=new_data, minor_version=2, version=1
163+
)
164+
LOGGER.debug(
165+
"Migration to version %s.%s successful",
166+
entry.version,
167+
entry.minor_version,
168+
)
169+
return True
170+
171+
return False

custom_components/plugwise/config_flow.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
CONF_PASSWORD,
3636
CONF_PORT,
3737
CONF_SCAN_INTERVAL,
38+
CONF_TIMEOUT,
3839
CONF_USERNAME,
3940
)
4041

@@ -50,6 +51,7 @@
5051
CONTEXT,
5152
DEFAULT_PORT,
5253
DEFAULT_SCAN_INTERVAL, # pw-beta option
54+
DEFAULT_TIMEOUT,
5355
DEFAULT_USERNAME,
5456
DOMAIN,
5557
FLOW_ID,
@@ -70,6 +72,7 @@
7072

7173
# Upstream
7274
from .coordinator import PlugwiseDataUpdateCoordinator
75+
from .util import get_timeout_for_version
7376

7477
type PlugwiseConfigEntry = ConfigEntry[PlugwiseDataUpdateCoordinator]
7578

@@ -103,7 +106,6 @@ def _base_schema(
103106
),
104107
}
105108
)
106-
107109
return vol.Schema({vol.Required(CONF_PASSWORD): str})
108110

109111

@@ -118,7 +120,7 @@ async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> Smile:
118120
password=data[CONF_PASSWORD],
119121
port=data[CONF_PORT],
120122
username=data[CONF_USERNAME],
121-
timeout=30,
123+
timeout=data[CONF_TIMEOUT],
122124
websession=websession,
123125
)
124126
await api.connect()
@@ -129,9 +131,11 @@ class PlugwiseConfigFlow(ConfigFlow, domain=DOMAIN):
129131
"""Handle a config flow for Plugwise Smile."""
130132

131133
VERSION = 1
134+
MINOR_VERSION = 2
132135

133136
discovery_info: ZeroconfServiceInfo | None = None
134137
_username: str = DEFAULT_USERNAME
138+
_timeout: int = DEFAULT_TIMEOUT
135139

136140
async def async_step_zeroconf(
137141
self, discovery_info: ZeroconfServiceInfo
@@ -147,9 +151,10 @@ async def async_step_zeroconf(
147151
self.hass,
148152
{
149153
CONF_HOST: discovery_info.host,
154+
CONF_PASSWORD: config_entry.data[CONF_PASSWORD],
150155
CONF_PORT: discovery_info.port,
151156
CONF_USERNAME: config_entry.data[CONF_USERNAME],
152-
CONF_PASSWORD: config_entry.data[CONF_PASSWORD],
157+
CONF_TIMEOUT: self._timeout,
153158
},
154159
)
155160
except Exception: # noqa: BLE001
@@ -168,6 +173,8 @@ async def async_step_zeroconf(
168173
_version = _properties.get(VERSION, "n/a")
169174
_name = f"{ZEROCONF_MAP.get(_product, _product)} v{_version}"
170175

176+
self._timeout = get_timeout_for_version(_version)
177+
171178
# This is an Anna, but we already have config entries.
172179
# Assuming that the user has already configured Adam, aborting discovery.
173180
if self._async_current_entries() and _product == SMILE_THERMO:
@@ -200,6 +207,7 @@ async def async_step_zeroconf(
200207
CONF_HOST: discovery_info.host,
201208
CONF_NAME: _name,
202209
CONF_PORT: discovery_info.port,
210+
CONF_TIMEOUT: self._timeout,
203211
CONF_USERNAME: self._username,
204212
},
205213
ATTR_CONFIGURATION_URL: (
@@ -227,6 +235,8 @@ async def async_step_user(
227235
user_input[CONF_HOST] = self.discovery_info.host
228236
user_input[CONF_PORT] = self.discovery_info.port
229237
user_input[CONF_USERNAME] = self._username
238+
239+
user_input[CONF_TIMEOUT] = self._timeout
230240
try:
231241
api = await validate_input(self.hass, user_input)
232242
except ConnectionFailedError:
@@ -253,7 +263,6 @@ async def async_step_user(
253263
api.smile_hostname or api.gateway_id, raise_on_progress=False
254264
)
255265
self._abort_if_unique_id_configured()
256-
257266
return self.async_create_entry(title=api.smile_name, data=user_input)
258267

259268
@staticmethod
@@ -278,7 +287,6 @@ async def async_step_none(
278287
if user_input is not None:
279288
# Apparently not possible to abort an options flow at the moment
280289
return self.async_create_entry(title="", data=self._options)
281-
282290
return self.async_show_form(step_id="none")
283291

284292
async def async_step_init(
@@ -322,5 +330,4 @@ async def async_step_init(
322330
): vol.All(vol.Coerce(float), vol.Range(min=1.5, max=10.0)),
323331
}
324332
) # pw-beta
325-
326333
return self.async_show_form(step_id=INIT, data_schema=vol.Schema(data))

custom_components/plugwise/const.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@
159159
"stretch": timedelta(seconds=60),
160160
"thermostat": timedelta(seconds=60),
161161
}
162-
DEFAULT_TIMEOUT: Final[int] = 10
162+
DEFAULT_TIMEOUT: Final[int] = 30
163163
DEFAULT_USERNAME: Final = "smile"
164164

165165
# --- Const for Plugwise Smile and Stretch

custom_components/plugwise/coordinator.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
CONF_PASSWORD,
1919
CONF_PORT,
2020
CONF_SCAN_INTERVAL, # pw-beta options
21+
CONF_TIMEOUT,
2122
CONF_USERNAME,
2223
)
2324
from homeassistant.core import HomeAssistant
@@ -30,6 +31,7 @@
3031
from .const import (
3132
DEFAULT_PORT,
3233
DEFAULT_SCAN_INTERVAL,
34+
DEFAULT_TIMEOUT,
3335
DEFAULT_USERNAME,
3436
DOMAIN,
3537
GATEWAY_ID,
@@ -73,7 +75,7 @@ def __init__(
7375
username=self.config_entry.data.get(CONF_USERNAME, DEFAULT_USERNAME),
7476
password=self.config_entry.data[CONF_PASSWORD],
7577
port=self.config_entry.data.get(CONF_PORT, DEFAULT_PORT),
76-
timeout=30,
78+
timeout=self.config_entry.data.get(CONF_TIMEOUT, DEFAULT_TIMEOUT),
7779
websession=async_get_clientsession(hass, verify_ssl=False),
7880
)
7981
self._current_devices: set[str] = set()
@@ -115,21 +117,21 @@ async def _async_update_data(self) -> PlugwiseData:
115117
raise ConfigEntryError("Device with unsupported firmware") from err
116118
else:
117119
LOGGER.debug(f"{self.api.smile_name} data: %s", data)
118-
self._async_add_remove_devices(data, self.config_entry)
120+
await self.async_add_remove_devices(data, self.config_entry)
119121

120122
return data
121123

122-
def _async_add_remove_devices(self, data: PlugwiseData, entry: ConfigEntry) -> None:
124+
async def async_add_remove_devices(self, data: PlugwiseData, entry: ConfigEntry) -> None:
123125
"""Add new Plugwise devices, remove non-existing devices."""
124126
# Check for new or removed devices
125127
self.new_devices = set(data.devices) - self._current_devices
126128
removed_devices = self._current_devices - set(data.devices)
127129
self._current_devices = set(data.devices)
128130

129131
if removed_devices:
130-
self._async_remove_devices(data, entry)
132+
await self.async_remove_devices(data, entry)
131133

132-
def _async_remove_devices(self, data: PlugwiseData, entry: ConfigEntry) -> None:
134+
async def async_remove_devices(self, data: PlugwiseData, entry: ConfigEntry) -> None:
133135
"""Clean registries when removed devices found."""
134136
device_reg = dr.async_get(self.hass)
135137
device_list = dr.async_entries_for_config_entry(

custom_components/plugwise/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@
88
"iot_class": "local_polling",
99
"loggers": ["plugwise"],
1010
"requirements": ["plugwise==1.4.0"],
11-
"version": "0.53.2",
11+
"version": "0.53.3",
1212
"zeroconf": ["_plugwise._tcp.local."]
1313
}

custom_components/plugwise/strings.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
"config": {
2020
"step": {
2121
"user": {
22-
"title": "Connect to the Plugwise Adam/Smile/Stretch",
23-
"description": "Please enter:",
22+
"title": "Set up Plugwise Adam/Smile/Stretch",
23+
"description": "Enter your Plugwise device: (setup can take up to 90s)",
2424
"data": {
2525
"password": "ID",
2626
"username": "Username",

custom_components/plugwise/translations/en.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
"config": {
2020
"step": {
2121
"user": {
22-
"title": "Connect to the Plugwise Adam/Smile/Stretch",
23-
"description": "Please enter:",
22+
"title": "Set up Plugwise Adam/Smile/Stretch",
23+
"description": "Enter your Plugwise device: (setup can take up to 90s)",
2424
"data": {
2525
"password": "ID",
2626
"username": "Username",

custom_components/plugwise/translations/nl.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
"config": {
2020
"step": {
2121
"user": {
22-
"title": "Verbinden met de Plugwise Adam/Smile/Stretch",
23-
"description": "Aub invoeren:",
22+
"title": "Installeer Plugwise Adam/Smile/Stretch",
23+
"description": "Van uw Plugwise apparaat voer in: (installeren kan tot 90s duren)",
2424
"data": {
2525
"password": "ID",
2626
"username": "Gebruikersnaam",

custom_components/plugwise/util.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
from plugwise.exceptions import PlugwiseException
99

1010
from homeassistant.exceptions import HomeAssistantError
11+
from packaging import version
1112

13+
from .const import DEFAULT_TIMEOUT
1214
from .entity import PlugwiseEntity
1315

1416
# For reference:
@@ -17,6 +19,17 @@
1719
# _P = ParamSpec("_P")
1820

1921

22+
def get_timeout_for_version(version_str: str) -> int:
23+
"""Determine timeout value based on gateway version.
24+
25+
A gateway firmware version > 3.2.0 should mean a latest-generation-device, allowing for a timeout of 10s.
26+
Legacy devices require a timeout of 30s.
27+
"""
28+
if version.parse(version_str) >= version.parse("3.2.0"):
29+
return 10
30+
return DEFAULT_TIMEOUT
31+
32+
2033
def plugwise_command[_PlugwiseEntityT: PlugwiseEntity, **_P, _R](
2134
func: Callable[Concatenate[_PlugwiseEntityT, _P], Awaitable[_R]],
2235
) -> Callable[Concatenate[_PlugwiseEntityT, _P], Coroutine[Any, Any, _R]]:

0 commit comments

Comments
 (0)