TP-Link dimmer switch binding?

I just recently purchased and installed one of TP-Link’s new HS220 v1 dimmer switches. Seems to work pretty nicely! I was wondering if there was a plan to add this to the current tp-link binding. Any ideas?

I can provide info for anyone who might need it (i.e. wireshark)

No actual plans unless someone asks for it :wink: Having wireshark info would be great because HS220 has a dimmer and that is something new for tplink switches. So I would like to have the json for:

  1. system get_sysinfo result of the device that gives all status info
  2. json command send to the device to change brightness.
  3. what device returns when brightness change command is given.
    (Not sure if you’re aware, but there is a plugin for tplink for wireshark to decode the signal)

I’d really like to get the hs220 working with openhab too.
I’ll paste in some JSON I collected from one of mine - Using this project https://github.com/konsumer/tplink-lightbulb (the details output).
(I scrambled a couple of the identifiers…)

On/Off in the tplink script seems to work, but setting the brightness with hsb doesn’t.

The kasa app seems to control the relay_state and the brightness value.

I’ve got several of these tplink devices, and so far the binding works great - thanks so much.

{
“sw_ver”: “1.4.8 Build 180109 Rel.171240”,
“hw_ver”: “1.0”,
“mic_type”: “IOT.SMARTPLUGSWITCH”,
“model”: “HS220(US)”,
“mac”: “AC:84:C6:56:??:??”,
“dev_name”: “Smart Wi-Fi Dimmer”,
“alias”: “Bar Dimmer”,
“relay_state”: 1,
“brightness”: 40,
“on_time”: 5,
“active_mode”: “none”,
“feature”: “TIM”,
“updating”: 0,
“icon_hash”: “”,
“rssi”: -33,
“led_off”: 0,
“longitude_i”: -1219719,
“latitude_i”: 377790,
“hwId”: “84DCCF37225C9E55319617F7D5???”,
“fwId”: “00000000000000000000000000000000”,
“deviceId”: “80067C9B066DE16313D8A94A38B35B581A???”,
“oemId”: “3B13224B2807E0D48A9DD06EBD344CD6”,
“preferred_state”: [
{
“index”: 0,
“brightness”: 100
},
{
“index”: 1,
“brightness”: 75
},
{
“index”: 2,
“brightness”: 50
},
{
“index”: 3,
“brightness”: 25
}
],
“next_action”: {
“type”: -1
},
“err_code”: 0
}

Hi Again, I managed to get the wireshark plugin going, and can report some results from the protocol:

When I turn the switch ON from the app:

SEND:
{“smartlife.iot.dimmer”:{“set_switch_state”:{“state”:1}}}
RECEIVE:
{“smartlife.iot.dimmer”:{“set_switch_state”:{“err_code”:0}}}

(Likewise for switch OFF only with “state”: 0)

When I switched to trying to sniff the brightness control the wireshark dissector doesn’t see anything. I did see some UDP packets, so I hacked the dissector to make a UDP version.

When I move the brightness slider in the app (UDP):

SEND:
{“smartlife.iot.dimmer”:{“set_brightness”:{“brightness”:17}}}
RECEIVE:
{“smartlife.iot.dimmer”:{“set_brightness”:{“err_code”:0}}}

If I select the “Gentle Off” preset value
SEND:
{“smartlife.iot.dimmer”:{“set_dimmer_transition”:{“brightness”:0,“duration”:1,“mode”:“gentle_on_off”}}}
RECEIVE:
{“smartlife.iot.dimmer”:{“set_dimmer_transition”:{“err_code”:0}}}

If I select the “25%” Preset:
SEND:
{“smartlife.iot.dimmer”:{“set_dimmer_transition”:{“brightness”:25,“duration”:1}}}
RECIEVE:
{“smartlife.iot.dimmer”:{“set_dimmer_transition”:{“err_code”:0}}}

I’ll dump my wireshark dissector in another reply.

Aside from changing from tcp to udp, I had to modify the “start” value at around line 40 from 4 to 0. Other than that, the original decryption seems to work ok.

