Skip to content

Commit

Permalink
Merge pull request #302 from petretiandrea/refactor/minor-improvement…
Browse files Browse the repository at this point in the history
…s-and-setup

refactor: setup and minor improvements
  • Loading branch information
petretiandrea authored Dec 30, 2022
2 parents dfe652d + 3b096ac commit 75c21c5
Show file tree
Hide file tree
Showing 17 changed files with 367 additions and 359 deletions.
4 changes: 2 additions & 2 deletions custom_components/tapo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
hass.config_entries.async_forward_entry_setup(entry, component)
)
return True
except:
raise ConfigEntryNotReady
except Exception as error:
raise ConfigEntryNotReady from error


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
Expand Down
11 changes: 5 additions & 6 deletions custom_components/tapo/binary_sensor.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from custom_components.tapo.common_setup import TapoCoordinator

from custom_components.tapo.common_setup import TapoUpdateCoordinator
from custom_components.tapo.tapo_sensor_entity import (
TapoOverheatSensor,
)
from custom_components.tapo.const import (
DOMAIN,
)
from custom_components.tapo.sensor import TapoSensor
from custom_components.tapo.sensors import OverheatSensorSource


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_devices):
# get tapo helper
coordinator: TapoUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
sensors = [TapoOverheatSensor(coordinator)]
coordinator: TapoCoordinator = hass.data[DOMAIN][entry.entry_id]
sensors = [TapoSensor(coordinator, OverheatSensorSource())]
async_add_devices(sensors, True)
60 changes: 35 additions & 25 deletions custom_components/tapo/common_setup.py
Original file line number Diff line number Diff line change
@@ -1,64 +1,72 @@
import async_timeout
import logging
from typing import Dict, Any
from datetime import timedelta
from dataclasses import dataclass
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import logging
import async_timeout
from plugp100 import TapoApiClient, TapoApiClientConfig, TapoDeviceState
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from homeassistant.helpers.update_coordinator import UpdateFailed
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.debounce import Debouncer
from custom_components.tapo.const import (
DOMAIN,
PLATFORMS,
CONF_HOST,
CONF_USERNAME,
CONF_PASSWORD,
)


_LOGGGER = logging.getLogger(__name__)


async def setup_tapo_coordinator_from_dictionary(
hass: HomeAssistant, entry: Dict[str, Any]
) -> "TapoUpdateCoordinator":
) -> "TapoCoordinator":
return await setup_tapo_coordinator(
hass,
entry.get(CONF_HOST),
entry.get(CONF_USERNAME),
entry.get(CONF_PASSWORD),
"",
)


async def setup_tapo_coordinator_from_config_entry(
hass: HomeAssistant, entry: ConfigEntry
) -> "TapoUpdateCoordinator":
) -> "TapoCoordinator":
return await setup_tapo_coordinator(
hass,
entry.data.get(CONF_HOST),
entry.data.get(CONF_USERNAME),
entry.data.get(CONF_PASSWORD),
entry.unique_id,
)


async def setup_tapo_coordinator(
hass: HomeAssistant, host: str, username: str, password: str
) -> "TapoUpdateCoordinator":
session = async_get_clientsession(hass)
config = TapoApiClientConfig(host, username, password, session)
client = TapoApiClient.from_config(config)

coordinator = TapoUpdateCoordinator(hass, client=client)
await coordinator.async_config_entry_first_refresh()

if not coordinator.last_update_success:
raise Exception("Failed to retrieve first tapo data")
hass: HomeAssistant, host: str, username: str, password: str, unique_id: str
) -> "TapoCoordinator":
api = (
hass.data[DOMAIN][f"{unique_id}_api"]
if f"{unique_id}_api" in hass.data[DOMAIN]
else None
)
if api is not None:
_LOGGGER.debug("Re-using setup API to create a coordinator")
coordinator = TapoCoordinator(hass, client=api)
else:
_LOGGGER.debug("Creating new API to create a coordinator")
session = async_get_clientsession(hass)
config = TapoApiClientConfig(host, username, password, session)
client = TapoApiClient.from_config(config)
coordinator = TapoCoordinator(hass, client=client)

try:
await coordinator.async_config_entry_first_refresh()
except ConfigEntryNotReady as error:
_LOGGGER.error("Failed to setup %s", str(error))
raise error

return coordinator

