Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Code Quality improvements for Aussie Broadband #65408

Merged
merged 14 commits into from Feb 16, 2022
15 changes: 2 additions & 13 deletions homeassistant/components/aussie_broadband/__init__.py
Expand Up @@ -15,7 +15,7 @@
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed

from .const import CONF_SERVICES, DEFAULT_UPDATE_INTERVAL, DOMAIN, SERVICE_ID
from .const import DEFAULT_UPDATE_INTERVAL, DOMAIN, SERVICE_ID

_LOGGER = logging.getLogger(__name__)
PLATFORMS = [Platform.SENSOR]
Expand All @@ -31,17 +31,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
)
try:
await client.login()
all_services = await client.get_services()
services = await client.get_services()
except AuthenticationException as exc:
raise ConfigEntryAuthFailed() from exc
except ClientError as exc:
raise ConfigEntryNotReady() from exc

# Filter the service list to those that are enabled in options
services = [
s for s in all_services if str(s["service_id"]) in entry.options[CONF_SERVICES]
]

# Create an appropriate refresh function
def update_data_factory(service_id):
async def async_update_data():
Expand Down Expand Up @@ -71,16 +66,10 @@ async def async_update_data():
"services": services,
}
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
entry.async_on_unload(entry.add_update_listener(update_listener))

return True


async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Reload to update options."""
await hass.config_entries.async_reload(entry.entry_id)


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload the config entry."""
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
Expand Down
99 changes: 16 additions & 83 deletions homeassistant/components/aussie_broadband/config_flow.py
Expand Up @@ -9,12 +9,10 @@

from homeassistant import config_entries
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import callback
from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.config_validation as cv

from .const import CONF_SERVICES, DOMAIN, SERVICE_ID
from .const import CONF_SERVICES, DOMAIN


class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
Expand All @@ -39,11 +37,11 @@ async def async_auth(self, user_input: dict[str, str]) -> dict[str, str] | None:
)
try:
await self.client.login()
return None
except AuthenticationException:
return {"base": "invalid_auth"}
except ClientError:
return {"base": "cannot_connect"}
return None