-- TP-Link Smart Home Protocol (Port 9999) Wireshark Dissector
-- For decrypting local network traffic between TP-Link
-- Smart Home Devices and the Kasa Smart Home App
--
-- Install under:
-- (Windows)      %APPDATA%\Wireshark\plugins\
-- (Linux, Mac)   $HOME/.wireshark/plugins
--
-- by Lubomir Stroetmann
-- Copyright 2016 softScheck GmbH
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
--    http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
--

-- Create TP-Link Smart Home protocol and its fields
p_tplinkudp = Proto ("TPLink-SmartHome-UDP","TP-Link Smart Home Protocol UDP")

-- Dissector function
function p_tplinkudp.dissector (buf, pkt, root)
  -- Validate packet length
  if buf:len() == 0 then return end
  pkt.cols.protocol = p_tplinkudp.name

  -- Decode data
        local ascii = ""
                local hex = ""

                -- Skip first 4 bytes (header)
        start = 0
        endPosition = buf:len() - 1

                -- Decryption key is -85 (256-85=171)
                local key = 171

                -- Decrypt Autokey XOR
                -- Save results as ascii and hex
        for index = start, endPosition do
          local c = buf(index,1):uint()
                  -- XOR first byte with key
                  d = bit32.bxor(c,key)
                  -- Use byte as next key
                  key = c

                  hex = hex .. string.format("%x", d)
          -- Convert to printable characters
          if d >= 0x20 and d <= 0x7E then
            ascii = ascii .. string.format("%c", d)
          else
            -- Use dot for non-printable bytes
            ascii = ascii .. "."
          end
        end


  -- Create subtree
  subtree = root:add(p_tplinkudp, buf(0))

  -- Add data to subtree
  subtree:add(ascii)
  -- Description of payload
  subtree:append_text(" (decrypted)")

  -- Call JSON Dissector with decrypted data
  local b = ByteArray.new(hex)
  local tvb = ByteArray.tvb(b, "JSON TVB")
  Dissector.get("json"):call(tvb, pkt, root)

end

-- Initialization routine
function p_tplinkudp.init()
end

-- Register a chained dissector for port 9999
local udp_dissector_table = DissectorTable.get("udp.port")
dissector = udp_dissector_table:get_dissector(9999)
udp_dissector_table:add(9999, p_tplinkudp)

Thanks for the wireshark results. I’ll add the hs220 to the binding, hopefully I’ll have a testable version next week.

I have some additional questions.
When changing the brightness when the device is off does it switch the device on? Do you know if a set_switch_state command was send in such a case or is simply changing brightness enough tp switch the device on?

Regarding the “Gentle Off” and “Preset”. What is the duration of the change of state? It looks like it is configurable. Maybe not from the app, but the command does seem to support if: it sends “duration:1” That might suggest 1 second or 1 minute? What can you tell me about the duration of the state changes?

From the kasa app, when the device is switched off, the dimmer slider is disabled, so I can’t really test. I guess to be safe, when the dimmer is > 0 the switch might have to be on too but I guess testing will bear out. I have some of the light bulbs too, and the behavior from the app is the same for those.

Playing with the gentle off and presets, it looks like the duration just stays at 1. Possibly a factor of the setting?

There’s a “Smart Dimmer Configuration” section in the Device Settings in the app. There’s a Gentle Off setting with choices of 10s 30s 1m, 5m and “Custom” which varies from 1m to 10m. So when I set the Gentle Off timer, there’s a TCP message like {“smartlife.iot.dimmer”:{“set_gentle_off_time”:{“duration”:510000}}} - I’m guessing that duration is in ms.

For the presets, there are fade on and fade off speed config items. There are choices of “Instant”, “Fast” “Medium”, and “Slow”,

Setting the fade on timer looks like this (TCP):
{“smartlife.iot.dimmer”:{“set_fade_on_time”:{“fadeTime”:500}}}

