tplink: forward compatible typing and test changes for kasa 0.8 (#131623)

pull/131249/head^2
Steven B. 2024-11-26 19:50:26 +00:00 committed by GitHub
parent 2edcda47b0
commit f3964596de
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 41 additions and 27 deletions

View File

@ -148,7 +148,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: TPLinkConfigEntry) -> bo
if conn_params_dict := entry.data.get(CONF_CONNECTION_PARAMETERS):
try:
conn_params = Device.ConnectionParameters.from_dict(conn_params_dict)
except KasaException:
except (KasaException, TypeError, ValueError, LookupError):
_LOGGER.warning(
"Invalid connection parameters dict for %s: %s", host, conn_params_dict
)

View File

@ -3,7 +3,7 @@
from __future__ import annotations
from dataclasses import dataclass
from typing import Final
from typing import Final, cast
from kasa import Feature
@ -98,4 +98,4 @@ class TPLinkBinarySensorEntity(CoordinatedTPLinkFeatureEntity, BinarySensorEntit
@callback
def _async_update_attrs(self) -> None:
"""Update the entity's attributes."""
self._attr_is_on = self._feature.value
self._attr_is_on = cast(bool | None, self._feature.value)

View File

@ -116,8 +116,8 @@ class TPLinkClimateEntity(CoordinatedTPLinkEntity, ClimateEntity):
@callback
def _async_update_attrs(self) -> None:
"""Update the entity's attributes."""
self._attr_current_temperature = self._temp_feature.value
self._attr_target_temperature = self._target_feature.value
self._attr_current_temperature = cast(float | None, self._temp_feature.value)
self._attr_target_temperature = cast(float | None, self._target_feature.value)
self._attr_hvac_mode = (
HVACMode.HEAT if self._state_feature.value else HVACMode.OFF
@ -134,7 +134,9 @@ class TPLinkClimateEntity(CoordinatedTPLinkEntity, ClimateEntity):
self._attr_hvac_action = HVACAction.OFF
return
self._attr_hvac_action = STATE_TO_ACTION[self._mode_feature.value]
self._attr_hvac_action = STATE_TO_ACTION[
cast(ThermostatState, self._mode_feature.value)
]
def _get_unique_id(self) -> str:
"""Return unique id."""

View File

@ -4,7 +4,7 @@ from __future__ import annotations
from dataclasses import dataclass
import logging
from typing import Final
from typing import Final, cast
from kasa import Device, Feature
@ -108,4 +108,4 @@ class TPLinkNumberEntity(CoordinatedTPLinkFeatureEntity, NumberEntity):
@callback
def _async_update_attrs(self) -> None:
"""Update the entity's attributes."""
self._attr_native_value = self._feature.value
self._attr_native_value = cast(float | None, self._feature.value)

View File

@ -93,4 +93,4 @@ class TPLinkSelectEntity(CoordinatedTPLinkFeatureEntity, SelectEntity):
@callback
def _async_update_attrs(self) -> None:
"""Update the entity's attributes."""
self._attr_current_option = self._feature.value
self._attr_current_option = cast(str | None, self._feature.value)

View File

@ -3,7 +3,7 @@
from __future__ import annotations
from dataclasses import dataclass
from typing import cast
from typing import TYPE_CHECKING, cast
from kasa import Feature
@ -161,6 +161,12 @@ class TPLinkSensorEntity(CoordinatedTPLinkFeatureEntity, SensorEntity):
# We probably do not need this, when we are rounding already?
self._attr_suggested_display_precision = self._feature.precision_hint
if TYPE_CHECKING:
# pylint: disable-next=import-outside-toplevel
from datetime import date, datetime
assert isinstance(value, str | int | float | date | datetime | None)
self._attr_native_value = value
# Map to homeassistant units and fallback to upstream one if none found
if (unit := self._feature.unit) is not None:

View File

@ -4,7 +4,7 @@ from __future__ import annotations
from dataclasses import dataclass
import logging
from typing import Any
from typing import Any, cast
from kasa import Feature
@ -99,4 +99,4 @@ class TPLinkSwitch(CoordinatedTPLinkFeatureEntity, SwitchEntity):
@callback
def _async_update_attrs(self) -> None:
"""Update the entity's attributes."""
self._attr_is_on = self._feature.value
self._attr_is_on = cast(bool | None, self._feature.value)

View File

@ -6,6 +6,7 @@ from typing import Any
from unittest.mock import AsyncMock, MagicMock, patch
from kasa import (
BaseProtocol,
Device,
DeviceConfig,
DeviceConnectionParameters,
@ -17,7 +18,6 @@ from kasa import (
Module,
)
from kasa.interfaces import Fan, Light, LightEffect, LightState
from kasa.protocol import BaseProtocol
from kasa.smart.modules.alarm import Alarm
from syrupy import SnapshotAssertion
@ -62,7 +62,9 @@ CONN_PARAMS_LEGACY = DeviceConnectionParameters(
DeviceFamily.IotSmartPlugSwitch, DeviceEncryptionType.Xor
)
DEVICE_CONFIG_LEGACY = DeviceConfig(IP_ADDRESS)
DEVICE_CONFIG_DICT_LEGACY = DEVICE_CONFIG_LEGACY.to_dict(exclude_credentials=True)
DEVICE_CONFIG_DICT_LEGACY = {
k: v for k, v in DEVICE_CONFIG_LEGACY.to_dict().items() if k != "credentials"
}
CREDENTIALS = Credentials("foo", "bar")
CREDENTIALS_HASH_AES = "AES/abcdefghijklmnopqrstuvabcdefghijklmnopqrstuv=="
CREDENTIALS_HASH_KLAP = "KLAP/abcdefghijklmnopqrstuv=="
@ -86,8 +88,12 @@ DEVICE_CONFIG_AES = DeviceConfig(
uses_http=True,
aes_keys=AES_KEYS,
)
DEVICE_CONFIG_DICT_KLAP = DEVICE_CONFIG_KLAP.to_dict(exclude_credentials=True)
DEVICE_CONFIG_DICT_AES = DEVICE_CONFIG_AES.to_dict(exclude_credentials=True)
DEVICE_CONFIG_DICT_KLAP = {
k: v for k, v in DEVICE_CONFIG_KLAP.to_dict().items() if k != "credentials"
}
DEVICE_CONFIG_DICT_AES = {
k: v for k, v in DEVICE_CONFIG_AES.to_dict().items() if k != "credentials"
}
CREATE_ENTRY_DATA_LEGACY = {
CONF_HOST: IP_ADDRESS,
CONF_ALIAS: ALIAS,

View File

@ -45,6 +45,7 @@ from . import (
CREDENTIALS_HASH_AES,
CREDENTIALS_HASH_KLAP,
DEVICE_CONFIG_AES,
DEVICE_CONFIG_DICT_KLAP,
DEVICE_CONFIG_KLAP,
DEVICE_CONFIG_LEGACY,
DEVICE_ID,
@ -538,9 +539,8 @@ async def test_move_credentials_hash(
from the device.
"""
device_config = {
**DEVICE_CONFIG_KLAP.to_dict(
exclude_credentials=True, credentials_hash="theHash"
)
**DEVICE_CONFIG_DICT_KLAP,
"credentials_hash": "theHash",
}
entry_data = {**CREATE_ENTRY_DATA_KLAP, CONF_DEVICE_CONFIG: device_config}
@ -586,9 +586,8 @@ async def test_move_credentials_hash_auth_error(
in async_setup_entry.
"""
device_config = {
**DEVICE_CONFIG_KLAP.to_dict(
exclude_credentials=True, credentials_hash="theHash"
)
**DEVICE_CONFIG_DICT_KLAP,
"credentials_hash": "theHash",
}
entry_data = {**CREATE_ENTRY_DATA_KLAP, CONF_DEVICE_CONFIG: device_config}
@ -630,9 +629,8 @@ async def test_move_credentials_hash_other_error(
at the end of the test.
"""
device_config = {
**DEVICE_CONFIG_KLAP.to_dict(
exclude_credentials=True, credentials_hash="theHash"
)
**DEVICE_CONFIG_DICT_KLAP,
"credentials_hash": "theHash",
}
entry_data = {**CREATE_ENTRY_DATA_KLAP, CONF_DEVICE_CONFIG: device_config}
@ -729,7 +727,7 @@ async def test_credentials_hash_auth_error(
await hass.async_block_till_done()
expected_config = DeviceConfig.from_dict(
DEVICE_CONFIG_KLAP.to_dict(exclude_credentials=True, credentials_hash="theHash")
{**DEVICE_CONFIG_DICT_KLAP, "credentials_hash": "theHash"}
)
expected_config.uses_http = False
expected_config.http_client = "Foo"
@ -767,7 +765,9 @@ async def test_migrate_remove_device_config(
CONF_HOST: expected_entry_data[CONF_HOST],
CONF_ALIAS: ALIAS,
CONF_MODEL: MODEL,
CONF_DEVICE_CONFIG: device_config.to_dict(exclude_credentials=True),
CONF_DEVICE_CONFIG: {
k: v for k, v in device_config.to_dict().items() if k != "credentials"
},
}
entry = MockConfigEntry(