async def async_step_user(
self, user_input: dict[str, Any] | None = None
Expand All @@ -61,15 +59,10 @@ async def async_step_user(
if not self.services:
return self.async_abort(reason="no_services_found")

if len(self.services) == 1:
return self.async_create_entry(
title=self.data[CONF_USERNAME],
data=self.data,
options={CONF_SERVICES: [str(self.services[0][SERVICE_ID])]},
)

# Account has more than one service, select service to add
return await self.async_step_service()
return self.async_create_entry(
title=self.data[CONF_USERNAME],
data=self.data,
)

return self.async_show_form(
step_id="user",
Expand All @@ -82,37 +75,20 @@ async def async_step_user(
errors=errors,
)

async def async_step_service(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Handle the optional service selection step."""
if user_input is not None:
return self.async_create_entry(
title=self.data[CONF_USERNAME], data=self.data, options=user_input
)
async def async_step_reauth(self, user_input: dict[str, str]) -> FlowResult:
"""Handle reauth on credential failure."""
self._reauth_username = user_input[CONF_USERNAME]

service_options = {str(s[SERVICE_ID]): s["description"] for s in self.services}
return self.async_show_form(
step_id="service",
data_schema=vol.Schema(
{
vol.Required(
CONF_SERVICES, default=list(service_options.keys())
): cv.multi_select(service_options)
}
),
errors=None,
)
return await self.async_step_reauth_confirm()

async def async_step_reauth(
self, user_input: dict[str, Any] | None = None
async def async_step_reauth_confirm(
self, user_input: dict[str, str] | None = None
) -> FlowResult:
"""Handle reauth."""
"""Handle users reauth credentials."""

errors: dict[str, str] | None = None
if user_input and user_input.get(CONF_USERNAME):
self._reauth_username = user_input[CONF_USERNAME]

elif self._reauth_username and user_input and user_input.get(CONF_PASSWORD):
if user_input and self._reauth_username:
data = {
CONF_USERNAME: self._reauth_username,
CONF_PASSWORD: user_input[CONF_PASSWORD],
Expand All @@ -130,7 +106,7 @@ async def async_step_reauth(
return self.async_create_entry(title=self._reauth_username, data=data)

return self.async_show_form(
step_id="reauth",
step_id="reauth_confirm",
description_placeholders={"username": self._reauth_username},
data_schema=vol.Schema(
{
Bre77 marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -139,46 +115,3 @@ async def async_step_reauth(
),
errors=errors,
)

@staticmethod
@callback
def async_get_options_flow(
config_entry: config_entries.ConfigEntry,
) -> config_entries.OptionsFlow:
"""Get the options flow for this handler."""
return OptionsFlowHandler(config_entry)


class OptionsFlowHandler(config_entries.OptionsFlow):
"""Options flow for picking services."""

def __init__(self, config_entry: config_entries.ConfigEntry) -> None:
"""Initialize options flow."""
self.config_entry = config_entry

async def async_step_init(self, user_input=None):
"""Manage the options."""
if user_input is not None:
return self.async_create_entry(title="", data=user_input)

if self.config_entry.state != config_entries.ConfigEntryState.LOADED:
return self.async_abort(reason="unknown")
data = self.hass.data[DOMAIN][self.config_entry.entry_id]
try:
services = await data["client"].get_services()
except AuthenticationException:
return self.async_abort(reason="invalid_auth")
except ClientError:
return self.async_abort(reason="cannot_connect")
service_options = {str(s[SERVICE_ID]): s["description"] for s in services}
return self.async_show_form(
step_id="init",
data_schema=vol.Schema(
{
vol.Required(
CONF_SERVICES,
default=self.config_entry.options.get(CONF_SERVICES),
): cv.multi_select(service_options),
}
),
)
58 changes: 36 additions & 22 deletions homeassistant/components/aussie_broadband/sensor.py
@@ -1,7 +1,9 @@
"""Support for Aussie Broadband metric sensors."""
from __future__ import annotations

from typing import Any
from collections.abc import Callable
from dataclasses import dataclass
from typing import Any, cast

from homeassistant.components.sensor import (
SensorEntity,
Expand All @@ -14,88 +16,101 @@
from homeassistant.helpers.device_registry import DeviceEntryType
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from .const import DOMAIN, SERVICE_ID

SENSOR_DESCRIPTIONS: tuple[SensorEntityDescription, ...] = (

@dataclass
class SensorValueEntityDescription(SensorEntityDescription):
"""Class describing Aussie Broadband sensor entities."""

value: Callable = lambda x: x


SENSOR_DESCRIPTIONS: tuple[SensorValueEntityDescription, ...] = (
# Internet Services sensors
SensorEntityDescription(
SensorValueEntityDescription(
key="usedMb",
name="Data Used",
state_class=SensorStateClass.TOTAL_INCREASING,
native_unit_of_measurement=DATA_MEGABYTES,
icon="mdi:network",
),
SensorEntityDescription(
SensorValueEntityDescription(
key="downloadedMb",
name="Downloaded",
state_class=SensorStateClass.TOTAL_INCREASING,
native_unit_of_measurement=DATA_MEGABYTES,
icon="mdi:download-network",
),
SensorEntityDescription(
SensorValueEntityDescription(
key="uploadedMb",
name="Uploaded",
state_class=SensorStateClass.TOTAL_INCREASING,
native_unit_of_measurement=DATA_MEGABYTES,
icon="mdi:upload-network",
),
# Mobile Phone Services sensors
SensorEntityDescription(
SensorValueEntityDescription(
key="national",
name="National Calls",
state_class=SensorStateClass.TOTAL_INCREASING,
icon="mdi:phone",
value=lambda x: x.get("calls"),
),
SensorEntityDescription(
SensorValueEntityDescription(
key="mobile",
name="Mobile Calls",
state_class=SensorStateClass.TOTAL_INCREASING,
icon="mdi:phone",
value=lambda x: x.get("calls"),
),
SensorEntityDescription(
SensorValueEntityDescription(
key="international",
name="International Calls",
entity_registry_enabled_default=False,
state_class=SensorStateClass.TOTAL_INCREASING,
icon="mdi:phone-plus",
),
SensorEntityDescription(
SensorValueEntityDescription(
key="sms",
name="SMS Sent",
state_class=SensorStateClass.TOTAL_INCREASING,
icon="mdi:message-processing",
value=lambda x: x.get("calls"),
),
SensorEntityDescription(
SensorValueEntityDescription(
key="internet",
name="Data Used",
state_class=SensorStateClass.TOTAL_INCREASING,
native_unit_of_measurement=DATA_KILOBYTES,
icon="mdi:network",
value=lambda x: x.get("kbytes"),
),
SensorEntityDescription(
SensorValueEntityDescription(
key="voicemail",
name="Voicemail Calls",
entity_registry_enabled_default=False,
state_class=SensorStateClass.TOTAL_INCREASING,
icon="mdi:phone",
),
SensorEntityDescription(
SensorValueEntityDescription(
key="other",
name="Other Calls",
entity_registry_enabled_default=False,
state_class=SensorStateClass.TOTAL_INCREASING,
icon="mdi:phone",
),
# Generic sensors
SensorEntityDescription(
SensorValueEntityDescription(
key="daysTotal",
name="Billing Cycle Length",
native_unit_of_measurement=TIME_DAYS,
icon="mdi:calendar-range",
),
SensorEntityDescription(
SensorValueEntityDescription(
key="daysRemaining",
name="Billing Cycle Remaining",
native_unit_of_measurement=TIME_DAYS,
Expand All @@ -122,8 +137,10 @@ async def async_setup_entry(
class AussieBroadandSensorEntity(CoordinatorEntity, SensorEntity):
"""Base class for Aussie Broadband metric sensors."""

entity_description: SensorValueEntityDescription

def __init__(
Bre77 marked this conversation as resolved.
Show resolved Hide resolved
self, service: dict[str, Any], description: SensorEntityDescription
self, service: dict[str, Any], description: SensorValueEntityDescription
) -> None:
"""Initialize the sensor."""
super().__init__(service["coordinator"])
Expand All @@ -134,16 +151,13 @@ def __init__(
entry_type=DeviceEntryType.SERVICE,
identifiers={(DOMAIN, service[SERVICE_ID])},
manufacturer="Aussie Broadband",
configuration_url=f"https://my.aussiebroadband.com.au/#/{service['name']}/{service[SERVICE_ID]}/",
configuration_url=f"https://my.aussiebroadband.com.au/#/{service['name'].lower()}/{service[SERVICE_ID]}/",
name=service["description"],
model=service["name"],
)

@property
def native_value(self):
def native_value(self) -> StateType:
"""Return the state of the sensor."""
if self.entity_description.key == "internet":
return self.coordinator.data[self.entity_description.key].get("kbytes")
if self.entity_description.key in ("national", "mobile", "sms"):
return self.coordinator.data[self.entity_description.key].get("calls")
return self.coordinator.data[self.entity_description.key]
parent = self.coordinator.data[self.entity_description.key]
return cast(StateType, self.entity_description.value(parent))
2 changes: 1 addition & 1 deletion homeassistant/components/aussie_broadband/strings.json
Expand Up @@ -13,7 +13,7 @@
"services": "Services"
}
},
"reauth": {
"reauth_confirm": {
"title": "[%key:common::config_flow::title::reauth%]",
"description": "Update password for {username}",
"data": {
Expand Down
Expand Up @@ -11,7 +11,7 @@
"unknown": "Unexpected error"
},
"step": {
"reauth": {
"reauth_confirm": {
"data": {
"password": "Password"
},
Expand Down