diff --git a/kasa/feature.py b/kasa/feature.py index 02c78b20..1f7d3f3d 100644 --- a/kasa/feature.py +++ b/kasa/feature.py @@ -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: diff --git a/kasa/iot/iotbulb.py b/kasa/iot/iotbulb.py index d9456e96..6819d94b 100644 --- a/kasa/iot/iotbulb.py +++ b/kasa/iot/iotbulb.py @@ -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", diff --git a/kasa/iot/iotdevice.py b/kasa/iot/iotdevice.py index e69de80c..29ba3155 100755 --- a/kasa/iot/iotdevice.py +++ b/kasa/iot/iotdevice.py @@ -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", diff --git a/kasa/iot/iotdimmer.py b/kasa/iot/iotdimmer.py index 672b2265..cfe937b8 100644 --- a/kasa/iot/iotdimmer.py +++ b/kasa/iot/iotdimmer.py @@ -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, ) ) diff --git a/kasa/iot/iotplug.py b/kasa/iot/iotplug.py index ecf73e03..dadb38f2 100644 --- a/kasa/iot/iotplug.py +++ b/kasa/iot/iotplug.py @@ -65,6 +65,7 @@ class IotPlug(IotDevice): self._add_feature( Feature( device=self, + id="led", name="LED", icon="mdi:led-{state}", attribute_getter="led", diff --git a/kasa/iot/modules/ambientlight.py b/kasa/iot/modules/ambientlight.py index 2d7d679b..d49768ef 100644 --- a/kasa/iot/modules/ambientlight.py +++ b/kasa/iot/modules/ambientlight.py @@ -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="%", ) ) diff --git a/kasa/iot/modules/cloud.py b/kasa/iot/modules/cloud.py index 41d2cbf5..5022a68e 100644 --- a/kasa/iot/modules/cloud.py +++ b/kasa/iot/modules/cloud.py @@ -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, ) ) diff --git a/kasa/iot/modules/emeter.py b/kasa/iot/modules/emeter.py index 1542e66a..53fb20da 100644 --- a/kasa/iot/modules/emeter.py +++ b/kasa/iot/modules/emeter.py @@ -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, ) ) diff --git a/kasa/module.py b/kasa/module.py index 5b6354a9..3da0c1ad 100644 --- a/kasa/module.py +++ b/kasa/module.py @@ -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 ( diff --git a/kasa/smart/modules/alarmmodule.py b/kasa/smart/modules/alarmmodule.py index 97bdcf78..845eb65a 100644 --- a/kasa/smart/modules/alarmmodule.py +++ b/kasa/smart/modules/alarmmodule.py @@ -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, diff --git a/kasa/smart/modules/autooffmodule.py b/kasa/smart/modules/autooffmodule.py index 8977719c..cb8d5e57 100644 --- a/kasa/smart/modules/autooffmodule.py +++ b/kasa/smart/modules/autooffmodule.py @@ -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, ) ) diff --git a/kasa/smart/modules/battery.py b/kasa/smart/modules/battery.py index 20bca34b..6f914bdf 100644 --- a/kasa/smart/modules/battery.py +++ b/kasa/smart/modules/battery.py @@ -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, ) ) diff --git a/kasa/smart/modules/brightness.py b/kasa/smart/modules/brightness.py index b1209848..b0b58c07 100644 --- a/kasa/smart/modules/brightness.py +++ b/kasa/smart/modules/brightness.py @@ -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", diff --git a/kasa/smart/modules/cloudmodule.py b/kasa/smart/modules/cloudmodule.py index 55338f26..8b9d8f41 100644 --- a/kasa/smart/modules/cloudmodule.py +++ b/kasa/smart/modules/cloudmodule.py @@ -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, ) ) diff --git a/kasa/smart/modules/colormodule.py b/kasa/smart/modules/colormodule.py index 3adf0b4e..716d4c44 100644 --- a/kasa/smart/modules/colormodule.py +++ b/kasa/smart/modules/colormodule.py @@ -22,6 +22,7 @@ class ColorModule(SmartModule): self._add_feature( Feature( device, + "hsv", "HSV", container=self, attribute_getter="hsv", diff --git a/kasa/smart/modules/colortemp.py b/kasa/smart/modules/colortemp.py index 1392775c..d6b43d02 100644 --- a/kasa/smart/modules/colortemp.py +++ b/kasa/smart/modules/colortemp.py @@ -28,6 +28,7 @@ class ColorTemperatureModule(SmartModule): self._add_feature( Feature( device, + "color_temperature", "Color temperature", container=self, attribute_getter="color_temp", diff --git a/kasa/smart/modules/energymodule.py b/kasa/smart/modules/energymodule.py index 6a75299e..9cfe8cfb 100644 --- a/kasa/smart/modules/energymodule.py +++ b/kasa/smart/modules/energymodule.py @@ -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, ) ) diff --git a/kasa/smart/modules/fanmodule.py b/kasa/smart/modules/fanmodule.py index 08a681e7..6eeaa4d4 100644 --- a/kasa/smart/modules/fanmodule.py +++ b/kasa/smart/modules/fanmodule.py @@ -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", diff --git a/kasa/smart/modules/firmware.py b/kasa/smart/modules/firmware.py index 5f0c8bb0..626add0f 100644 --- a/kasa/smart/modules/firmware.py +++ b/kasa/smart/modules/firmware.py @@ -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, ) ) diff --git a/kasa/smart/modules/frostprotection.py b/kasa/smart/modules/frostprotection.py index 07363279..cedaf78b 100644 --- a/kasa/smart/modules/frostprotection.py +++ b/kasa/smart/modules/frostprotection.py @@ -27,6 +27,7 @@ class FrostProtectionModule(SmartModule): self._add_feature( Feature( device, + "frost_protection_enabled", name="Frost protection enabled", container=self, attribute_getter="enabled", diff --git a/kasa/smart/modules/humidity.py b/kasa/smart/modules/humidity.py index ad2bd8c9..ec7d51a7 100644 --- a/kasa/smart/modules/humidity.py +++ b/kasa/smart/modules/humidity.py @@ -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, ) ) diff --git a/kasa/smart/modules/ledmodule.py b/kasa/smart/modules/ledmodule.py index 6fd0d637..e3113159 100644 --- a/kasa/smart/modules/ledmodule.py +++ b/kasa/smart/modules/ledmodule.py @@ -23,6 +23,7 @@ class LedModule(SmartModule): Feature( device=device, container=self, + id="led", name="LED", icon="mdi:led-{state}", attribute_getter="led", diff --git a/kasa/smart/modules/lighteffectmodule.py b/kasa/smart/modules/lighteffectmodule.py index 7f03b8ff..bd0eea0a 100644 --- a/kasa/smart/modules/lighteffectmodule.py +++ b/kasa/smart/modules/lighteffectmodule.py @@ -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", diff --git a/kasa/smart/modules/lighttransitionmodule.py b/kasa/smart/modules/lighttransitionmodule.py index 1cb7f48a..f213d9ac 100644 --- a/kasa/smart/modules/lighttransitionmodule.py +++ b/kasa/smart/modules/lighttransitionmodule.py @@ -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", diff --git a/kasa/smart/modules/reportmodule.py b/kasa/smart/modules/reportmodule.py index 99d95fec..16827a8c 100644 --- a/kasa/smart/modules/reportmodule.py +++ b/kasa/smart/modules/reportmodule.py @@ -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, diff --git a/kasa/smart/modules/temperature.py b/kasa/smart/modules/temperature.py index 49ffe046..4880fc30 100644 --- a/kasa/smart/modules/temperature.py +++ b/kasa/smart/modules/temperature.py @@ -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", diff --git a/kasa/smart/modules/temperaturecontrol.py b/kasa/smart/modules/temperaturecontrol.py index 69847002..ae487bdf 100644 --- a/kasa/smart/modules/temperaturecontrol.py +++ b/kasa/smart/modules/temperaturecontrol.py @@ -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, diff --git a/kasa/smart/modules/timemodule.py b/kasa/smart/modules/timemodule.py index 80f1308e..23814f57 100644 --- a/kasa/smart/modules/timemodule.py +++ b/kasa/smart/modules/timemodule.py @@ -25,6 +25,7 @@ class TimeModule(SmartModule): self._add_feature( Feature( device=device, + id="time", name="Time", attribute_getter="time", container=self, diff --git a/kasa/smart/modules/waterleak.py b/kasa/smart/modules/waterleak.py index 1809c556..6dbc00eb 100644 --- a/kasa/smart/modules/waterleak.py +++ b/kasa/smart/modules/waterleak.py @@ -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, ) ) diff --git a/kasa/smart/smartdevice.py b/kasa/smart/smartdevice.py index 185352a5..89813387 100644 --- a/kasa/smart/smartdevice.py +++ b/kasa/smart/smartdevice.py @@ -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, ) ) diff --git a/kasa/tests/test_feature.py b/kasa/tests/test_feature.py index fe6ba7f2..101a21c0 100644 --- a/kasa/tests/test_feature.py +++ b/kasa/tests/test_feature.py @@ -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,