Răsfoiți Sursa

Add support for Kogan Switches.

For Kogan SmartPlug with Energy Monitoring (single plug, piggyback style)
Supports:
   switch (on/off) - standard switch functionality
   current power drain monitoring (W) - standard switch functionality
   current current draw monitoring (A) - extra attribute
   current voltage monitoring (V) - extra attribute
   timer countdown monitoring (seconds) - extra attribute
Jason Rumney 5 ani în urmă
părinte
comite
f1024df385

+ 18 - 4
README.md

@@ -59,6 +59,16 @@ Current temperature is also displayed.
 
 - **Child lock** (on/off)
 
+### Switch devices
+
+**Kogan Energy monitoring Smart Plug
+- **power** (on/off)
+- **current power consumption** (Watts)
+- **Additional non-standard attributes**
+  - **current current draw** (Amps)
+  - **current voltage** (Volts)
+  - **timer** (seconds) [provided as read only]
+
 ---
 
 ### Warning
@@ -114,28 +124,32 @@ tuya_local:
 
 #### type
 
-    _(string) (Optional)_ The type of Tuya device. `auto` to automatically detect the device type, or if that doesn't work, select from the available options `heater`, `geco_heater` `gpcv_heater`, `dehumidifier`, `fan` or `kogan_heater`.
+    _(string) (Optional)_ The type of Tuya device. `auto` to automatically detect the device type, or if that doesn't work, select from the available options `heater`, `geco_heater` `gpcv_heater`, `dehumidifier`, `fan`, `kogan_heater` or `kogan_switch`.
 
     _Default value: auto_
 
 #### climate
 
-    _(boolean) (Optional)_ Whether to surface this appliance as a climate device.
+    _(boolean) (Optional)_ Whether to surface this appliance as a climate device. (not supported for switches)
 
     _Default value: true_
 
 #### display_light
 
-    _(boolean) (Optional)_ Whether to surface this appliance's LED display control as a light (not supported for Kogan, GECO or GPCV Heaters).
+    _(boolean) (Optional)_ Whether to surface this appliance's LED display control as a light (not supported for Kogan, GECO or GPCV Heaters, or switches).
 
     _Default value: false_
 
 #### child_lock
 
-    _(boolean) (Optional)_ Whether to surface this appliances's child lock as a lock device (not supported for fans).
+    _(boolean) (Optional)_ Whether to surface this appliances's child lock as a lock device (not supported for fans or switches).
 
     _Default value: false_
 
+#### switch
+
+    _(boolean) (Optional)_ Whether to surface this device as a switch device (supported only for switches)
+
 ## Heater gotchas
 
 Goldair GPPH heaters have individual target temperatures for their Comfort and Eco modes, whereas Home Assistant only supports a single target temperature. Therefore, when you're in Comfort mode you will set the Comfort temperature (`5`-`35`), and when you're in Eco mode you will set the Eco temperature (`5`-`21`), just like you were using the heater's own control panel. Bear this in mind when writing automations that change the operation mode and set a temperature at the same time: you must change the operation mode _before_ setting the new target temperature, otherwise you will set the current thermostat rather than the new one.

+ 0 - 1
custom_components/tuya_local/climate.py

@@ -11,7 +11,6 @@ from .const import (
     CONF_TYPE_GPCV_HEATER,
     CONF_TYPE_GPPH_HEATER,
     CONF_TYPE_KOGAN_HEATER,
-    CONF_TYPE_GPPH_HEATER,
     CONF_CLIMATE,
     CONF_TYPE_AUTO,
 )

+ 8 - 0
custom_components/tuya_local/configuration.py

@@ -11,9 +11,11 @@ from .const import (
     CONF_TYPE_GECO_HEATER,
     CONF_TYPE_GPCV_HEATER,
     CONF_TYPE_KOGAN_HEATER,
+    CONF_TYPE_KOGAN_SWITCH,
     CONF_CLIMATE,
     CONF_DISPLAY_LIGHT,
     CONF_CHILD_LOCK,
+    CONF_SWITCH,
     CONF_TYPE_AUTO,
 )
 
@@ -60,6 +62,12 @@ INDIVIDUAL_CONFIG_SCHEMA_TEMPLATE = [
         "default": False,
         "option": True,
     },
+    {
+        "key": CONF_SWITCH,
+        "type": bool,
+        "required": False,
+        "default": False,
+        "option": True,
 ]
 
 

+ 2 - 0
custom_components/tuya_local/const.py

