Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
5933c09
Add Fluss+ Button integration (#139925)
Marcello17 Dec 23, 2025
bcc5985
Enable HomeWizard Battery group mode by default when device controls …
DCSBL Dec 23, 2025
5107b70
Add helper utility for patching Pydantic model methods in UniFi Prote…
RaHehl Dec 23, 2025
c0fa6ad
Support KNX scene entity configuration from UI (#159494)
farmio Dec 23, 2025
aa20a74
Bump reolink_aio to 0.18.0 (#159649)
starkillerOG Dec 23, 2025
a3dec29
Add Computer Name to device in Libre Hardware Monitor (#159342)
Sab44 Dec 23, 2025
b3c78d4
Improve date handling in UniFi Protect media source (#159491)
RaHehl Dec 23, 2025
02f412f
Update template sensor tests to use new framework (#159466)
Petro31 Dec 23, 2025
a33a4b6
Deprecate pyserial-asyncio in requirements manager (#159368)
epenet Dec 23, 2025
85311e3
Add solar production sensors to neurio_energy (#159533)
W7RZL Dec 23, 2025
0f3f16f
Remove migration of wrong encoded folder path from WebDAV (#159457)
jpbede Dec 23, 2025
b87b72a
Repair flow description placeholders are optional (#159385)
epenet Dec 23, 2025
9715a7c
Add light level data to switchbot presence sensor (#159356)
lukkigi Dec 23, 2025
af12188
Add Transmission get_torrents service and codeowner (#159211)
andrew-codechimp Dec 23, 2025
19f8d9d
Fix ZeroDivisionError for inverse unit conversions (#159161)
ReneNulschDE Dec 23, 2025
7c14862
Normalize unique ID in WLED (#157901)
mik-laj Dec 23, 2025
0525c75
Support media player grouping in bluesound integration (#159455)
LouisChrist Dec 23, 2025
5150efd
Create issue for Sonos when Sonos system does not have UPnP enabled (…
PeteRager Dec 23, 2025
34db548
Change Samsung TV WoL turn_on log from warning to debug (#158676)
ptarjan Dec 23, 2025
b07b699
Add account selector to Anglian Water config flow (#158242)
pantherale0 Dec 23, 2025
7c71c03
Remove deprecated import from stiebel_eltron (#158110)
ThyMYthOS Dec 23, 2025
9516502
Adjust vesync to follow action-setup (#157795)
cdnninja Dec 23, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions CODEOWNERS

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

123 changes: 101 additions & 22 deletions homeassistant/components/anglian_water/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from __future__ import annotations

import logging
from typing import Any
from typing import TYPE_CHECKING, Any

from aiohttp import CookieJar
from pyanglianwater import AnglianWater
Expand All @@ -30,14 +30,11 @@
vol.Required(CONF_PASSWORD): selector.TextSelector(
selector.TextSelectorConfig(type=selector.TextSelectorType.PASSWORD)
),
vol.Required(CONF_ACCOUNT_NUMBER): selector.TextSelector(),
}
)


async def validate_credentials(
auth: MSOB2CAuth, account_number: str
) -> str | MSOB2CAuth:
async def validate_credentials(auth: MSOB2CAuth) -> str | MSOB2CAuth:
"""Validate the provided credentials."""
try:
await auth.send_login_request()
Expand All @@ -46,6 +43,33 @@ async def validate_credentials(
except Exception:
_LOGGER.exception("Unexpected exception")
return "unknown"
return auth


def humanize_account_data(account: dict) -> str:
"""Convert an account data into a human-readable format."""
if account["address"]["company_name"] != "":
return f"{account['account_number']} - {account['address']['company_name']}"
if account["address"]["building_name"] != "":
return f"{account['account_number']} - {account['address']['building_name']}"
return f"{account['account_number']} - {account['address']['postcode']}"


async def get_accounts(auth: MSOB2CAuth) -> list[selector.SelectOptionDict]:
"""Retrieve the list of accounts associated with the authenticated user."""
_aw = AnglianWater(authenticator=auth)
accounts = await _aw.api.get_associated_accounts()
return [
selector.SelectOptionDict(
value=str(account["account_number"]),
label=humanize_account_data(account),
)
for account in accounts["result"]["active"]
]


async def validate_account(auth: MSOB2CAuth, account_number: str) -> str | MSOB2CAuth:
"""Validate the provided account number."""
_aw = AnglianWater(authenticator=auth)
try:
await _aw.validate_smart_meter(account_number)
Expand All @@ -57,36 +81,91 @@ async def validate_credentials(
class AnglianWaterConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Anglian Water."""

def __init__(self) -> None:
"""Initialize the config flow."""
self.authenticator: MSOB2CAuth | None = None
self.accounts: list[selector.SelectOptionDict] = []
self.user_input: dict[str, Any] | None = None

async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle the initial step."""
errors: dict[str, str] = {}
if user_input is not None:
validation_response = await validate_credentials(
MSOB2CAuth(
username=user_input[CONF_USERNAME],
password=user_input[CONF_PASSWORD],
session=async_create_clientsession(
self.hass,
cookie_jar=CookieJar(quote_cookie=False),
),
self.authenticator = MSOB2CAuth(
username=user_input[CONF_USERNAME],
password=user_input[CONF_PASSWORD],
session=async_create_clientsession(
self.hass,
cookie_jar=CookieJar(quote_cookie=False),
),
user_input[CONF_ACCOUNT_NUMBER],
)
validation_response = await validate_credentials(self.authenticator)
if isinstance(validation_response, str):
errors["base"] = validation_response
else:
await self.async_set_unique_id(user_input[CONF_ACCOUNT_NUMBER])
self._abort_if_unique_id_configured()
return self.async_create_entry(
title=user_input[CONF_ACCOUNT_NUMBER],
data={
**user_input,
CONF_ACCESS_TOKEN: validation_response.refresh_token,
},
self.accounts = await get_accounts(self.authenticator)
if len(self.accounts) > 1:
self.user_input = user_input
return await self.async_step_select_account()
account_number = self.accounts[0]["value"]
self.user_input = user_input
return await self.async_step_complete(
{
CONF_ACCOUNT_NUMBER: account_number,
}
)

return self.async_show_form(
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors
)

async def async_step_select_account(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle the account selection step."""
errors = {}
if user_input is not None:
if TYPE_CHECKING:
assert self.authenticator
validation_result = await validate_account(
self.authenticator,
user_input[CONF_ACCOUNT_NUMBER],
)
if isinstance(validation_result, str):
errors["base"] = validation_result
else:
return await self.async_step_complete(user_input)
return self.async_show_form(
step_id="select_account",
data_schema=vol.Schema(
{
vol.Required(CONF_ACCOUNT_NUMBER): selector.SelectSelector(
selector.SelectSelectorConfig(
options=self.accounts,
multiple=False,
mode=selector.SelectSelectorMode.DROPDOWN,
)
)
}
),
errors=errors,
)

async def async_step_complete(self, user_input: dict[str, Any]) -> ConfigFlowResult:
"""Handle the final configuration step."""
await self.async_set_unique_id(user_input[CONF_ACCOUNT_NUMBER])
self._abort_if_unique_id_configured()
if TYPE_CHECKING:
assert self.authenticator
assert self.user_input
config_entry_data = {
**self.user_input,
CONF_ACCOUNT_NUMBER: user_input[CONF_ACCOUNT_NUMBER],
CONF_ACCESS_TOKEN: self.authenticator.refresh_token,
}
return self.async_create_entry(
title=user_input[CONF_ACCOUNT_NUMBER],
data=config_entry_data,
)
11 changes: 9 additions & 2 deletions homeassistant/components/anglian_water/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,21 @@
"unknown": "[%key:common::config_flow::error::unknown%]"
},
"step": {
"select_account": {
"data": {
"account_number": "Billing account number"
},
"data_description": {
"account_number": "Select the billing account you wish to use."
},
"description": "Multiple active billing accounts were found with your credentials. Please select the account you wish to use. If this is unexpected, contact Anglian Water to confirm your active accounts."
},
"user": {
"data": {
"account_number": "Billing Account Number",
"password": "[%key:common::config_flow::data::password%]",
"username": "[%key:common::config_flow::data::username%]"
},
"data_description": {
"account_number": "Your account number found on your latest bill.",
"password": "Your password",
"username": "Username or email used to log in to the Anglian Water website."
},
Expand Down
Loading
Loading