With response:
{“smartlife.iot.dimmer”:{“set_fade_on_time”:{“err_code”:0}}}

The timer values are Instant -> 500, Fast -> 1000, Medium -> 2000 and Slow -> 3000

A command that comes out after setting the fade speed is
Send:
{“smartlife.iot.dimmer”:{“get_dimmer_parameters”:{}}}

Receive:
{“smartlife.iot.dimmer”:{“get_dimmer_parameters”:{“minThreshold”:0,“fadeOnTime”:3000,“fadeOffTime”:3000,“gentleOnTime”:3000,“gentleOffTime”:510000,“rampRate”:30,“bulb_type”:1,“err_code”:0}}}

The fade off timer sequence is the same
e.g.
{“smartlife.iot.dimmer”:{“set_fade_off_time”:{“fadeTime”:500}}}

I see another TCP sequence for default behavior:
{“smartlife.iot.dimmer”:{“get_default_behavior”:{}}}
returns something like this:
{“smartlife.iot.dimmer”:{“get_default_behavior”:{“soft_on”:{“mode”:“last_status”},“hard_on”:{“mode”:“last_status”},“long_press”:{“mode”:“instant_on_off”},“double_click”:{“mode”:“gentle_on_off”},“err_code”:0}}}

I’ve updated the tp-link binding in the Eclipse Market place. I’ve added support for HS220. You can test if it works and if it works ok I’m going to make it a pull request to be added to the main repository.

I didn’t added support for fade or gentleoff, because I wanted the basic functionality working first. Gentle off is also something that can be done with rules, and should probably be done as such. fade support might be interesting, and can probably be added by adding 2 channels: fadeOnTime and fadeOffTime.

I’ve got the new binding installed. I’ll check it out over the weekend. thanks again

It mostly works. I am able to control the dimmer from openhab, but I’m not sure about some of the feedback paths.

The switch has dimmer level up/down buttons, changing the light level from the switch doesn’t seem to update the slider in my openhab UI. Likewise, changing the level from the kasa app doesn’t seem to feed back to openhab.

I noticed that I’m not able to switch the light off. Not sure If I’m doing something wrong. I’ve got some of the light bulbs too. With those, I move the slider all the way to the left, and the light goes off. With the HS220 it seems like I can only set the dimmer to a low level, but not completely off. I could probably work around it with a rule, but like I said, the lb100/lb110 bulbs seem to be able to shut off.

Here’s my thing / item definition if it helps:
Thing tplinksmarthome:hs220:fhdimmer [ipAddress=“192.168.1.196”, refresh=10, transitionPeriod=1000 ]
Dimmer fh_dim “Front Hall Dimmer” (gInsideLight)
{channel=“tplinksmarthome:hs220:fhdimmer:brightness”}

A minor thing. I noticed in PaperUI -> Configuration > Bindings > TP-Link Smart Home Devices Binding. The Supported Things lists HS210 twice (but one of the two has the correct thing tag).

ok some work to do :wink:

I’m not sure why it won’t update the state. It might be I used the wrong type to set the data. I’m going to change this.

The lights should switch off, because it also sends a set_switch_state command. So I’m not sure why that doesn’t work. Can you do the following test: Make the item a Switch instead of a Dimmer (keep brightness channel) And see what happens when you switch the device on/off? Does that work? Can you also try the same by setting the channel in the thing to the switch channel?

I’ve got some event logs to share.

So with the item set like this:
Dimmer bar2_dim “Over Bar Dimmer” (gInsideLight)
{channel=“tplinksmarthome:hs220:bardimmer:brightness”}