@@ -12,9 +12,11 @@ CONF_TYPE_FAN = "fan"
 CONF_TYPE_GECO_HEATER = "geco_heater"
 CONF_TYPE_GPCV_HEATER = "gpcv_heater"
 CONF_TYPE_KOGAN_HEATER = "kogan_heater"
+CONF_TYPE_KOGAN_SWITCH = "kogan_switch"
 CONF_CLIMATE = "climate"
 CONF_DISPLAY_LIGHT = "display_light"
 CONF_CHILD_LOCK = "child_lock"
+CONF_SWITCH = "switch"
 
 API_PROTOCOL_VERSIONS = [3.3, 3.1]
 SCAN_INTERVAL = timedelta(seconds=30)

+ 4 - 2
custom_components/tuya_local/device.py

@@ -19,6 +19,7 @@ from .const import (
     CONF_TYPE_GPCV_HEATER,
     CONF_TYPE_GPPH_HEATER,
     CONF_TYPE_KOGAN_HEATER,
+    CONF_TYPE_KOGAN_SWITCH,
 )
 
 _LOGGER = logging.getLogger(__name__)
@@ -89,17 +90,18 @@ class TuyaLocalDevice(object):
         _LOGGER.debug(f"Inferring device type from cached state: {cached_state}")
         if "1" not in cached_state:
             return CONF_TYPE_KOGAN_HEATER
-        if "5" in cached_state and "3" not in cached_state:
+        if "5" in cached_state and "3" not in cached_state and "103" in cached_state:
             return CONF_TYPE_DEHUMIDIFIER
         if "8" in cached_state:
             return CONF_TYPE_FAN
+        if "5" in cached_state and "3" not in cached_state:
+            return CONF_TYPE_KOGAN_SWITCH
         if "106" in cached_state:
             return CONF_TYPE_GPPH_HEATER
         if "7" in cached_state:
             return CONF_TYPE_GPCV_HEATER
         if "3" in cached_state:
             return CONF_TYPE_GECO_HEATER
-
         return None
 
     def set_fixed_properties(self, fixed_properties):

+ 0 - 0
custom_components/tuya_local/kogan_socket/__init__.py


+ 16 - 0
custom_components/tuya_local/kogan_socket/const.py

@@ -0,0 +1,16 @@
+from homeassistant.components.switch import (
+    ATTR_CURRENT_POWER_W,
+    DEVICE_CLASS_OUTLET,
+)
+ATTR_SWITCH = "switch"
+ATTR_TIMER = "timer"
+ATTR_CURRENT_A = "current"
+ATTR_VOLTAGE_V = "voltage"
+
+PROPERTY_TO_DPS_ID = {
+    ATTR_SWITCH: "1",
+    ATTR_TIMER: "2",
+    ATTR_CURRENT_A: "4",
+    ATTR_CURRENT_POWER_W: "5",
+    ATTR_VOLTAGE_V: "6",
+}

+ 94 - 0
custom_components/tuya_local/kogan_socket/switch.py

@@ -0,0 +1,94 @@
+"""
+Platform to control the switch on Kogan WiFi-connected energy monitoring sockets.
+"""
+try:
+    from homeassistant.components.switch import SwitchEntity
+except ImportError:
+    from homeassistant.components.switch import SwitchDevice as SwitchEntity
+
+from homeassistant.components.switch import (
+    ATTR_CURRENT_POWER_W,
+    DEVICE_CLASS_OUTLET,
+)
+
+from homeassistant.const import STATE_UNAVAILABLE
+
+from .const import (
+    ATTR_CURRENT_A,
+    ATTR_SWITCH,
+    ATTR_TIMER,
+    ATTR_VOLTAGE_V,
+    PROPERTY_TO_DPS_ID,
+)
+
+
+class KoganSocketSwitch(SwitchEntity):
+    """Representation of a Kogan WiFi-connected energy monitoring socket"""
+
+    def __init__(self, device):
+        """Initialize the switch.
+        Args:
+            device (TuyaLocalDevice): The device API instance."""
+        self._device = device
+
+    @property
+    def should_poll(self):
+        """Return the polling state."""
+        return True
+
+    @property
+    def name(self):
+        """Return the name of the switch."""
+        return self._device.name
+
+    @property
+    def unique_id(self):
+        """Return the unique id for this switch."""
+        return self._device.unique_id
+
+    @property
+    def device_info(self):
+        """Return device information about this switch."""
+        return self._device.device_info
+
+    @property
+    def device_class(self):
+        """Return the class of this device"""
+        return DEVICE_CLASS_OUTLET
+
+    @property
+    def is_on(self):
+        """Return the whether the switch is on."""
+        if self.is_switched_on is None:
+            return STATE_UNAVAILABLE
+        else:
+            return self.is_switched_on
+
+    @property
+    def current_power_w(self):
+        """Return the current power consumption in Watts"""
+        return self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_CURRENT_POWER_W]) / 10.0
+
+    @property
+    def device_state_attributes(self):
+        """Get additional attributes that HA doesn't naturally support."""
+        timer = self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_TIMER])
+        voltage = self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_VOLTAGE_V]) / 10.0
+        current = self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_CURRENT_A]) / 1000.0
+        return {
+            ATTR_CURRENT_POWER_W: self.current_power_w,
+            ATTR_CURRENT_A: current,
+            ATTR_VOLTAGE_V: voltage,
+            ATTR_TIMER: timer,
+        }
+
+    async def async_turn_on(self, **kwargs):
+        """Turn the switch on"""
+        await self._device.async_set_property(PROPERTY_TO_DPS_ID[ATTR_SWITCH], True)
+
+    async def async_turn_off(self, **kwargs):
+        """Turn the switch off"""
+        await self._device.async_set_property(PROPERTY_TO_DPS_ID[ATTR_SWITCH], False)
+
+    async def async_update(self):
+        await self._device.async_refresh()

