{"id":21149238,"url":"https://github.com/pascal-fb-martin/housekasa","last_synced_at":"2026-04-28T19:02:45.852Z","repository":{"id":74188446,"uuid":"439531772","full_name":"pascal-fb-martin/housekasa","owner":"pascal-fb-martin","description":"A web service to  control TP-Link Kasa switches and plugs","archived":false,"fork":false,"pushed_at":"2026-04-21T22:30:29.000Z","size":547,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-22T00:31:52.189Z","etag":null,"topics":["home-automation","kasa","kasa-plugs","kasa-switches","microservice","raspberry-pi","smart-home","smart-switch","tp-link-kasa","tplink","tplink-kasa"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pascal-fb-martin.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2021-12-18T04:58:40.000Z","updated_at":"2026-04-21T22:30:33.000Z","dependencies_parsed_at":null,"dependency_job_id":"d51f3cab-4411-4368-82f0-1cbb2f89a00d","html_url":"https://github.com/pascal-fb-martin/housekasa","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/pascal-fb-martin/housekasa","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pascal-fb-martin%2Fhousekasa","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pascal-fb-martin%2Fhousekasa/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pascal-fb-martin%2Fhousekasa/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pascal-fb-martin%2Fhousekasa/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pascal-fb-martin","download_url":"https://codeload.github.com/pascal-fb-martin/housekasa/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pascal-fb-martin%2Fhousekasa/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32394478,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-28T14:34:11.604Z","status":"ssl_error","status_checked_at":"2026-04-28T14:32:37.009Z","response_time":56,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["home-automation","kasa","kasa-plugs","kasa-switches","microservice","raspberry-pi","smart-home","smart-switch","tp-link-kasa","tplink","tplink-kasa"],"created_at":"2024-11-20T09:33:21.180Z","updated_at":"2026-04-28T19:02:45.845Z","avatar_url":"https://github.com/pascal-fb-martin.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# HouseKasa\n\nA House web service to control TP-Link Kasa devices (lights, plugs..)\n\n## Overview\n\nThis is a web server to give access to TP-Link Kasa WiFi devices. This server can sense the current status and control the state of each device. The web API is meant to be compatible with the [House control API](https://github.com/pascal-fb-martin/houseportal/blob/master/controlapi.md).\n\nSee the [gallery](https://github.com/pascal-fb-martin/housekasa/blob/main/gallery/README.md) for a view of HouseKasa's web UI.\n\nThis service is not really meant to be accessed directly by end-users: these should use [houselights](https://github.com/pascal-fb-martin/houselights) to control Kasa devices.\n\nSo far HouseKasa has been tested with the following US models:\n\n* HS220\n* HS200\n* KP400\n* EP10\n\n## Installation\n\n* Install the OpenSSL development package(s).\n* Install [echttp](https://github.com/pascal-fb-martin/echttp).\n* Install [houseportal](https://github.com/pascal-fb-martin/houseportal).\n* Clone this GitHub repository.\n* make\n* sudo make install\n* Edit /etc/house/kasa.json (see below)\n\nOtherwise installing [houselights](https://github.com/pascal-fb-martin/houselights) is recommended, but not necessarily on the same computer.\n\n## Configuration\n\nThe preferred method is to configure the devices from the Configure web page.\nThe configuration is stored in file /etc/house/kasa.json. A typical example of configuration is:\n\n```\n{\n    \"kasa\" : {\n        \"devices\" : [\n            {\n                \"name\" : \"light1\",\n                \"address\" : \"xxxxxxxxxx\",\n                \"description\" : \"a Kasa light\"\n            },\n            {\n                \"name\" : \"light2\",\n                \"address\" : \"xxxxxxxxxx\",\n                \"description\" : \"another Kasa light\"\n            }\n        ]\n    }\n}\n```\n\n## Device Setup\n\nEach device must be setup using the Kasa phone app. The protocol for setting up devices has not been reverse engineered at that time.\n\n## Kasa Protocol\n\nThis section describes the subset of the Kasa protocol that is implemented in HouseKasa. This is not a complete description of the protocol, only of the subset implemented by HouseKasa: others have provided more extensive documentation of that protocol (such as [softCheck](https://github.com/softScheck/tplink-smartplug/blob/master/tplink-smarthome-commands.txt)).\n\nHouseKasa actually uses a variant of the Kasa protocol not widely described on the Internet, and that does not exactly match what the Kasa app is using. In this section we will first describe how the Kasa app interacts with some Kasa device types, and then describe what HouseKasa actually uses.\n\nNote that some discrepancies in the protocol used by different device types might be attributed to the firmware version: when identifying a device it is recommended to consider both the type of device and the firmware version.\n\nAll commands and responses contain an encrypted JSON structure. The JSON structures are described based on examples of real network captures, with some values obscured.\n\nThe encryption cypher is a byte XOR operation with the previous (encrypted) bytes, except for the first byte which is encrypted with 0xab. Since XOR is its own inverse, encryption and decryption are the same operation, except for which byte value to use as the key for the next byte: either the original value (decryption) or the values after the XOR (encryption).\n\nThe Kasa protocol uses port 9999, both for UDP and TCP.\n\nUDP packets contain the encrypted JSON as-is. The size of the JSON data is determined the size of the UDP packet.\n\nTCP-based commands are prefixed with a big-endian 4 bytes integer, which represents the length of the JSON string that follows (the TCP protocol provide no information about the structure of the application data, so applications typically must use their own \"framing\" method).\n\n#### HS220\n\nThe example bellow shows the discovery of a HS220 (US model), using UDP:\n\n```\n\u003e {\"system\":{\"get_sysinfo\":{}}}\n\u003c {\"system\":{\"get_sysinfo\":{\"sw_ver\":\"1.5.11 Build 200214 Rel.152651\",\n  \"hw_ver\":\"1.0\",\"mic_type\":\"IOT.SMARTPLUGSWITCH\",\"model\":\"HS220(US)\",\n  \"mac\":\"xx:xx:xx:xx:xx:xx\",\"dev_name\":\"Smart Wi-Fi Dimmer\",\n  \"alias\":\"TP-LINK_Smart Dimmer_ECC6\",\"relay_state\":0,\"brightness\":87,\n  \"on_time\":0,\"active_mode\":\"none\",\"feature\":\"TIM\",\"updating\":0,\"icon_hash\":\"\",\n  \"rssi\":-57,\"led_off\":0,\"longitude_i\":0,\"latitude_i\":0,\n  \"hwId\":\"xxxxxxxxxxxxxxxxxxxxxxxxxxxx95BD\",\n  \"fwId\":\"00000000000000000000000000000000\",\n  \"deviceId\":\"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx6D52\",\n  \"oemId\":\"xxxxxxxxxxxxxxxxxxxxxxxxxxxx4CD6\",\n  \"preferred_state\":[{\"index\":0,\"brightness\":100},{\"index\":1,\"brightness\":75},{\"index\":2,\"brightness\":50},{\"index\":3,\"brightness\":25}],\n  \"next_action\":{\"type\":-1},\"err_code\":0}}}\n```\n\nIn the example above, the system.get_sysinfo command returns a long structure that indicates the model of the device (system.get_sysinfo.model) and its device ID (system.get_sysinfo.deviceID.\n\nThis device is a single dimmer, and the state is directly part of the system.get_sysinfo structure (e.g. system.get_sysinfo.relay_state). It has four preferred levels (in array system.get_sysinfo.preferred_state: 100%, 75%, 50% and 25%). These preferred levels are shown on the phone app as shortcuts in the dimmer control screen.\n\nThe device can be turned on and of, without changing the dimmer level:\n\n```\n\u003e {\"context\":{\"source\":\"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"},\"smartlife.iot.dimmer\":{\"set_switch_state\":{\"state\":0}}}\n\u003c {\"smartlife.iot.dimmer\":{\"set_switch_state\":{\"err_code\":0}}}\n```\n\nThe \"smartlife.iot.dimmer.set_switch_state.state\" item controls if the light is on (1) or off (0).\n\nThe context structure is optional. \n\nThe brightness level is also controllable:\n\n```\n\u003e {\"context\":{\"source\":\"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"},\n  \"smartlife.iot.dimmer\":{\"set_brightness\":{\"brightness\":75},\n  \"set_switch_state\":{\"state\":1}}}\n\u003c {\"smartlife.iot.dimmer\":{\"set_brightness\":{\"err_code\":0},\n  \"set_switch_state\":{\"err_code\":0}}}\n```\n\nThat command actually achieves two results: it both sets the dimmer level (\"smartlife.iot.dimmer.set_brightness.brightness\") and turns the light on (\"smartlife.iot.dimmer.set_switch_state.state\"). I guess the brightness level has little immediate effect when the light is turned off anyway.\n\n(HouseKasa does not support controlling the dimmer level at this time.)\n\n#### KP400\n\nThe example below shows the discovery of a KP400 (US model), using UDP:\n\n```\n\u003e {\"system\":{\"get_sysinfo\":{}}}\n\u003c {\"system\":{\"get_sysinfo\":{\"sw_ver\":\"1.0.6 Build 200821 Rel.090909\",\n  \"hw_ver\":\"2.0\",\"model\":\"KP400(US)\",\n  \"deviceId\":\"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx042E\",\n  \"oemId\":\"xxxxxxxxxxxxxxxxxxxxxxxxxxxxE646\",\n  \"hwId\":\"xxxxxxxxxxxxxxxxxxxxxxxxxxxxBE38\",\"rssi\":-52,\"longitude_i\":0,\n  \"latitude_i\":0,\"alias\":\"TP-LINK_Smart Plug_BC6F\",\"status\":\"new\",\n  \"mic_type\":\"IOT.SMARTPLUGSWITCH\",\"feature\":\"TIM\",\"mac\":\"xx:xx:xx:xx:xx:xx\",\n  \"updating\":0,\"led_off\":0,\n  \"children\":[{\"id\":\"00\",\"state\":1,\"alias\":\"Kasa_Smart Plug_BC6F_0\",\"on_time\":20,\"next_action\":{\"type\":-1}},{\"id\":\"01\",\"state\":1,\"alias\":\"Kasa_Smart Plug_BC6F_1\",\"on_time\":20,\"next_action\":{\"type\":-1}}],\n  \"child_num\":2,\"ntc_state\":0,\"err_code\":0}}}\n```\n\nThe JSON returned is similar, but not all the same items are present (probably due to the different firmware version).\n\nThis device actually control two outlets independently, which is described in array \"system.get_sysinfo.children\". Note that each outlet has a two-character ID (00 and 01).\n\nNote the \"system.get_sysinfo.deviceID\" value: this will be important later, when controlling each outlet.\n\nEach outlet can be controlled independently by providing an identifier for that outlet. The outlet ID is the concatenation of the deviceID and outlet ID:\n\n```\n\u003e {\"context\":{\"child_ids\":[\"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx042E00\"],\n  \"source\":\"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"},\n  \"system\":{\"set_relay_state\":{\"state\":1}}}\n\u003c {\"system\":{\"set_relay_state\":{\"err_code\":0}}}\n```\n\nThe array context.child_ids indicates which outlet(s) should be controlled. Apparently the protocol allows controlling multiple outlets in one command. The \"system.set_relay_state.state\" value indicates if the outlet should be turned off (0) or on (1).\n\nThe source item is optional. The context struct is mandatory (at least if one wants to control a subset of the outlets).\n\n#### Alias\n\nEach device or outlet can be assigned an alias, which is a way for the user to give a name to each:\n\n```\n\u003e {\"system\":{\"set_dev_alias\":{\"alias\":\"xxxxxxx\"}}}\n\u003c {\"system\":{\"set_dev_alias\":{\"err_code\":0}}}\n```\n\nNote that a multi-plug device like the KP400 has both a global alias and an individual alias for each plug.\n\n### How HouseKasa Uses the Kasa Protocol\n\nThis section describes what subset and variant of the Kasa protocol is used by the HouseKasa service.\n\nThe Kasa devices actually respond to all commands on the UDP port, not just to discovery commands. Thus HouseKasa only use UDP, port 9999.\n\nDiscovery of which devices are present on the network is made on a periodic basis (about every 30 seconds) using a UDP broadcast with the command:\n\n```\n{\"system\":{\"get_sysinfo\":{}}}\n```\n\nHouseKasa searches for the following items in the response:\n\n```\nsystem.get_sysinfo.model\nsystem.get_sysinfo.alias (if not set in HouseKasa)\nsystem.get_sysinfo.relay_state (may not be present)\nsystem.get_sysinfo.deviceId\nsystem.get_sysinfo.children[].id\nsystem.get_sysinfo.children[].state (may not be present)\nsystem.get_sysinfo.children[].alias (if not set in HouseKasa)\n```\n\nIf the array \"system.get_sysinfo.children\" is present, its state and alias elements take precedence over \"system.get_sysinfo.relay_state\" and \"system.get_sysinfo.alias\".\n\nThe state of every known device is maintained by doing an unicast system.get_sysinfo request every 10 seconds, except after issuing a command, in which case the request is made at higher frequency for a minute, or else until the requested state was reached.\n\nThe Kasa devices (KP400 and HS220) all accept the \"system.set_relay_state\" command. However since the KP400 has two outlets, the exact outlet targetted must be specified using \"context.child_ids\". It happens that the HS220 also accept the presence of an outlet ID: it just ignores it. So the command sent for on and off is:\n\n```\n{\"context\":{\"child_ids\":[\"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"]},\"system\":{\"set_relay_state\":{\"state\":x}}}\n```\n\nHouseKasa uses the response only as a prompt for sending a state request (\"system.get_sysinfo\"). This is because the response does not provide any context, or the state of the device. For example, a response from a KP400 does not indicate which plug this is related to. Since the only reliable information is the device IP address (UDP packet source adress), the simplest is to immediately query that device.\n\nHouseKasa will query the state of each known device periodically (unicast UDP packet) to verify that the device is still present and to maintain its state current (the device could be controlled by others).\n\n## Command line tool\n\nThis software provides a tool named `kasa` to test controls of device:\n\n```\nkasa\n```\n\nQuery the status of all devices present. This is a way to do some manual discovery.\n\n```\nkasa *host*\n```\n\nQuery the status of the specified device.\n\n```\nkasa *host* alias *name*\n```\n\nSet the alias name for this device. This alias name is stored in the device.\n\n```\nkasa *host* on|off [hs220]\n```\n\nSet the device on or off. The hs220 option makes the tool use the alternative syntax accepted by the HS220 devices.\n\n```\nkasa *host* on|off kp400 *n*\n```\n\nSet an outlet on or off on a KP400 device. The KP400 has multiple outlets that can be controled independently, which is why the outlet number must be specified.\n\n```\nkasa -h|--help|help\n```\n\nShow the tool's help.\n\n## Debian Packaging\n\nThe provided Makefile supports building private Debian packages. These are _not_ official packages:\n\n- They do not follow all Debian policies.\n\n- They are not built using Debian standard conventions and tools.\n\n- The packaging is not separate from the upstream sources, and there is\n  no source package.\n\nTo build a Debian package, use the `debian-package` target:\n\n```\nmake debian-package\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpascal-fb-martin%2Fhousekasa","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpascal-fb-martin%2Fhousekasa","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpascal-fb-martin%2Fhousekasa/lists"}