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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Address late review of SwitchBee #78412

Merged
merged 8 commits into from Sep 16, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 5 additions & 5 deletions homeassistant/components/switchbee/__init__.py
Expand Up @@ -2,15 +2,16 @@

from __future__ import annotations

from aiohttp.client_exceptions import ClientConnectorError
from switchbee.api import CentralUnitAPI, SwitchBeeError

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import PlatformNotReady
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession

from .const import DOMAIN, SCAN_INTERVAL_SEC
from .const import DOMAIN
from .coordinator import SwitchBeeCoordinator

PLATFORMS: list[Platform] = [Platform.SWITCH]
Expand All @@ -27,13 +28,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
api = CentralUnitAPI(central_unit, user, password, websession)
try:
await api.connect()
except SwitchBeeError as exp:
raise PlatformNotReady("Failed to connect to the Central Unit") from exp
except (SwitchBeeError, ClientConnectorError) as exp:
jafar-atili marked this conversation as resolved.
Show resolved Hide resolved
raise ConfigEntryNotReady("Failed to connect to the Central Unit") from exp

coordinator = SwitchBeeCoordinator(
hass,
api,
SCAN_INTERVAL_SEC,
)

await coordinator.async_config_entry_first_refresh()
Expand Down
7 changes: 4 additions & 3 deletions homeassistant/components/switchbee/config_flow.py
Expand Up @@ -4,6 +4,7 @@
import logging
from typing import Any

from aiohttp.client_exceptions import ClientConnectorError
from switchbee.api import CentralUnitAPI, SwitchBeeError
import voluptuous as vol

Expand Down Expand Up @@ -38,12 +39,12 @@ async def validate_input(hass: HomeAssistant, data: dict[str, Any]):
)
try:
await api.connect()
except SwitchBeeError as exp:
except (SwitchBeeError, ClientConnectorError) as exp:
_LOGGER.error(exp)
if "LOGIN_FAILED" in str(exp):
raise InvalidAuth from SwitchBeeError
raise InvalidAuth from exp

raise CannotConnect from SwitchBeeError
raise CannotConnect from exp

return format_mac(api.mac)

Expand Down
33 changes: 11 additions & 22 deletions homeassistant/components/switchbee/coordinator.py
Expand Up @@ -10,7 +10,7 @@
from homeassistant.helpers.device_registry import format_mac
jafar-atili marked this conversation as resolved.
Show resolved Hide resolved
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed

from .const import DOMAIN
from .const import DOMAIN, SCAN_INTERVAL_SEC

_LOGGER = logging.getLogger(__name__)

Expand All @@ -22,44 +22,33 @@ def __init__(
self,
hass: HomeAssistant,
swb_api: CentralUnitAPI,
scan_interval: int,
) -> None:
"""Initialize."""
self._api: CentralUnitAPI = swb_api
self.api: CentralUnitAPI = swb_api
self._reconnect_counts: int = 0
self._mac_addr_fmt: str = format_mac(swb_api.mac)
self.mac_formated: str = format_mac(swb_api.mac)
super().__init__(
hass,
_LOGGER,
name=DOMAIN,
update_interval=timedelta(seconds=scan_interval),
update_interval=timedelta(seconds=SCAN_INTERVAL_SEC),
)

@property
def api(self) -> CentralUnitAPI:
"""Return SwitchBee API object."""
return self._api

@property
def mac_formated(self) -> str:
"""Return formatted MAC address."""
return self._mac_addr_fmt

async def _async_update_data(self) -> dict[int, SwitchBeeBaseDevice]:
"""Update data via library."""

if self._reconnect_counts != self._api.reconnect_count:
self._reconnect_counts = self._api.reconnect_count
if self._reconnect_counts != self.api.reconnect_count:
self._reconnect_counts = self.api.reconnect_count
_LOGGER.debug(
"Central Unit re-connected again due to invalid token, total %i",
self._reconnect_counts,
)

# The devices are loaded once during the config_entry
if not self._api.devices:
if not self.api.devices:
# Try to load the devices from the CU for the first time
try:
await self._api.fetch_configuration(
await self.api.fetch_configuration(
[
DeviceType.Switch,
DeviceType.TimedSwitch,
Expand All @@ -76,10 +65,10 @@ async def _async_update_data(self) -> dict[int, SwitchBeeBaseDevice]:

# Get the state of the devices
try:
await self._api.fetch_states()
await self.api.fetch_states()
except SwitchBeeError as exp:
raise UpdateFailed(
f"Error communicating with API: {exp}"
) from SwitchBeeError
else:
return self._api.devices

return self.api.devices
12 changes: 5 additions & 7 deletions homeassistant/components/switchbee/switch.py
Expand Up @@ -27,7 +27,7 @@ async def async_setup_entry(
coordinator: SwitchBeeCoordinator = hass.data[DOMAIN][entry.entry_id]

async_add_entities(
Device(hass, device, coordinator)
SwitchBeeSwitchEntity(device, coordinator)
for device in coordinator.data.values()
if device.type
in [
Expand All @@ -39,12 +39,11 @@ async def async_setup_entry(
)


class Device(CoordinatorEntity[SwitchBeeCoordinator], SwitchEntity):
class SwitchBeeSwitchEntity(CoordinatorEntity[SwitchBeeCoordinator], SwitchEntity):
"""Representation of an Switchbee switch."""

def __init__(
self,
hass: HomeAssistant,
device: SwitchBeeBaseDevice,
coordinator: SwitchBeeCoordinator,
) -> None:
Expand Down Expand Up @@ -77,7 +76,7 @@ def __init__(
@property
def available(self) -> bool:
"""Return True if entity is available."""
return self._is_online
return self._is_online and self.coordinator.last_update_success
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The last update check is provided by the parent entity.

return self._is_online and super().available


@callback
def _handle_coordinator_update(self) -> None:
Expand All @@ -92,9 +91,9 @@ async def async_refresh_state():
"""Refresh the device state in the Central Unit.

This function addresses issue of a device that came online back but still report
unavialbe state (-1).
unavailable state (-1).
Such device (offline device) will keep reporting unavailable state (-1)
unitl it being controlled by the user (state changed to on/off).
until it has been actuated by the user (state changed to on/off).

With this code we keep trying setting dummy state for the device
in order for it to start reporting its real state back (assuming it came back online)
Expand Down Expand Up @@ -130,7 +129,6 @@ async def async_refresh_state():
)
self._is_online = True

self._is_online = True
# timed power switch state is an integer representing the number of minutes left until it goes off
# regulare switches state is ON/OFF (1/0 respectively)
self._attr_is_on = (
Expand Down
3 changes: 0 additions & 3 deletions tests/components/switchbee/test_config_flow.py
Expand Up @@ -2,8 +2,6 @@
import json
from unittest.mock import patch

import pytest

from homeassistant import config_entries
from homeassistant.components.switchbee.config_flow import SwitchBeeError
from homeassistant.components.switchbee.const import DOMAIN
Expand All @@ -16,7 +14,6 @@
from tests.common import MockConfigEntry, load_fixture


@pytest.fixture(autouse=True)
async def test_form(hass):
"""Test we get the form."""

Expand Down