Skip to content

Commit c7aa8b6

Browse files
committed
Implement re-authentication flow and handle authentication errors in Rinnai integration
1 parent 8158230 commit c7aa8b6

File tree

3 files changed

+135
-11
lines changed

3 files changed

+135
-11
lines changed

custom_components/rinnaicontrolr-ha/config_flow.py

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD
1111
from homeassistant.helpers.aiohttp_client import async_get_clientsession
1212
from homeassistant.core import callback
13+
from homeassistant.exceptions import ConfigEntryAuthFailed
1314

1415
from .const import (
1516
DOMAIN,
@@ -20,8 +21,9 @@
2021
CONF_REFRESH_TOKEN,
2122
)
2223

23-
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
24+
class ConfigFlow(config_entries.ConfigFlow):
2425
"""Handle a config flow for Rinnai."""
26+
DOMAIN = DOMAIN
2527

2628
VERSION = 2
2729

@@ -91,6 +93,75 @@ async def async_step_user(self, user_input=None):
9193
}
9294
)
9395

96+
async def async_step_reauth(self, user_input=None):
97+
"""Handle re-authentication with the user."""
98+
errors: dict[str, str] = {}
99+
if user_input is None:
100+
return self.async_show_form(
101+
step_id="reauth",
102+
data_schema=vol.Schema(
103+
{
104+
vol.Required(CONF_EMAIL, default=self.context.get(CONF_EMAIL, "")): str,
105+
vol.Required(CONF_PASSWORD): str,
106+
}
107+
),
108+
errors=errors,
109+
)
110+
111+
self.username = user_input[CONF_EMAIL]
112+
self.password = user_input[CONF_PASSWORD]
113+
114+
try:
115+
self.api = API()
116+
await self.api.async_login(self.username, self.password)
117+
except RequestError as request_error:
118+
LOGGER.error("Reauth: Error connecting to the Rinnai API: %s", request_error)
119+
errors["base"] = "cannot_connect"
120+
return self.async_show_form(
121+
step_id="reauth",
122+
data_schema=vol.Schema(
123+
{
124+
vol.Required(CONF_EMAIL, default=self.username): str,
125+
vol.Required(CONF_PASSWORD): str,
126+
}
127+
),
128+
errors=errors,
129+
)
130+
except Exception as err:
131+
LOGGER.error("Reauth: Unexpected error: %s", err)
132+
errors["base"] = "unknown"
133+
return self.async_show_form(
134+
step_id="reauth",
135+
data_schema=vol.Schema(
136+
{
137+
vol.Required(CONF_EMAIL, default=self.username): str,
138+
vol.Required(CONF_PASSWORD): str,
139+
}
140+
),
141+
errors=errors,
142+
)
143+
144+
# Safely get entry_id from context
145+
entry_id = self.context.get("entry_id")
146+
if not entry_id:
147+
LOGGER.error("Reauth: No entry_id in context; cannot update tokens.")
148+
return self.async_abort(reason="reauth_failed")
149+
entry = self.hass.config_entries.async_get_entry(entry_id)
150+
if entry:
151+
self.hass.config_entries.async_update_entry(
152+
entry,
153+
data={
154+
**entry.data,
155+
CONF_EMAIL: self.username,
156+
CONF_ACCESS_TOKEN: self.api.access_token,
157+
CONF_REFRESH_TOKEN: self.api.refresh_token,
158+
},
159+
)
160+
self.hass.async_create_task(
161+
self.hass.config_entries.async_reload(entry.entry_id)
162+
)
163+
return self.async_abort(reason="reauth_successful")
164+
94165
@staticmethod
95166
@callback
96167
def async_get_options_flow(config_entry):

custom_components/rinnaicontrolr-ha/device.py

Lines changed: 62 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
from aiorinnai.api import API
77
from aiorinnai.errors import RequestError
8+
from aiorinnai.api import Unauthenticated
9+
from homeassistant.exceptions import ConfigEntryAuthFailed
810
from async_timeout import timeout
911

1012
from homeassistant.core import HomeAssistant
@@ -54,6 +56,9 @@ async def _async_update_data(self) -> None:
5456
await asyncio.gather(
5557
self._update_device()
5658
)
59+
except Unauthenticated as error:
60+
LOGGER.error("Authentication error: %s", error)
61+
raise ConfigEntryAuthFailed from error
5762
except RequestError as error:
5863
raise UpdateFailed(error) from error
5964

@@ -193,30 +198,78 @@ def pump_cycles(self) -> Optional[float]:
193198
return float(self._device_information["data"]["getDevice"]["info"]["m20_pump_cycles"])
194199

