Improve categorization of features (#904)

This updates the categorization of features and makes the id mandatory for features
This commit is contained in:
Teemu R 2024-05-07 11:13:35 +02:00 committed by GitHub
parent 50b5107f75
commit 55653d0346
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 131 additions and 52 deletions

View File

@ -62,6 +62,8 @@ class Feature:
#: Device instance required for getting and setting values
device: Device
#: Identifier
id: str
#: User-friendly short description
name: str
#: Name of the property that allows accessing the value
@ -99,15 +101,8 @@ class Feature:
#: If set, this property will be used to set *choices*.
choices_getter: str | None = None
#: Identifier
id: str | None = None
def __post_init__(self):
"""Handle late-binding of members."""
# Set id, if unset
if self.id is None:
self.id = self.name.lower().replace(" ", "_")
# Populate minimum & maximum values, if range_getter is given
container = self.container if self.container is not None else self.device
if self.range_getter is not None:

View File

@ -213,6 +213,7 @@ class IotBulb(IotDevice, Bulb):
self._add_feature(
Feature(
device=self,
id="brightness",
name="Brightness",
attribute_getter="brightness",
attribute_setter="set_brightness",
@ -227,6 +228,7 @@ class IotBulb(IotDevice, Bulb):
self._add_feature(
Feature(
device=self,
id="color_temperature",
name="Color temperature",
container=self,
attribute_getter="color_temp",

View File

@ -334,6 +334,7 @@ class IotDevice(Device):
self._add_feature(
Feature(
device=self,
id="rssi",
name="RSSI",
attribute_getter="rssi",
icon="mdi:signal",
@ -344,6 +345,7 @@ class IotDevice(Device):
self._add_feature(
Feature(
device=self,
id="on_since",
name="On since",
attribute_getter="on_since",
icon="mdi:clock",

View File

@ -91,12 +91,15 @@ class IotDimmer(IotPlug):
self._add_feature(
Feature(
device=self,
id="brightness",
name="Brightness",
attribute_getter="brightness",
attribute_setter="set_brightness",
minimum_value=1,
maximum_value=100,
unit="%",
type=Feature.Type.Number,
category=Feature.Category.Primary,
)
)

View File

@ -65,6 +65,7 @@ class IotPlug(IotDevice):
self._add_feature(
Feature(
device=self,
id="led",
name="LED",
icon="mdi:led-{state}",
attribute_getter="led",

View File

@ -22,10 +22,13 @@ class AmbientLight(IotModule):
Feature(
device=device,
container=self,
id="ambient_light",
name="Ambient Light",
icon="mdi:brightness-percent",
attribute_getter="ambientlight_brightness",
type=Feature.Type.Sensor,
category=Feature.Category.Primary,
unit="%",
)
)

View File

@ -30,10 +30,12 @@ class Cloud(IotModule):
Feature(
device=device,
container=self,
id="cloud_connection",
name="Cloud connection",
icon="mdi:cloud",
attribute_getter="is_connected",
type=Feature.Type.BinarySensor,
category=Feature.Category.Info,
)
)

View File

@ -24,6 +24,7 @@ class Emeter(Usage):
unit="W",
id="current_power_w", # for homeassistant backwards compat
precision_hint=1,
category=Feature.Category.Primary,
)
)
self._add_feature(
@ -35,16 +36,19 @@ class Emeter(Usage):
unit="kWh",
id="today_energy_kwh", # for homeassistant backwards compat
precision_hint=3,
category=Feature.Category.Info,
)
)
self._add_feature(
Feature(
device,
id="consumption_this_month",
name="This month's consumption",
attribute_getter="emeter_this_month",
container=self,
unit="kWh",
precision_hint=3,
category=Feature.Category.Info,
)
)
self._add_feature(
@ -56,6 +60,7 @@ class Emeter(Usage):
unit="kWh",
id="total_energy_kwh", # for homeassistant backwards compat
precision_hint=3,
category=Feature.Category.Info,
)
)
self._add_feature(
@ -67,6 +72,7 @@ class Emeter(Usage):
unit="V",
id="voltage", # for homeassistant backwards compat
precision_hint=1,
category=Feature.Category.Primary,
)
)
self._add_feature(
@ -78,6 +84,7 @@ class Emeter(Usage):
unit="A",
id="current_a", # for homeassistant backwards compat
precision_hint=2,
category=Feature.Category.Primary,
)
)

View File

@ -53,14 +53,10 @@ class Module(ABC):
def _add_feature(self, feature: Feature):
"""Add module feature."""
def _slugified_name(name):
return name.lower().replace(" ", "_").replace("'", "_")
feat_name = _slugified_name(feature.name)
if feat_name in self._module_features:
raise KasaException("Duplicate name detected %s" % feat_name)
self._module_features[feat_name] = feature
id_ = feature.id
if id_ in self._module_features:
raise KasaException("Duplicate id detected %s" % id_)
self._module_features[id_] = feature
def __repr__(self) -> str:
return (

View File

@ -27,7 +27,8 @@ class AlarmModule(SmartModule):
self._add_feature(
Feature(
device,
"Alarm",
id="alarm",
name="Alarm",
container=self,
attribute_getter="active",
icon="mdi:bell",
@ -37,7 +38,8 @@ class AlarmModule(SmartModule):
self._add_feature(
Feature(
device,
"Alarm source",
id="alarm_source",
name="Alarm source",
container=self,
attribute_getter="source",
icon="mdi:bell",
@ -46,7 +48,8 @@ class AlarmModule(SmartModule):
self._add_feature(
Feature(
device,
"Alarm sound",
id="alarm_sound",
name="Alarm sound",
container=self,
attribute_getter="alarm_sound",
attribute_setter="set_alarm_sound",
@ -58,7 +61,8 @@ class AlarmModule(SmartModule):
self._add_feature(
Feature(
device,
"Alarm volume",
id="alarm_volume",
name="Alarm volume",
container=self,
attribute_getter="alarm_volume",
attribute_setter="set_alarm_volume",
@ -70,7 +74,8 @@ class AlarmModule(SmartModule):
self._add_feature(
Feature(
device,
"Test alarm",
id="test_alarm",
name="Test alarm",
container=self,
attribute_setter="play",
type=Feature.Type.Action,
@ -79,7 +84,8 @@ class AlarmModule(SmartModule):
self._add_feature(
Feature(
device,
"Stop alarm",
id="stop_alarm",
name="Stop alarm",
container=self,
attribute_setter="stop",
type=Feature.Type.Action,

View File

@ -23,7 +23,8 @@ class AutoOffModule(SmartModule):
self._add_feature(
Feature(
device,
"Auto off enabled",
id="auto_off_enabled",
name="Auto off enabled",
container=self,
attribute_getter="enabled",
attribute_setter="set_enabled",
@ -33,7 +34,8 @@ class AutoOffModule(SmartModule):
self._add_feature(
Feature(
device,
"Auto off minutes",
id="auto_off_minutes",
name="Auto off minutes",
container=self,
attribute_getter="delay",
attribute_setter="set_delay",
@ -42,7 +44,12 @@ class AutoOffModule(SmartModule):
)
self._add_feature(
Feature(
device, "Auto off at", container=self, attribute_getter="auto_off_at"
device,
id="auto_off_at",
name="Auto off at",
container=self,
attribute_getter="auto_off_at",
category=Feature.Category.Info,
)
)

View File

@ -22,20 +22,25 @@ class BatterySensor(SmartModule):
self._add_feature(
Feature(
device,
"battery_level",
"Battery level",
container=self,
attribute_getter="battery",
icon="mdi:battery",
unit="%",
category=Feature.Category.Info,
)
)
self._add_feature(
Feature(
device,
"battery_low",
"Battery low",
container=self,
attribute_getter="battery_low",
icon="mdi:alert",
type=Feature.Type.BinarySensor,
category=Feature.Category.Debug,
)
)

View File

@ -25,7 +25,8 @@ class Brightness(SmartModule):
self._add_feature(
Feature(
device,
"Brightness",
id="brightness",
name="Brightness",
container=self,
attribute_getter="brightness",
attribute_setter="set_brightness",

View File

@ -24,11 +24,13 @@ class CloudModule(SmartModule):
self._add_feature(
Feature(
device,
"Cloud connection",
id="cloud_connection",
name="Cloud connection",
container=self,
attribute_getter="is_connected",
icon="mdi:cloud",
type=Feature.Type.BinarySensor,
category=Feature.Category.Info,
)
)

View File

@ -22,6 +22,7 @@ class ColorModule(SmartModule):
self._add_feature(
Feature(
device,
"hsv",
"HSV",
container=self,
attribute_getter="hsv",

View File

@ -28,6 +28,7 @@ class ColorTemperatureModule(SmartModule):
self._add_feature(
Feature(
device,
"color_temperature",
"Color temperature",
container=self,
attribute_getter="color_temp",

View File

@ -22,31 +22,37 @@ class EnergyModule(SmartModule):
self._add_feature(
Feature(
device,
"consumption_current",
name="Current consumption",
attribute_getter="current_power",
container=self,
unit="W",
precision_hint=1,
category=Feature.Category.Primary,
)
)
self._add_feature(
Feature(
device,
"consumption_today",
name="Today's consumption",
attribute_getter="emeter_today",
container=self,
unit="Wh",
precision_hint=2,
category=Feature.Category.Info,
)
)
self._add_feature(
Feature(
device,
"consumption_this_month",
name="This month's consumption",
attribute_getter="emeter_this_month",
container=self,
unit="Wh",
precision_hint=2,
category=Feature.Category.Info,
)
)

View File

@ -22,7 +22,8 @@ class FanModule(SmartModule):
self._add_feature(
Feature(
device,
"Fan speed level",
id="fan_speed_level",
name="Fan speed level",
container=self,
attribute_getter="fan_speed_level",
attribute_setter="set_fan_speed_level",
@ -36,7 +37,8 @@ class FanModule(SmartModule):
self._add_feature(
Feature(
device,
"Fan sleep mode",
id="fan_sleep_mode",
name="Fan sleep mode",
container=self,
attribute_getter="sleep_mode",
attribute_setter="set_sleep_mode",

View File

@ -52,7 +52,8 @@ class Firmware(SmartModule):
self._add_feature(
Feature(
device,
"Auto update enabled",
id="auto_update_enabled",
name="Auto update enabled",
container=self,
attribute_getter="auto_update_enabled",
attribute_setter="set_auto_update_enabled",
@ -62,10 +63,12 @@ class Firmware(SmartModule):
self._add_feature(
Feature(
device,
"Update available",
id="update_available",
name="Update available",
container=self,
attribute_getter="update_available",
type=Feature.Type.BinarySensor,
category=Feature.Category.Info,
)
)

View File

@ -27,6 +27,7 @@ class FrostProtectionModule(SmartModule):
self._add_feature(
Feature(
device,
"frost_protection_enabled",
name="Frost protection enabled",
container=self,
attribute_getter="enabled",

View File

@ -22,21 +22,25 @@ class HumiditySensor(SmartModule):
self._add_feature(
Feature(
device,
"Humidity",
id="humidity",
name="Humidity",
container=self,
attribute_getter="humidity",
icon="mdi:water-percent",
unit="%",
category=Feature.Category.Primary,
)
)
self._add_feature(
Feature(
device,
"Humidity warning",
id="humidity_warning",
name="Humidity warning",
container=self,
attribute_getter="humidity_warning",
type=Feature.Type.BinarySensor,
icon="mdi:alert",
category=Feature.Category.Debug,
)
)

View File

@ -23,6 +23,7 @@ class LedModule(SmartModule):
Feature(
device=device,
container=self,
id="led",
name="LED",
icon="mdi:led-{state}",
attribute_getter="led",

View File

@ -34,7 +34,8 @@ class LightEffectModule(SmartModule):
self._add_feature(
Feature(
device,
"Light effect",
id="light_effect",
name="Light effect",
container=self,
attribute_getter="effect",
attribute_setter="set_effect",

View File

@ -31,6 +31,7 @@ class LightTransitionModule(SmartModule):
Feature(
device=self._device,
container=self,
id="smooth_transitions",
name="Smooth transitions",
icon=icon,
attribute_getter="enabled_v1",
@ -46,7 +47,8 @@ class LightTransitionModule(SmartModule):
self._add_feature(
Feature(
self._device,
"Smooth transition on",
id="smooth_transition_on",
name="Smooth transition on",
container=self,
attribute_getter="turn_on_transition",
attribute_setter="set_turn_on_transition",
@ -58,7 +60,8 @@ class LightTransitionModule(SmartModule):
self._add_feature(
Feature(
self._device,
"Smooth transition off",
id="smooth_transition_off",
name="Smooth transition off",
container=self,
attribute_getter="turn_off_transition",
attribute_setter="set_turn_off_transition",

View File

@ -22,7 +22,8 @@ class ReportModule(SmartModule):
self._add_feature(
Feature(
device,
"Report interval",
id="report_interval",
name="Report interval",
container=self,
attribute_getter="report_interval",
category=Feature.Category.Debug,

View File

@ -22,7 +22,8 @@ class TemperatureSensor(SmartModule):
self._add_feature(
Feature(
device,
"Temperature",
id="temperature",
name="Temperature",
container=self,
attribute_getter="temperature",
icon="mdi:thermometer",
@ -33,17 +34,20 @@ class TemperatureSensor(SmartModule):
self._add_feature(
Feature(
device,
"Temperature warning",
id="temperature_warning",
name="Temperature warning",
container=self,
attribute_getter="temperature_warning",
type=Feature.Type.BinarySensor,
icon="mdi:alert",
category=Feature.Category.Debug,
)
)
self._add_feature(
Feature(
device,
"Temperature unit",
id="temperature_unit",
name="Temperature unit",
container=self,
attribute_getter="temperature_unit",
attribute_setter="set_temperature_unit",

View File

@ -36,7 +36,8 @@ class TemperatureControl(SmartModule):
self._add_feature(
Feature(
device,
"Target temperature",
id="target_temperature",
name="Target temperature",
container=self,
attribute_getter="target_temperature",
attribute_setter="set_target_temperature",
@ -50,7 +51,8 @@ class TemperatureControl(SmartModule):
self._add_feature(
Feature(
device,
"Temperature offset",
id="temperature_offset",
name="Temperature offset",
container=self,
attribute_getter="temperature_offset",
attribute_setter="set_temperature_offset",
@ -64,7 +66,8 @@ class TemperatureControl(SmartModule):
self._add_feature(
Feature(
device,
"State",
id="state",
name="State",
container=self,
attribute_getter="state",
attribute_setter="set_state",
@ -76,7 +79,8 @@ class TemperatureControl(SmartModule):
self._add_feature(
Feature(
device,
"Mode",
id="mode",
name="Mode",
container=self,
attribute_getter="mode",
category=Feature.Category.Primary,

View File

@ -25,6 +25,7 @@ class TimeModule(SmartModule):
self._add_feature(
Feature(
device=device,
id="time",
name="Time",
attribute_getter="time",
container=self,

View File

@ -30,19 +30,23 @@ class WaterleakSensor(SmartModule):
self._add_feature(
Feature(
device,
"Water leak",
id="water_leak",
name="Water leak",
container=self,
attribute_getter="status",
icon="mdi:water",
category=Feature.Category.Debug,
)
)
self._add_feature(
Feature(
device,
"Water alert",
id="water_alert",
name="Water alert",
container=self,
attribute_getter="alert",
icon="mdi:water-alert",
category=Feature.Category.Primary,
)
)

View File

@ -225,7 +225,8 @@ class SmartDevice(Bulb, Fan, Device):
self._add_feature(
Feature(
self,
"Device ID",
id="device_id",
name="Device ID",
attribute_getter="device_id",
category=Feature.Category.Debug,
)
@ -234,7 +235,8 @@ class SmartDevice(Bulb, Fan, Device):
self._add_feature(
Feature(
self,
"State",
id="state",
name="State",
attribute_getter="is_on",
attribute_setter="set_state",
type=Feature.Type.Switch,
@ -246,7 +248,8 @@ class SmartDevice(Bulb, Fan, Device):
self._add_feature(
Feature(
self,
"Signal Level",
id="signal_level",
name="Signal Level",
attribute_getter=lambda x: x._info["signal_level"],
icon="mdi:signal",
category=Feature.Category.Info,
@ -257,7 +260,8 @@ class SmartDevice(Bulb, Fan, Device):
self._add_feature(
Feature(
self,
"RSSI",
id="rssi",
name="RSSI",
attribute_getter=lambda x: x._info["rssi"],
icon="mdi:signal",
category=Feature.Category.Debug,
@ -268,6 +272,7 @@ class SmartDevice(Bulb, Fan, Device):
self._add_feature(
Feature(
device=self,
id="ssid",
name="SSID",
attribute_getter="ssid",
icon="mdi:wifi",
@ -279,11 +284,12 @@ class SmartDevice(Bulb, Fan, Device):
self._add_feature(
Feature(
self,
"Overheated",
id="overheated",
name="Overheated",
attribute_getter=lambda x: x._info["overheated"],
icon="mdi:heat-wave",
type=Feature.Type.BinarySensor,
category=Feature.Category.Debug,
category=Feature.Category.Info,
)
)
@ -293,10 +299,11 @@ class SmartDevice(Bulb, Fan, Device):
self._add_feature(
Feature(
device=self,
id="on_since",
name="On since",
attribute_getter="on_since",
icon="mdi:clock",
category=Feature.Category.Debug,
category=Feature.Category.Info,
)
)

View File

@ -19,6 +19,7 @@ def dummy_feature() -> Feature:
feat = Feature(
device=DummyDevice(), # type: ignore[arg-type]
id="dummy_feature",
name="dummy_feature",
attribute_getter="dummygetter",
attribute_setter="dummysetter",
@ -47,6 +48,7 @@ def test_feature_missing_type():
with pytest.raises(ValueError):
Feature(
device=DummyDevice(), # type: ignore[arg-type]
id="dummy_error",
name="dummy error",
attribute_getter="dummygetter",
attribute_setter="dummysetter",
@ -104,6 +106,7 @@ async def test_feature_action(mocker):
"""Test that setting value on button calls the setter."""
feat = Feature(
device=DummyDevice(), # type: ignore[arg-type]
id="dummy_feature",
name="dummy_feature",
attribute_setter="call_action",
container=None,