Skip to content

Commit 086261e

Browse files
authored
Merge pull request #792 from plugwise/reconfigure
Catchup core quality changes
2 parents 35f335e + 36f6c68 commit 086261e

File tree

19 files changed

+616
-367
lines changed

19 files changed

+616
-367
lines changed

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,13 @@
22

33
Versions from 0.40 and up
44

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

7+
- Rework quality improvements from Core Quality Scale
8+
9+
## v0.55.4
10+
11+
- Link to plugwise [v1.6.4](https://github.com/plugwise/python-plugwise/releases/tag/v1.6.4)
712
- Internal: Adjustments in the CI process
813

914
## v0.55.3

custom_components/plugwise/binary_sensor.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@
4040
from .coordinator import PlugwiseDataUpdateCoordinator
4141
from .entity import PlugwiseEntity
4242

43-
PARALLEL_UPDATES = 0 # Upstream
43+
# Coordinator is used to centralize the data updates
44+
PARALLEL_UPDATES = 0
4445

4546

4647
@dataclass(frozen=True)
@@ -54,7 +55,6 @@ class PlugwiseBinarySensorEntityDescription(BinarySensorEntityDescription):
5455
PLUGWISE_BINARY_SENSORS: tuple[PlugwiseBinarySensorEntityDescription, ...] = (
5556
PlugwiseBinarySensorEntityDescription(
5657
key=BATTERY_STATE,
57-
translation_key=BATTERY_STATE,
5858
device_class=BinarySensorDeviceClass.BATTERY,
5959
entity_category=EntityCategory.DIAGNOSTIC,
6060
),

custom_components/plugwise/button.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from .entity import PlugwiseEntity
1818
from .util import plugwise_command
1919

20-
PARALLEL_UPDATES = 0 # Upstream
20+
PARALLEL_UPDATES = 0
2121

2222

2323
async def async_setup_entry(

custom_components/plugwise/climate.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
from .entity import PlugwiseEntity
5757
from .util import plugwise_command
5858

59-
PARALLEL_UPDATES = 0 # Upstream
59+
PARALLEL_UPDATES = 0
6060

6161

6262
async def async_setup_entry(

custom_components/plugwise/config_flow.py

Lines changed: 96 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from __future__ import annotations
44

55
from copy import deepcopy
6+
import logging
67
from typing import Any, Self
78

89
from plugwise import Smile
@@ -69,18 +70,25 @@
6970

7071
type PlugwiseConfigEntry = ConfigEntry[PlugwiseDataUpdateCoordinator]
7172

73+
_LOGGER = logging.getLogger(__name__)
74+
7275
# Upstream basically the whole file (excluding the pw-beta options)
7376

77+
SMILE_RECONF_SCHEMA = vol.Schema(
78+
{
79+
vol.Required(CONF_HOST): str,
80+
}
81+
)
82+
7483

75-
def base_schema(
76-
cf_input: ZeroconfServiceInfo | dict[str, Any] | None,
77-
) -> vol.Schema:
84+
def smile_user_schema(cf_input: ZeroconfServiceInfo | dict[str, Any] | None) -> vol.Schema:
7885
"""Generate base schema for gateways."""
7986
if not cf_input: # no discovery- or user-input available
8087
return vol.Schema(
8188
{
8289
vol.Required(CONF_HOST): str,
8390
vol.Required(CONF_PASSWORD): str,
91+
# Port under investigation for removal (hence not added in #132878)
8492
vol.Optional(CONF_PORT, default=DEFAULT_PORT): int,
8593
vol.Required(CONF_USERNAME, default=SMILE): vol.In(
8694
{SMILE: FLOW_SMILE, STRETCH: FLOW_STRETCH}
@@ -106,7 +114,7 @@ def base_schema(
106114
async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> Smile:
107115
"""Validate whether the user input allows us to connect to the gateway.
108116
109-
Data has the keys from base_schema() with values provided by the user.
117+
Data has the keys from the schema with values provided by the user.
110118
"""
111119
websession = async_get_clientsession(hass, verify_ssl=False)
112120
api = Smile(
@@ -120,6 +128,32 @@ async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> Smile:
120128
return api
121129

122130

131+
async def verify_connection(
132+
hass: HomeAssistant, user_input: dict[str, Any]
133+
) -> tuple[Smile | None, dict[str, str]]:
134+
"""Verify and return the gateway connection or an error."""
135+
errors: dict[str, str] = {}
136+
137+
try:
138+
return (await validate_input(hass, user_input), errors)
139+
except ConnectionFailedError:
140+
errors[CONF_BASE] = "cannot_connect"
141+
except InvalidAuthentication:
142+
errors[CONF_BASE] = "invalid_auth"
143+
except InvalidSetupError:
144+
errors[CONF_BASE] = "invalid_setup"
145+
except (InvalidXMLError, ResponseError):
146+
errors[CONF_BASE] = "response_error"
147+
except UnsupportedDeviceError:
148+
errors[CONF_BASE] = "unsupported"
149+
except Exception: # noqa: BLE001
150+
_LOGGER.exception(
151+
"Unknown exception while verifying connection with your Plugwise Smile"
152+
)
153+
errors[CONF_BASE] = "unknown"
154+
return (None, errors)
155+
156+
123157
class PlugwiseConfigFlow(ConfigFlow, domain=DOMAIN):
124158
"""Handle a config flow for Plugwise Smile."""
125159

@@ -197,51 +231,71 @@ def is_matching(self, other_flow: Self) -> bool:
197231

198232
return False
199233

234+
200235
async def async_step_user(
201236
self, user_input: dict[str, Any] | None = None
202237
) -> ConfigFlowResult:
203238
"""Handle the initial step when using network/gateway setups."""
204239
errors: dict[str, str] = {}
205240

206-
if not user_input:
207-
return self.async_show_form(
208-
step_id=SOURCE_USER,
209-
data_schema=base_schema(self.discovery_info),
210-
errors=errors,
211-
)
212-
213-
if self.discovery_info:
214-
user_input[CONF_HOST] = self.discovery_info.host
215-
user_input[CONF_PORT] = self.discovery_info.port
216-
user_input[CONF_USERNAME] = self._username
217-
218-
try:
219-
api = await validate_input(self.hass, user_input)
220-
except ConnectionFailedError:
221-
errors[CONF_BASE] = "cannot_connect"
222-
except InvalidAuthentication:
223-
errors[CONF_BASE] = "invalid_auth"
224-
except InvalidSetupError:
225-
errors[CONF_BASE] = "invalid_setup"
226-
except (InvalidXMLError, ResponseError):
227-
errors[CONF_BASE] = "response_error"
228-
except UnsupportedDeviceError:
229-
errors[CONF_BASE] = "unsupported"
230-
except Exception: # noqa: BLE001
231-
errors[CONF_BASE] = "unknown"
232-
233-
if errors:
234-
return self.async_show_form(
235-
step_id=SOURCE_USER,
236-
data_schema=base_schema(user_input),
237-
errors=errors,
238-
)
239-
240-
await self.async_set_unique_id(
241-
api.smile_hostname or api.gateway_id, raise_on_progress=False
241+
if user_input is not None:
242+
if self.discovery_info:
243+
user_input[CONF_HOST] = self.discovery_info.host
244+
user_input[CONF_PORT] = self.discovery_info.port
245+
user_input[CONF_USERNAME] = self._username
246+
247+
api, errors = await verify_connection(self.hass, user_input)
248+
if api:
249+
await self.async_set_unique_id(
250+
api.smile_hostname or api.gateway_id, raise_on_progress=False
251+
)
252+
self._abort_if_unique_id_configured()
253+
return self.async_create_entry(title=api.smile_name, data=user_input)
254+
255+
return self.async_show_form(
256+
step_id=SOURCE_USER,
257+
data_schema=smile_user_schema(self.discovery_info),
258+
errors=errors,
242259
)
243-
self._abort_if_unique_id_configured()
244-
return self.async_create_entry(title=api.smile_name, data=user_input)
260+
261+
async def async_step_reconfigure(
262+
self, user_input: dict[str, Any] | None = None
263+
) -> ConfigFlowResult:
264+
"""Handle reconfiguration of the integration."""
265+
errors: dict[str, str] = {}
266+
267+
reconfigure_entry = self._get_reconfigure_entry()
268+
269+
if user_input:
270+
# Redefine ingest existing username and password
271+
full_input = {
272+
CONF_HOST: user_input.get(CONF_HOST),
273+
CONF_PORT: reconfigure_entry.data.get(CONF_PORT),
274+
CONF_USERNAME: reconfigure_entry.data.get(CONF_USERNAME),
275+
CONF_PASSWORD: reconfigure_entry.data.get(CONF_PASSWORD),
276+
}
277+
278+
api, errors = await verify_connection(self.hass, full_input)
279+
if api:
280+
await self.async_set_unique_id(
281+
api.smile_hostname or api.gateway_id, raise_on_progress=False
282+
)
283+
self._abort_if_unique_id_mismatch(reason="not_the_same_smile")
284+
return self.async_update_reload_and_abort(
285+
reconfigure_entry,
286+
data_updates=full_input,
287+
)
288+
289+
return self.async_show_form(
290+
step_id="reconfigure",
291+
data_schema=self.add_suggested_values_to_schema(
292+
data_schema=SMILE_RECONF_SCHEMA,
293+
suggested_values=reconfigure_entry.data,
294+
),
295+
description_placeholders={"title": reconfigure_entry.title},
296+
errors=errors,
297+
)
298+
245299

246300
@staticmethod
247301
@callback

custom_components/plugwise/coordinator.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,17 +97,31 @@ async def _async_update_data(self) -> PlugwiseData:
9797
await self._connect()
9898
data = await self.api.async_update()
9999
except ConnectionFailedError as err:
100-
raise UpdateFailed("Failed to connect") from err
100+
raise UpdateFailed(
101+
translation_domain=DOMAIN,
102+
translation_key="failed_to_connect",
103+
) from err
101104
except InvalidAuthentication as err:
102-
raise ConfigEntryError("Authentication failed") from err
105+
raise ConfigEntryError(
106+
translation_domain=DOMAIN,
107+
translation_key="authentication_failed",
108+
) from err
103109
except (InvalidXMLError, ResponseError) as err:
110+
# pwbeta TODO; we had {err} in the text, but not upstream, do we want this?
104111
raise UpdateFailed(
105-
f"Invalid XML data or error from Plugwise device: {err}"
112+
translation_domain=DOMAIN,
113+
translation_key="invalid_xml_data",
106114
) from err
107115
except PlugwiseError as err:
108-
raise UpdateFailed("Data incomplete or missing") from err
116+
raise UpdateFailed(
117+
translation_domain=DOMAIN,
118+
translation_key="data_incomplete_or_missing",
119+
) from err
109120
except UnsupportedDeviceError as err:
110-
raise ConfigEntryError("Device with unsupported firmware") from err
121+
raise ConfigEntryError(
122+
translation_domain=DOMAIN,
123+
translation_key="unsupported_firmware",
124+
) from err
111125
else:
112126
LOGGER.debug(f"{self.api.smile_name} data: %s", data)
113127
await self.async_add_remove_devices(data, self.config_entry)

custom_components/plugwise/entity.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,3 @@ def available(self) -> bool:
9393
def device(self) -> GwEntityData:
9494
"""Return data for this device."""
9595
return self.coordinator.data.devices[self._dev_id]
96-
97-
async def async_added_to_hass(self) -> None:
98-
"""Subscribe to updates."""
99-
self._handle_coordinator_update()
100-
await super().async_added_to_hass()

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.6.4"],
11-
"version": "0.55.4",
11+
"version": "0.55.5",
1212
"zeroconf": ["_plugwise._tcp.local."]
1313
}

custom_components/plugwise/number.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
from .entity import PlugwiseEntity
3232
from .util import plugwise_command
3333

34-
PARALLEL_UPDATES = 0 # Upstream
34+
PARALLEL_UPDATES = 0
3535

3636

3737
@dataclass(frozen=True, kw_only=True)

custom_components/plugwise/select.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
from .entity import PlugwiseEntity
3434
from .util import plugwise_command
3535

36-
PARALLEL_UPDATES = 0 # Upstream
36+
PARALLEL_UPDATES = 0
3737

3838

3939
@dataclass(frozen=True, kw_only=True)

0 commit comments

Comments
 (0)