195200
async def async_set_temperature(self, temperature: int) -> None:
196-
await self.api_client.device.set_temperature(self._device_information["data"]["getDevice"], temperature)
201+
try:
202+
await self.api_client.device.set_temperature(self._device_information["data"]["getDevice"], temperature)
203+
except Unauthenticated as error:
204+
LOGGER.error("Authentication error: %s", error)
205+
raise ConfigEntryAuthFailed from error
206+
except RequestError as error:
207+
raise UpdateFailed(error) from error
197208

198209
async def async_start_recirculation(self, duration: int) -> None:
199-
await self.api_client.device.start_recirculation(self._device_information["data"]["getDevice"], duration)
210+
try:
211+
await self.api_client.device.start_recirculation(self._device_information["data"]["getDevice"], duration)
212+
except Unauthenticated as error:
213+
LOGGER.error("Authentication error: %s", error)
214+
raise ConfigEntryAuthFailed from error
215+
except RequestError as error:
216+
raise UpdateFailed(error) from error
200217

201218
async def async_stop_recirculation(self) -> None:
202-
await self.api_client.device.stop_recirculation(self._device_information["data"]["getDevice"])
219+
try:
220+
await self.api_client.device.stop_recirculation(self._device_information["data"]["getDevice"])
221+
except Unauthenticated as error:
222+
LOGGER.error("Authentication error: %s", error)
223+
raise ConfigEntryAuthFailed from error
224+
except RequestError as error:
225+
raise UpdateFailed(error) from error
203226

204227
async def async_enable_vacation_mode(self) -> None:
205-
await self.api_client.device.enable_vacation_mode(self._device_information["data"]["getDevice"])
228+
try:
229+
await self.api_client.device.enable_vacation_mode(self._device_information["data"]["getDevice"])
230+
except Unauthenticated as error:
231+
LOGGER.error("Authentication error: %s", error)
232+
raise ConfigEntryAuthFailed from error
233+
except RequestError as error:
234+
raise UpdateFailed(error) from error
206235

207236
async def async_disable_vacation_mode(self) -> None:
208-
await self.api_client.device.disable_vacation_mode(self._device_information["data"]["getDevice"])
237+
try:
238+
await self.api_client.device.disable_vacation_mode(self._device_information["data"]["getDevice"])
239+
except Unauthenticated as error:
240+
LOGGER.error("Authentication error: %s", error)
241+
raise ConfigEntryAuthFailed from error
242+
except RequestError as error:
243+
raise UpdateFailed(error) from error
209244

210245
async def async_turn_off(self) -> None:
211-
await self.api_client.device.turn_off(self._device_information["data"]["getDevice"])
246+
try:
247+
await self.api_client.device.turn_off(self._device_information["data"]["getDevice"])
248+
except Unauthenticated as error:
249+
LOGGER.error("Authentication error: %s", error)
250+
raise ConfigEntryAuthFailed from error
251+
except RequestError as error:
252+
raise UpdateFailed(error) from error
212253

213254
async def async_turn_on(self) -> None:
214-
await self.api_client.device.turn_on(self._device_information["data"]["getDevice"])
255+
try:
256+
await self.api_client.device.turn_on(self._device_information["data"]["getDevice"])
257+
except Unauthenticated as error:
258+
LOGGER.error("Authentication error: %s", error)
259+
raise ConfigEntryAuthFailed from error
260+
except RequestError as error:
261+
raise UpdateFailed(error) from error
215262

216263
@Throttle(MIN_TIME_BETWEEN_UPDATES)
217264
async def async_do_maintenance_retrieval(self) -> None:
218-
await self.api_client.device.do_maintenance_retrieval(self._device_information["data"]["getDevice"])
219-
LOGGER.debug("Rinnai Maintenance Retrieval Started")
265+
try:
266+
await self.api_client.device.do_maintenance_retrieval(self._device_information["data"]["getDevice"])
267+
LOGGER.debug("Rinnai Maintenance Retrieval Started")
268+
except Unauthenticated as error:
269+
LOGGER.error("Authentication error: %s", error)
270+
raise ConfigEntryAuthFailed from error
271+
except RequestError as error:
272+
raise UpdateFailed(error) from error
220273

221274
async def _update_device(self, *_) -> None:
222275
"""Update the device information from the API"""

custom_components/rinnaicontrolr-ha/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@
55
"documentation": "https://github.com/explosivo22/rinnaicontrolr-ha/",
66
"codeowners": [ "@explosivo22" ],
77
"requirements": [ "aiorinnai==0.4.0a2" ],
8-
"version": "1.5.6",
8+
"version": "1.5.7",
99
"iot_class": "cloud_polling"
1010
}

0 commit comments

Comments
 (0)