TP-Link HS220 Dimmer

I’ve installed one of the new TP-Link HS220 dimmer switches. It shows up in Home Assistant as a switch. Is there any timeline for exposing the dimming functionality?

And 13 minutes later it’s sold out on both amazon and the TP-link site :frowning:

I’m not surprised. I’ve been waiting for someone to produce exactly this product for a long time. Hope they planned for big production runs.

There is no timeline or anything, no one has even reported this upstream (https://github.com/GadgetReactor/pyHS100) - someone needs to find out how the dimming functionality is implemented, if not the same as bulbs, and create a patch for it. I suppose it should be exposed not as a switch but as a bulb to homeassistant, maybe simply defining it as a light instead of switch would work?

Thank you for responding.

Contributing to an open source project has been one of those things I’m going to do “real soon now.” Certainly since I have one of these devices, I’ll gather any information and test any code to help move the request along.

Assuming anyone can raise an issue, I’m going to look at other requests to see how others are phrasing their requests and submit it upstream.

Can you point me to any code/techniques that would allow me to find out how the dimming functionality is implemented? Is it just using Wireshark to watch what happens when using the Kasa app to dim the lights?

Thanks again for your help.

1 Like

You could indeed use wireshark to capture the data the app sends (the simplest way is probably to install the app in an emulator, or simply do a tcpdump if you have access to your router). The port it uses is 9999, on https://github.com/softScheck/tplink-smartplug/ there is a wireshark dissector for the traffic to make analysis easier :slight_smile:

So I finally got my HS220. Has anything been submitted yet to get it working properly? If not I might be able to give it a try.

See https://github.com/GadgetReactor/pyHS100/commit/1aee353cbfd4a5219a67700a8d25a446d6e52fcf – there has been no new release including this, but you can use the git version until then.

After I install the thing no lights come on or anything. It’s as if there is no power g9ing to it but after checking the load and lead wires it has electricity flowing through it. I don’t know what to do. (All of my connections are correct btw

Ohhh

WiFi
Dimmer
No hub
Universal 100-240V

Nice. Any updates on dimmer function working on home assistant?

Thanks!

Just remember to download the Kesa app on a device to install the switches. You will be able to give it a name when you set it up through the app.

I have 5 of them now and instead of going out to eat I purchase a switch.

Any update on dimming in HA?

So what exactly is not working at the moment? Or more specifically, how do you expect this to work? The interface is available there in the backend library (those switches are presented as switches allowing “brightness” setting), so I suppose they could be added as lights to homeassistant?

@teprrr

I’ve tried adding them as a light, but they don’t work. Has HASS been updated since the backend library was updated back in may?

Looks like homeassistant already uses the newest release (0.3.3) released in September, the problem is however, that it is implemented as a switch (which allows brightness setting), so in order for this to function there are some changes necessary to the light platform (light/tplink.py), which should initialize a SmartPlug instead of SmartLight for these devices.

I just ordered one of these and should be in in a few days. So does the dimmer function for this work or not?

It is implemented in the backend library, but homeassistant does not support that at the moment. If someone wants to work on that, please feel free to do so. If not, I’ll probably tackle it when I find time to finish https://github.com/home-assistant/home-assistant/pull/15329 .

lol i dont even know where to start with that! My initial though process is that just a few lines for the brightness need to be added to the tplink.py light component.

This is what the pyHS100/smartbulb.py script has listed… https://github.com/GadgetReactor/pyHS100/blob/master/pyHS100/smartbulb.py

from pyHS100 import SmartDevice, SmartDeviceException
import re
from typing import Any, Dict, Optional, Tuple

TPLINK_KELVIN = {‘LB130’: (2500, 9000),
‘LB120’: (2700, 6500),
‘LB230’: (2500, 9000),
‘KB130’: (2500, 9000)}

class SmartBulb(SmartDevice):
“”“Representation of a TP-Link Smart Bulb.
Usage example when used as library:
p = SmartBulb(“192.168.1.105”)
# print the devices alias
print(p.alias)
# change state of bulb
p.state = “ON”
p.state = “OFF”
# query and print current state of plug
print(p.state)
# check whether the bulb supports color changes
if p.is_color:
# set the color to an HSV tuple
p.hsv = (180, 100, 100)
# get the current HSV value
print(p.hsv)
# check whether the bulb supports setting color temperature
if p.is_variable_color_temp:
# set the color temperature in Kelvin
p.color_temp = 3000
# get the current color temperature
print(p.color_temp)
# check whether the bulb is dimmable
if p.is_dimmable:
# set the bulb to 50% brightness
p.brightness = 50
# check the current brightness
print(p.brightness)
Errors reported by the device are raised as SmartDeviceExceptions,
and should be handled by the user of the library.
“””
# bulb states
BULB_STATE_ON = ‘ON’
BULB_STATE_OFF = ‘OFF’

def __init__(self,
             host: str,
             protocol: 'TPLinkSmartHomeProtocol' = None) -> None:
    SmartDevice.__init__(self, host, protocol)
    self.emeter_type = "smartlife.iot.common.emeter"

@property
def is_color(self) -> bool:
    """
    Whether the bulb supports color changes
    :return: True if the bulb supports color changes, False otherwise
    :rtype: bool
    """
    return bool(self.sys_info['is_color'])

@property
def is_dimmable(self) -> bool:
    """
    Whether the bulb supports brightness changes
    :return: True if the bulb supports brightness changes, False otherwise
    :rtype: bool
    """
    return bool(self.sys_info['is_dimmable'])

@property
def is_variable_color_temp(self) -> bool:
    """
    Whether the bulb supports color temperature changes
    :return: True if the bulb supports color temperature changes, False
    otherwise
    :rtype: bool
    """
    return bool(self.sys_info['is_variable_color_temp'])

@property
def valid_temperature_range(self) -> Tuple[int, int]:
    """
    Returns the white temperature range (in Kelvin)
    depending on the bulb model
    :return: White temperature range in Kelvin (minimun, maximum)
    :rtype: tuple
    """
    if not self.is_variable_color_temp:
        return (0, 0)
    for model, temp_range in TPLINK_KELVIN.items():
        if re.match(model, self.sys_info['model']):
            return temp_range
    return (0, 0)

def get_light_state(self) -> Dict:
    return self._query_helper("smartlife.iot.smartbulb.lightingservice",
                              "get_light_state")

def set_light_state(self, state: Dict) -> Dict:
    return self._query_helper("smartlife.iot.smartbulb.lightingservice",
                              "transition_light_state", state)

@property
def hsv(self) -> Optional[Tuple[int, int, int]]:
    """
    Returns the current HSV state of the bulb, if supported
    :return: hue, saturation and value (degrees, %, %)
    :rtype: tuple
    """

    if not self.is_color:
        return None

    light_state = self.get_light_state()
    if not self.is_on:
        hue = light_state['dft_on_state']['hue']
        saturation = light_state['dft_on_state']['saturation']
        value = light_state['dft_on_state']['brightness']
    else:
        hue = light_state['hue']
        saturation = light_state['saturation']
        value = light_state['brightness']

    return hue, saturation, value

@hsv.setter
def hsv(self, state: Tuple[int, int, int]):
    """
    Sets new HSV, if supported
    :param tuple state: hue, saturation and value (degrees, %, %)
    """
    if not self.is_color:
        return None

    if not isinstance(state[0], int) or not (0 <= state[0] <= 255):
        raise SmartDeviceException(
                'Invalid hue value: {} '
                '(valid range: 0-255)'.format(state[0]))

    if not isinstance(state[1], int) or not (0 <= state[1] <= 100):
        raise SmartDeviceException(
                'Invalid saturation value: {} '
                '(valid range: 0-100%)'.format(state[1]))

    if not isinstance(state[2], int) or not (0 <= state[2] <= 100):
        raise SmartDeviceException(
                'Invalid brightness value: {} '
                '(valid range: 0-100%)'.format(state[2]))

    light_state = {
        "hue": state[0],
        "saturation": state[1],
        "brightness": state[2],
        "color_temp": 0
        }
    self.set_light_state(light_state)

@property
def color_temp(self) -> Optional[int]:
    """
    Color temperature of the device, if supported
    :return: Color temperature in Kelvin
    :rtype: int
    """
    if not self.is_variable_color_temp:
        return None

    light_state = self.get_light_state()
    if not self.is_on:
        return int(light_state['dft_on_state']['color_temp'])
    else:
        return int(light_state['color_temp'])

@color_temp.setter
def color_temp(self, temp: int) -> None:
    """
    Set the color temperature of the device, if supported
    :param int temp: The new color temperature, in Kelvin
    """
    if not self.is_variable_color_temp:
        return None

    if temp < self.valid_temperature_range[0] or \
            temp > self.valid_temperature_range[1]:
        raise ValueError("Temperature should be between {} "
                         "and {}".format(*self.valid_temperature_range))

    light_state = {
        "color_temp": temp,
    }
    self.set_light_state(light_state)

@property
def brightness(self) -> Optional[int]:
    """
    Current brightness of the device, if supported
    :return: brightness in percent
    :rtype: int
    """
    if not self.is_dimmable:
        return None

    light_state = self.get_light_state()
    if not self.is_on:
        return int(light_state['dft_on_state']['brightness'])
    else:
        return int(light_state['brightness'])

@brightness.setter
def brightness(self, brightness: int) -> None:
    """
    Set the current brightness of the device, if supported
    :param int brightness: brightness in percent
    """
    if not self.is_dimmable:
        return None

    light_state = {
        "brightness": brightness,
    }
    self.set_light_state(light_state)

@property
def state(self) -> str:
    """
    Retrieve the bulb state
    :returns: one of
              BULB_STATE_ON
              BULB_STATE_OFF
    :rtype: str
    """
    light_state = self.get_light_state()
    if light_state['on_off']:
        return self.BULB_STATE_ON
    return self.BULB_STATE_OFF

@state.setter
def state(self, bulb_state: str) -> None:
    """
    Set the new bulb state
    :param bulb_state: one of
                       BULB_STATE_ON
                       BULB_STATE_OFF
    """
    if bulb_state == self.BULB_STATE_ON:
        new_state = 1
    elif bulb_state == self.BULB_STATE_OFF:
        new_state = 0
    else:
        raise ValueError

    light_state = {
        "on_off": new_state,
    }
    self.set_light_state(light_state)

@property
def state_information(self) -> Dict[str, Any]:
    """
    Return bulb-specific state information.
    :return: Bulb information dict, keys in user-presentable form.
    :rtype: dict
    """
    info = {
        'Brightness': self.brightness,
        'Is dimmable': self.is_dimmable,
    }  # type: Dict[str, Any]
    if self.is_variable_color_temp:
        info["Color temperature"] = self.color_temp
    if self.is_color:
        info["HSV"] = self.hsv

    return info

@property
def is_on(self) -> bool:
    return bool(self.state == self.BULB_STATE_ON)

def turn_off(self) -> None:
    """
    Turn the bulb off.
    """
    self.state = self.BULB_STATE_OFF

def turn_on(self) -> None:
    """
    Turn the bulb on.
    """
    self.state = self.BULB_STATE_ON

@property
def has_emeter(self) -> bool:
    return True

As I mentioned, this requires (likely) using homeassistant’s light.tplink platform in combination with pyhs100’s SmartPlug where this feature is currently implemented. You can try with the client tool: pyhs100 --plug --ip <dimmer ip> brightness to see if it is working properly.

1 Like

So that command definitely works! im able to set the brightness to whatever level i want. It only shows up as a switch in homeassistant. If I set it up as a light in ha it shows unavailable. Works as a switch but no dimming options.