As I vary the dimmer level I see this:
2018-05-07 22:18:27.802 [ome.event.ItemCommandEvent] - Item ‘bar2_dim’ received command 8
2018-05-07 22:18:27.803 [vent.ItemStateChangedEvent] - bar2_dim changed from NULL to 8
2018-05-07 22:18:29.842 [ome.event.ItemCommandEvent] - Item ‘bar2_dim’ received command 100
2018-05-07 22:18:29.843 [vent.ItemStateChangedEvent] - bar2_dim changed from 8 to 100
2018-05-07 22:18:36.679 [ome.event.ItemCommandEvent] - Item ‘bar2_dim’ received command 8
2018-05-07 22:18:36.680 [vent.ItemStateChangedEvent] - bar2_dim changed from 100 to 8
2018-05-07 22:18:42.464 [ome.event.ItemCommandEvent] - Item ‘bar2_dim’ received command 0
2018-05-07 22:18:42.464 [vent.ItemStateChangedEvent] - bar2_dim changed from 8 to 0
2018-05-07 22:18:42.581 [hingStatusInfoChangedEvent] - ‘tplinksmarthome:hs220:bardimmer’ changed from ONLINE to OFFLINE (COMMUNICATION_ERROR): Error (-3): invalid argument

Watching the light, I see the dimmer level go up and down, but I can’t make it go to zero.

With the item set as
Switch bar2_dim “Over Bar Dimmer” (gInsideLight)
{channel=“tplinksmarthome:hs220:bardimmer:brightness”}

2018-05-07 22:20:12.124 [ome.event.ItemCommandEvent] - Item ‘bar2_dim’ received command OFF
2018-05-07 22:20:12.125 [vent.ItemStateChangedEvent] - bar2_dim changed from ON to OFF
2018-05-07 22:20:12.139 [hingStatusInfoChangedEvent] - ‘tplinksmarthome:hs220:bardimmer’ changed from ONLINE to OFFLINE (COMMUNICATION_ERROR): Error (-3): invalid argument
2018-05-07 22:20:14.102 [ome.event.ItemCommandEvent] - Item ‘bar2_dim’ received command ON
2018-05-07 22:20:14.103 [vent.ItemStateChangedEvent] - bar2_dim changed from OFF to ON

When I changed it to a Switch the light was dimmed. Turning the switch on made the light go full bright, but switching off seemed to have no effect.

Setting the item up like this:
Switch bar2_dim “Over Bar Dimmer” (gInsideLight)
{channel=“tplinksmarthome:hs220:bardimmer:switch”}

2018-05-07 22:25:55.662 [.ItemChannelLinkAddedEvent] - Link ‘bar2_dim-tplinksmarthome:hs220:bardimmer:switch’ has been added.
2018-05-07 22:25:55.771 [hingStatusInfoChangedEvent] - ‘tplinksmarthome:hs220:bardimmer’ changed from OFFLINE (COMMUNICATION_ERROR): Error (-3): invalid argument to ONLINE
2018-05-07 22:26:14.534 [ome.event.ItemCommandEvent] - Item ‘bar2_dim’ received command OFF
2018-05-07 22:26:14.535 [vent.ItemStateChangedEvent] - bar2_dim changed from ON to OFF
2018-05-07 22:26:14.637 [hingStatusInfoChangedEvent] - ‘tplinksmarthome:hs220:bardimmer’ changed from ONLINE to OFFLINE (COMMUNICATION_ERROR): Error (-3): invalid argument
2018-05-07 22:26:17.216 [ome.event.ItemCommandEvent] - Item ‘bar2_dim’ received command ON
2018-05-07 22:26:17.217 [vent.ItemStateChangedEvent] - bar2_dim changed from OFF to ON
2018-05-07 22:26:18.772 [ome.event.ItemCommandEvent] - Item ‘bar2_dim’ received command OFF
2018-05-07 22:26:18.773 [vent.ItemStateChangedEvent] - bar2_dim changed from ON to OFF

Makes the switching (on and off) work.

Actually, independent switching and dimming is probably better for me. Is that something that is available for the lb100 bulbs?

I’ve updated the binding in the marketplace based on your feedback. If you uninstall the current binding and reinstall the binding from the eclipse market place you should have the new version. You can check this by seeing if the label correctly show hs220.

I hope the binding correctly read the status when changed via switch or kasa app. I’ve made a change here that hopefully fixes the issue.