+ 39 - 0
custom_components/tuya_local/switch.py

@@ -0,0 +1,39 @@
+"""
+Setup for different kinds of Tuya switch devices
+"""
+from . import DOMAIN
+from .const import (
+    CONF_DEVICE_ID,
+    CONF_TYPE,
+    CONF_TYPE_KOGAN_SWITCH,
+    CONF_TYPE_AUTO,
+    CONF_SWITCH,
+)
+from .kogan_switch.switch import KoganSocketSwitch
+
+
+async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
+    """Set up the switch device according to its type."""
+    data = hass.data[DOMAIN][discovery_info[CONF_DEVICE_ID]]
+    device = data["device"]
+
+    if discovery_info[CONF_TYPE] == CONF_TYPE_AUTO:
+        discovery_info[CONF_TYPE] = await device.async_inferred_type()
+
+        if discovery_info[CONF_TYPE] is None:
+            raise ValueError(f"Unable to detecttype for device {device.name}")
+
+    if discovery_info[CONF_TYPE] == CONF_TYPE_KOGAN_SWITCH:
+        data[CONF_SWITCH] = KoganSocketSwitch(device)
+
+    if CONF_SWITCH in data:
+        async_add_entities([data[CONF_SWITCH]])
+
+
+async def async_setup_entry(hass, config_entry, async_add_entities):
+    config = {**config_entry.data, **config_entry.options}
+    discovery_info = {
+        CONF_DEVICE_ID: config[CONF_DEVICE_ID],
+        CONF_TYPE: config[CONF_TYPE],
+    }
+    await async_setup_platform(hass, {}, async_add_entities, discovery_info)

+ 8 - 6
custom_components/tuya_local/translations/en.json

@@ -11,9 +11,10 @@
           "device_id": "Device ID (uuid)",
           "local_key": "Local key",
           "type": "Device type",
-          "climate": "Include a climate entity",
-          "display_light": "Include LED display as a light entity",
-          "child_lock": "Include child lock as a lock entity (unsupported on fans)"
+          "climate": "Include a climate entity (unsupported on switches)",
+          "display_light": "Include LED display as a light entity (Goldair only)",
+          "child_lock": "Include child lock as a lock entity (unsupported on fans and switches)",
+	  "switch": "Include a switch entity (switches only)"
         }
       }
     },
@@ -34,9 +35,10 @@
           "host": "IP address or hostname",
           "local_key": "Local key",
           "type": "Device type",
-          "climate": "Include device as a climate entity",
-          "display_light": "Include LED display as light entity",
-          "child_lock": "Include child lock as lock entity (unsupported on fans)"
+          "climate": "Include device as a climate entity (unsupported for switches)",
+          "display_light": "Include LED display as light entity (Goldair only)",
+          "child_lock": "Include child lock as lock entity (unsupported on fans and switches)",
+	  "switch": "Include device as a switch entity (switches only)"
         }
       },
       "imported": {

+ 1 - 1
hacs.json

@@ -1,7 +1,7 @@
 {
   "name": "Tuya local devices",
   "render_readme": true,
-  "domains": ["climate", "light", "lock"],
+    "domains": ["climate", "light", "lock", "switch"],
   "country": ["NZ", "AU"],
   "homeassistant": "0.109.0",
   "iot_class": "Local Polling"