From 18bda8c72116ca6aeddfdc717f3131d1fa7c1f58 Mon Sep 17 00:00:00 2001 From: veista Date: Mon, 3 Jan 2022 03:33:03 +0200 Subject: [PATCH] Added Button Entity and Fixed bugs Fixed bugs and added button entity --- custom_components/smartthings/button.py | 122 ++++++++++++++++++++++ custom_components/smartthings/const.py | 1 + custom_components/smartthings/sensor.py | 9 +- custom_components/smartthings/smartapp.py | 2 + custom_components/smartthings/switch.py | 82 ++++----------- hacs.json | 4 +- 6 files changed, 150 insertions(+), 70 deletions(-) create mode 100644 custom_components/smartthings/button.py diff --git a/custom_components/smartthings/button.py b/custom_components/smartthings/button.py new file mode 100644 index 0000000..d36c4cd --- /dev/null +++ b/custom_components/smartthings/button.py @@ -0,0 +1,122 @@ +"""Support for buttons through the SmartThings cloud API.""" +from __future__ import annotations + +from collections import namedtuple +from collections.abc import Sequence + +from pysmartthings.device import DeviceEntity + +from homeassistant.components.button import ButtonEntity + +from . import SmartThingsEntity +from .const import DATA_BROKERS, DOMAIN + +Map = namedtuple( + "map", + "button_command name icon device_class extra_state_attributes", +) + +CAPABILITY_TO_BUTTON = { + "custom.dustFilter": [ + Map( + "resetDustFilter", + "Reset Dust Filter", + "mdi:air-filter", + None, + [ + "dustFilterUsageStep", + "dustFilterUsage", + "dustFilterLastResetDate", + "dustFilterStatus", + "dustFilterCapacity", + "dustFilterResetType", + ], + ) + ], +} + + +async def async_setup_entry(hass, config_entry, async_add_entities): + """Add switches for a config entry.""" + broker = hass.data[DOMAIN][DATA_BROKERS][config_entry.entry_id] + buttons = [] + for device in broker.devices.values(): + for capability in broker.get_assigned(device.device_id, "button"): + maps = CAPABILITY_TO_BUTTON[capability] + buttons.extend( + [ + SmartThingsButton( + device, + capability, + m.button_command, + m.name, + m.icon, + m.device_class, + m.extra_state_attributes, + ) + for m in maps + ] + ) + + async_add_entities(buttons) + + +def get_capabilities(capabilities: Sequence[str]) -> Sequence[str] | None: + """Return all capabilities supported if minimum required are present.""" + # Must be able to be turned on. + return [ + capability for capability in CAPABILITY_TO_BUTTON if capability in capabilities + ] + + +class SmartThingsButton(SmartThingsEntity, ButtonEntity): + """Define a SmartThings button.""" + + def __init__( + self, + device: DeviceEntity, + capability: str, + button_command: str | None, + name: str, + icon: str | None, + device_class: str | None, + extra_state_attributes: str | None, + ) -> None: + """Init the class.""" + super().__init__(device) + self._capability = capability + self._button_command = button_command + self._name = name + self._icon = icon + self._attr_device_class = device_class + self._extra_state_attributes = extra_state_attributes + + async def async_press(self) -> None: + """Handle the button press.""" + await self._device.command("main", self._capability, self._button_command, []) + + @property + def name(self) -> str: + """Return the name of the switch.""" + return f"{self._device.label} {self._name}" + + @property + def unique_id(self) -> str: + """Return a unique ID.""" + return f"{self._device.device_id}.{self._name}" + + @property + def icon(self) -> str | None: + return self._icon + + @property + def extra_state_attributes(self): + """Return device specific state attributes.""" + state_attributes = {} + if self._extra_state_attributes is not None: + attributes = self._extra_state_attributes + for attribute in attributes: + value = self._device.status.attributes[attribute].value + if value is not None: + state_attributes[attribute] = value + return state_attributes diff --git a/custom_components/smartthings/const.py b/custom_components/smartthings/const.py index 7310507..0a39179 100644 --- a/custom_components/smartthings/const.py +++ b/custom_components/smartthings/const.py @@ -39,6 +39,7 @@ "cover", "number", "select", + "button", "switch", "binary_sensor", "sensor", diff --git a/custom_components/smartthings/sensor.py b/custom_components/smartthings/sensor.py index d9629a8..097ba10 100644 --- a/custom_components/smartthings/sensor.py +++ b/custom_components/smartthings/sensor.py @@ -698,13 +698,10 @@ def __init__( """Init the class.""" super().__init__(device) self.report_name = report_name - self._attr_state_class = STATE_CLASS_MEASUREMENT - if ( - self.report_name != "power" - or self.report_name != "deltaEnergy" - or self.report_name != "powerEnergy" - ): + if self.report_name in ("energy", "energySaved"): self._attr_state_class = STATE_CLASS_TOTAL_INCREASING + else: + self._attr_state_class = STATE_CLASS_MEASUREMENT @property def name(self) -> str: diff --git a/custom_components/smartthings/smartapp.py b/custom_components/smartthings/smartapp.py index ca890a7..e1e25b6 100644 --- a/custom_components/smartthings/smartapp.py +++ b/custom_components/smartthings/smartapp.py @@ -363,6 +363,8 @@ async def delete_subscription(sub: SubscriptionEntity): if disabled_capability in new_capabilities: new_capabilities.remove(disabled_capability) capabilities.update(new_capabilities) + else: + capabilities.update(device.capabilities) # Remove unused capabilities capabilities.difference_update(IGNORED_CAPABILITIES) diff --git a/custom_components/smartthings/switch.py b/custom_components/smartthings/switch.py index bf10526..ff87910 100644 --- a/custom_components/smartthings/switch.py +++ b/custom_components/smartthings/switch.py @@ -45,25 +45,6 @@ None, ) ], - "custom.dustFilter": [ - Map( - None, - "resetDustFilter", - None, - None, - None, - "Reset Dust Filter", - "mdi:air-filter", - [ - "dustFilterUsageStep", - "dustFilterUsage", - "dustFilterLastResetDate", - "dustFilterStatus", - "dustFilterCapacity", - "dustFilterResetType", - ], - ) - ], } @@ -74,10 +55,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): for device in broker.devices.values(): for capability in broker.get_assigned(device.device_id, "switch"): maps = CAPABILITY_TO_SWITCH[capability] - if ( - capability == "custom.autoCleaningMode" - or capability == "custom.dustFilter" - ): + if capability == "custom.autoCleaningMode": switches.extend( [ SmartThingsCustomSwitch( @@ -133,9 +111,9 @@ class SmartThingsSwitch(SmartThingsEntity, SwitchEntity): def __init__( self, device: DeviceEntity, - attribute: str | None, - on_command: str | None, - off_command: str | None, + attribute: str, + on_command: str, + off_command: str, on_value: str | int | None, off_value: str | int | None, name: str, @@ -175,9 +153,7 @@ def name(self) -> str: @property def unique_id(self) -> str: """Return a unique ID.""" - if self._attribute is not None: - return f"{self._device.device_id}.{self._attribute}" - return f"{self._device.device_id}.{self._name}" + return f"{self._device.device_id}.{self._attribute}" @property def is_on(self) -> bool: @@ -208,9 +184,9 @@ def __init__( self, device: DeviceEntity, capability: str, - attribute: str | None, - on_command: str | None, - off_command: str | None, + attribute: str, + on_command: str, + off_command: str, on_value: str | int | None, off_value: str | int | None, name: str, @@ -231,38 +207,22 @@ def __init__( async def async_turn_off(self, **kwargs) -> None: """Turn the switch off.""" - if self._off_command is not None: - if self._on_value is not None: - result = await self._device.command( - "main", self._capability, self._off_command, [self._off_value] - ) - if result: - self._device.status.update_attribute_value( - self._attribute, self._off_value - ) - else: - await self._device.command( - "main", self._capability, self._off_command, [] - ) + result = await self._device.command( + "main", self._capability, self._off_command, [self._off_value] + ) + if result: + self._device.status.update_attribute_value(self._attribute, self._off_value) # State is set optimistically in the command above, therefore update # the entity state ahead of receiving the confirming push updates self.async_write_ha_state() async def async_turn_on(self, **kwargs) -> None: """Turn the switch on.""" - if self._on_command is not None: - if self._on_value is not None: - result = await self._device.command( - "main", self._capability, self._on_command, [self._on_value] - ) - if result: - self._device.status.update_attribute_value( - self._attribute, self._on_value - ) - else: - await self._device.command( - "main", self._capability, self._on_command, [] - ) + result = await self._device.command( + "main", self._capability, self._on_command, [self._on_value] + ) + if result: + self._device.status.update_attribute_value(self._attribute, self._on_value) # State is set optimistically in the command above, therefore update # the entity state ahead of receiving the confirming push updates @@ -276,9 +236,7 @@ def name(self) -> str: @property def unique_id(self) -> str: """Return a unique ID.""" - if self._attribute is not None: - return f"{self._device.device_id}.{self._attribute}" - return f"{self._device.device_id}.{self._name}" + return f"{self._device.device_id}.{self._attribute}" @property def is_on(self) -> bool: @@ -356,4 +314,4 @@ def is_on(self) -> bool: @property def icon(self) -> str | None: - return "mdi:led-on" \ No newline at end of file + return "mdi:led-on" diff --git a/hacs.json b/hacs.json index 8afb573..5595353 100644 --- a/hacs.json +++ b/hacs.json @@ -1,7 +1,7 @@ { "name": "SmartThings Custom", - "domains": ["binary_sensor","climate", "cover", "fan", "light", "lock", "select", "sensor", "switch", "number"], - "homeassistant": "2021.12.0", + "domains": ["binary_sensor","climate", "cover", "fan", "light", "lock", "select", "sensor", "switch", "number", "button"], + "homeassistant": "2021.12.7", "render_readme": true, "content_in_root": false, "iot_class": "cloud_push"