It looks like it’s not allowed to send brightness 0. This would explain why you could not switch the device off. I’ve changed this. In this update it doesn’t switch the device on when brightness is set. It assumes when brightness is set the device will switch on. (I read on other forums this should work). Can you confirm this does work?

I’ve removed the switch channel. When you define a Switch item on the brightness channel it will only send a switch on/off command and not update the brightness. This is actually how it also works with bulbs. So you can create 2 items a Dimmer and Switch to the same brightness channel and this will give you independent switching and dimming.

Hi, Sorry It took me so long to get back to this.

I can confirm that the label in the binding config looks correct now.

I setup a hs220 thing like this:
Thing tplinksmarthome:hs220:fhdimmer [ipAddress=“192.168.1.196”, refresh=10, transitionPeriod=1000 ]

Then I setup Dimmer and Switch items like this:
Dimmer fronthall_dim “Front Hall Dimmer” (gInsideLight)
{channel=“tplinksmarthome:hs220:fhdimmer:brightness”}
Switch fronthall_sw “Front Hall” (gInsideLight)
{channel=“tplinksmarthome:hs220:fhdimmer:brightness”}

Switching and Dimmer level setting seem to work great.
I like that the dimmer seems to be independent of the switching.

The feedback loop is also working between kasa, the physical switch and openhab seems to be working for switching and dimmer level. There is some lag, but I don’t notice a big difference between kasa and openhab.

I’ll keep testing it, but this version seems to be a winner so far.

Thanks so much for your updates.

I just got 2 LB110 lightbulbs setup with the binding from the OH stable repository

It kinda works, but does not keep track of state

Switching on and off or setting a dimmer lever works but it very quickly loses track of the current state

When might this be published in the Ubuntu OH repository?
Do the fixes possibly apply to the LB110?

The state issue with the bulbs should be fixed in the binding that is available via the Eclipse Marketplace. The fix wil also be in the 2.3.0 release. (The HS220 didn’t make it in the 2.3.0 release, but will be in the 2.4.0 and is already in the Marketplace and 2.4.0-SNAPSHOT).

Thank you
I just performed an Ubuntu apt upgrade to version 2.3. That seems to have fixed the bulb state issue.

Hi,

I am sorry to bring this old post alive, but trying to configure a TP-Link HS220 Dimmer this week, I stumbled across some issues with the binding still.

I tried the version shipped with 2.4-Stable and some snapshots: 2.4.0.201810291414 and 2.4.0.201812271133.

The Thing is added thru a Things file:
tplinksmarthome:hs220:HO_FF_Kitchen_DimmerXXXX "Kitchen Suspended Lights" [ ipAddress="XX.XX.XX.XX", refresh=1 ]

The Dimmer item:
Dimmer HO_FF_Kitchen_SuspendedLights "Kitchen Suspended Lights" <slider> (Kitchen, Kitchen_Lights) {channel="tplinksmarthome:hs220:HO_FF_Kitchen_DimmerXXXX:brightness"}

The switch item:
Switch HO_FF_Kitchen_SuspendedLights_OnOff "Kitchen Suspended Lights On Off" <switch> (Kitchen, Kitchen_Lights_AOOC) {channel="tplinksmarthome:hs220:HO_FF_Kitchen_DimmerXXXX:switch"}

The issues are:

  1. When the Dimmer slider, thru PaperUI, is put to 0%, the item fails with Error (-3)
    'tplinksmarthome:hs220:HO_FF_Kitchen_DimmerXXXX' changed from ONLINE to OFFLINE (COMMUNICATION_ERROR): Error (-3): invalid argument
  2. The item state does not update when turned ON or OFF, or when Brightness is changed using physical buttons.
  3. The item label is still HS210.

Thanks!

Hmm that’s strange. It looks like some changes I made didn’t end up in the original pull request. Sorry for that. There is a jar that should contain the fix (I think, didn’t check) it’s here: edit: removed link, the updated binding is available in the eclipse market place

I’m going to make a fix asap.