Expand All @@ -67,7 +75,7 @@ async def setup_tapo_coordinator(
DEBOUNCER_COOLDOWN = 2


class TapoUpdateCoordinator(DataUpdateCoordinator[TapoDeviceState]):
class TapoCoordinator(DataUpdateCoordinator[TapoDeviceState]):
def __init__(self, hass: HomeAssistant, client: TapoApiClient):
self.api = client
debouncer = Debouncer(
Expand All @@ -90,12 +98,14 @@ async def _async_update_data(self):
async with async_timeout.timeout(10):
return await self._update_with_fallback()
except Exception as exception:
raise UpdateFailed() from exception
raise UpdateFailed(
f"Error communication with API: {exception}"
) from exception

async def _update_with_fallback(self, retry=True):
try:
return await self.api.get_state()
except Exception as error:
except Exception: # pylint: disable=broad-except
if retry:
await self.api.login()
return await self._update_with_fallback(False)
71 changes: 36 additions & 35 deletions custom_components/tapo/config_flow.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Config flow for tapo integration."""
import logging
import re
from typing import Any

import voluptuous as vol

Expand All @@ -19,7 +19,7 @@

_LOGGER = logging.getLogger(__name__)

# TODO adjust the data schema to the data that you need

STEP_USER_DATA_SCHEMA = vol.Schema(
{
vol.Required(
Expand All @@ -40,7 +40,9 @@ class TapoConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
VERSION = 1
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL

async def async_step_user(self, user_input=None):
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> data_entry_flow.FlowResult:
"""Handle the initial step."""
if user_input is None:
return self.async_show_form(
Expand All @@ -51,54 +53,53 @@ async def async_step_user(self, user_input=None):
errors = {}

try:
entry_metadata = await self._validate_input(user_input)
# check if the same device has already been configured
await self.async_set_unique_id(entry_metadata["unique_id"])
if not user_input[CONF_HOST]:
raise InvalidHost
api = await self._try_setup_api(user_input)
unique_data = await self._get_unique_data_from_api(api)
unique_id = unique_data["unique_id"]
await self.async_set_unique_id(unique_id)
self._abort_if_unique_id_configured()
except CannotConnect:
self.hass.data[DOMAIN][f"{unique_id}_api"] = api
except CannotConnect as error:
errors["base"] = "cannot_connect"
except InvalidAuth:
_LOGGER.error("Failed to setup %s", str(error))
except InvalidAuth as error:
errors["base"] = "invalid_auth"
except InvalidHost:
_LOGGER.error("Failed to setup %s", str(error))
except InvalidHost as error:
errors["base"] = "invalid_hostname"
_LOGGER.error("Failed to setup %s", str(error))
except data_entry_flow.AbortFlow:
return self.async_abort(reason="already_configured")
except Exception: # pylint: disable=broad-except
_LOGGER.exception("Unexpected exception")
except Exception as error: # pylint: disable=broad-except
errors["base"] = "unknown"
_LOGGER.error("Failed to setup %s", str(error))
else:
return self.async_create_entry(
title=entry_metadata["title"], data=user_input
)
return self.async_create_entry(title=unique_data["title"], data=user_input)

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

async def _validate_input(self, data):
"""Validate the user input allows us to connect.
Data has the keys from STEP_USER_DATA_SCHEMA with values provided by the user.
"""

if not data[CONF_HOST]:
raise InvalidHost

tapo_api = await self._test_credentials(
data[CONF_HOST], data[CONF_USERNAME], data[CONF_PASSWORD]
)

state = await tapo_api.get_state()
if not state:
raise CannotConnect

# Return info that you want to store in the config entry.
return {"title": state.nickname, "unique_id": state.device_id}
async def _get_unique_data_from_api(self, api: TapoApiClient) -> dict[str, Any]:
try:
state = await api.get_state()
return {"title": state.nickname, "unique_id": state.device_id}
except Exception as error:
raise CannotConnect from error

async def _test_credentials(self, address, username, password) -> TapoApiClient:
async def _try_setup_api(
self, user_input: dict[str, Any] | None = None
) -> TapoApiClient:
try:
session = async_create_clientsession(self.hass)
config = TapoApiClientConfig(address, username, password, session)
config = TapoApiClientConfig(
user_input[CONF_HOST],
user_input[CONF_USERNAME],
user_input[CONF_PASSWORD],
session,
)
client = TapoApiClient.from_config(config)
await client.login()
return client
Expand Down
22 changes: 9 additions & 13 deletions custom_components/tapo/const.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
"""Constants for the tapo integration."""

from homeassistant.components.light import (
SUPPORT_BRIGHTNESS,
SUPPORT_COLOR,
SUPPORT_COLOR_TEMP,
)
from homeassistant.components.light import ColorMode

NAME = "tapo"
DOMAIN = "tapo"
Expand All @@ -13,14 +9,14 @@
SUPPORTED_DEVICE_AS_SWITCH = ["p100", "p105", "p110", "p115"]
SUPPORTED_DEVICE_AS_SWITCH_POWER_MONITOR = ["p110", "p115"]
SUPPORTED_DEVICE_AS_LIGHT = {
"l920": SUPPORT_BRIGHTNESS | SUPPORT_COLOR,
"l930": SUPPORT_BRIGHTNESS | SUPPORT_COLOR,
"l900": SUPPORT_BRIGHTNESS | SUPPORT_COLOR,
"l630": SUPPORT_BRIGHTNESS | SUPPORT_COLOR_TEMP | SUPPORT_COLOR,
"l530": SUPPORT_BRIGHTNESS | SUPPORT_COLOR_TEMP | SUPPORT_COLOR,
"l520": SUPPORT_BRIGHTNESS,
"l510": SUPPORT_BRIGHTNESS,
"l610": SUPPORT_BRIGHTNESS,
"l920": [ColorMode.ONOFF, ColorMode.BRIGHTNESS, ColorMode.HS],
"l930": [ColorMode.ONOFF, ColorMode.BRIGHTNESS, ColorMode.HS],
"l900": [ColorMode.ONOFF, ColorMode.BRIGHTNESS, ColorMode.HS],
"l630": [ColorMode.ONOFF, ColorMode.BRIGHTNESS, ColorMode.COLOR_TEMP, ColorMode.HS],
"l530": [ColorMode.ONOFF, ColorMode.BRIGHTNESS, ColorMode.COLOR_TEMP, ColorMode.HS],
"l520": [ColorMode.ONOFF, ColorMode.BRIGHTNESS],
"l510": [ColorMode.ONOFF, ColorMode.BRIGHTNESS],
"l610": [ColorMode.ONOFF, ColorMode.BRIGHTNESS],
}

ISSUE_URL = "https://github.com/petretiandrea/home-assistant-tapo-p100/issues"
Expand Down
Loading

0 comments on commit 75c21c5

Please sign in to comment.