{"id":49524431,"url":"https://github.com/sv-tools/scrypted-kasa-plugin","last_synced_at":"2026-05-02T02:02:50.509Z","repository":{"id":354803488,"uuid":"1225142187","full_name":"sv-tools/scrypted-kasa-plugin","owner":"sv-tools","description":"TP-Link Kasa plugin for Scrypted — cameras (video, two-way audio, spotlight, siren, status LED), plugs/outlets, wall switches, dimmers, and smart bulbs (on/off, brightness, color, color temperature).","archived":false,"fork":false,"pushed_at":"2026-04-30T07:18:09.000Z","size":88,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-30T07:20:41.654Z","etag":null,"topics":["camera","kasa","scrypted","smarthome"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sv-tools.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":"2026-04-30T01:46:32.000Z","updated_at":"2026-04-30T07:15:40.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/sv-tools/scrypted-kasa-plugin","commit_stats":null,"previous_names":["sv-tools/scrypted-kasa-plugin"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/sv-tools/scrypted-kasa-plugin","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sv-tools%2Fscrypted-kasa-plugin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sv-tools%2Fscrypted-kasa-plugin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sv-tools%2Fscrypted-kasa-plugin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sv-tools%2Fscrypted-kasa-plugin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sv-tools","download_url":"https://codeload.github.com/sv-tools/scrypted-kasa-plugin/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sv-tools%2Fscrypted-kasa-plugin/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32520156,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-02T01:12:54.858Z","status":"online","status_checked_at":"2026-05-02T02:00:05.923Z","response_time":132,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["camera","kasa","scrypted","smarthome"],"created_at":"2026-05-02T02:02:49.654Z","updated_at":"2026-05-02T02:02:50.487Z","avatar_url":"https://github.com/sv-tools.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# scrypted-kasa\n\n[![npm version](https://img.shields.io/npm/v/scrypted-kasa.svg)](https://www.npmjs.com/package/scrypted-kasa)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)\n\nAdds support for the TP-Link Kasa device family to [Scrypted](https://www.scrypted.app): cameras, plugs/outlets,\nswitches/dimmers, and bulbs. Discovery is unified — one UDP/9999 sweep finds everything\nand the plugin routes each device to the right Scrypted interfaces.\n\n## Installation\n\nIn Scrypted:\n\n1. Open the **Plugins** page.\n2. Choose **Install** and enter `scrypted-kasa`.\n3. The plugin appears as **Kasa Plugin** in the device list — see [Setup](#setup) below.\n\n## Cameras\n\n- Reads the camera's proprietary multipart stream (HTTPS port 19443, `/https/stream/mixed`)\n  and re-streams H.264 video + G.711 µ-law audio over local RTSP for Scrypted, HomeKit, the\n  NVR, etc.\n- Streams talk audio back to the camera's speaker via the camera's separate uplink endpoint\n  (HTTPS port 18443, `/https/speaker/audio/g711block`) — usable from HomeKit and any other\n  Scrypted client that supports two-way audio.\n- Exposes the camera's spotlight (when present, e.g. KC420WS) as an `OnOff` child light\n  device in the same room as the camera. Driven by the LINKIE2 control protocol on port\n  10443.\n- Exposes the camera's siren (when present) as an `OnOff` child switch device, also in\n  the camera's room. Triggering it turns the siren on; the camera auto-stops after the\n  duration configured in the Kasa app (default 30 s).\n- Exposes the camera's **status LED** as the camera's own `OnOff` interface, so HomeKit's\n  per-camera \"Link Status Indicator\" toggle drives the LED.\n\n## Plugs / Switches / Dimmers / Bulbs (legacy IOT protocol)\n\nEach device class is its own implementation, descended from a shared `KasaIotDevice`\nbase that holds the relay protocol:\n\n- **`KasaPlug`** — plain plugs/outlets (HS100/HS103/HS105/HS107/HS110/KP100/...). `OnOff`,\n  `ScryptedDeviceType.Outlet`.\n- **`KasaSwitch`** — plain wall switches (HS200/HS210/KS200/...). `OnOff`,\n  `ScryptedDeviceType.Switch`.\n- **`KasaDimmer`** — dimmer plugs and switches (HS220 plug, KS230 3-way switch). `OnOff` +\n  `Brightness`, `ScryptedDeviceType.Light` (dimmable devices are exposed as lights since\n  they're almost always wired to a light fixture, matching the Kasa app's UX).\n- **`KasaBulb`** — smart bulbs (LB1xx, KL1xx). `OnOff` + `Brightness`, plus\n  `ColorSettingHsv` for color bulbs and `ColorSettingTemperature` for variable-temperature\n  bulbs.\n\nMulti-outlet plug strips (HS300, KP303) aren't modeled yet — discovery skips them.\n\nStream protocol described in [go2rtc's `pkg/kasa`](https://github.com/AlexxIT/go2rtc/tree/master/pkg/kasa);\ncamera control protocols (LINKIE2, talk endpoint, status LED) modeled after the official\nKasa iOS app's network traffic. The plug/bulb/switch protocol is the well-documented\nlegacy \"smarthome\" TCP/9999 wire format.\n\nTested camera models: KD110, KC200, KC401, KC420WS, EC71.\n\n## Setup\n\n### Auto-discovery (recommended)\n\n1. Install the plugin.\n2. Open the plugin's page and click **Discover Devices**. Discovery runs only when you\n   click the button — there are no background scans.\n3. Every LAN-visible Kasa device appears in the list — cameras, plugs, switches, dimmers,\n   and bulbs are all discovered together.\n4. For each device you want to adopt, fill in the adoption form and click adopt:\n   - **Name** — pre-filled with the device's alias or model; edit to taste.\n   - **Room** — optional, picked from a dropdown of rooms already in use by other\n     Scrypted devices, or type a new one.\n   - **Username** / **Password** *(cameras only)* — your Kasa account email and password.\n     After the first camera is configured these are pre-populated from any other Kasa\n     camera in Scrypted.\n\nAdopted devices get their IP, port, name, model, MAC, serial number, and firmware version\npopulated automatically. The manufacturer is reported as `TP-Link Kasa`.\n\nDiscovery sends a single UDP/9999 burst on each connected /24:\n\n- A **broadcast** of the IOT.SMARTHOME `get_sysinfo` query.\n- A **paced unicast** of the same query at every IP on the subnet (~3 ms between sends).\n  Newer firmwares (e.g. KC420WS cameras) drop broadcast probes but still answer when the\n  packet is addressed directly. Pacing keeps the kernel/network from coalescing or\n  dropping the burst.\n\nIf a device is on a different VLAN/broadcast domain or a non-/24 subnet, use the manual\nsetup below.\n\n### Manual setup\n\n1. Install the plugin.\n2. Use **Add Device** to create a new device. The Add form takes:\n   - **Type** — Camera, Plug, Switch, Dimmer, or Bulb.\n   - **Name** — required.\n   - **Room** — optional, with the same dropdown of existing rooms as the discovery flow.\n3. After creation, open the device's settings and fill in:\n   - **IP Address** of the device on your LAN\n   - **Port** (default 19443 for cameras, 9999 for everything else)\n   - For cameras: **Username** + **Password** (your Kasa account email + password)\n\n## How it works\n\n### Receive (camera → Scrypted)\n\n- The plugin opens a single HTTPS connection to the camera on port 19443 and parses the\n  `multipart/x-mixed-replace` body, splitting it into video parts (`video/x-h264`,\n  annex-b) and audio parts (`audio/g711u`).\n- It scans the first H.264 frames for SPS (NAL 7) and PPS (NAL 8) and inlines them into\n  the locally served SDP as `sprop-parameter-sets` + `profile-level-id`. This lets short\n  -timeout consumers (HomeKit, browser players) pick up the codec immediately.\n- It then runs ffmpeg in codec-copy mode, re-packetizing the raw H.264 and G.711 µ-law\n  streams as RTP into a local RTSP server that Scrypted reads from.\n\n### Talk (Scrypted → camera speaker)\n\n- When a client engages two-way audio, the plugin opens a long-lived chunked POST to\n  `https://\u003cip\u003e:18443/https/speaker/audio/g711block` and ffmpeg-transcodes the client's\n  audio to 8 kHz mono G.711 µ-law.\n- Each 20 ms (160 byte) audio block is wrapped in a `multipart/x-mixed-replace` part with\n  `Content-Type: audio/g711u` and streamed to the camera. Empty `audio/heartbeat` parts\n  are sent every 3 s during silence to keep the connection alive — same pattern the Kasa\n  app uses.\n\n### Spotlight / control plane\n\n- The Kasa app talks to the camera's \"LINKIE2\" RPC on port 10443 for non-streaming\n  features. Wire format: HTTPS POST `/data/LINKIE2.json`, body `application/x-www-form-\n  urlencoded` with a single `content=\u003cbase64(xor_ab(json))\u003e` field. The XOR-AB autokey\n  cipher is the same one used by Kasa's UDP/9999 discovery protocol.\n- On adoption (and whenever credentials change) the plugin probes three LINKIE2 endpoints\n  serially:\n  - `smartlife.cam.ipcamera.dayNight.get_force_lamp_state` — if it returns `on`/`off`, a\n    child `OnOff` light device named \"\u003ccamera\u003e Spotlight\" is registered in the camera's\n    room.\n  - `smartlife.cam.ipcamera.siren.get_state` — same pattern; produces a \"\u003ccamera\u003e Siren\"\n    switch child device.\n  - `smartlife.cam.ipcamera.led.get_status` — drives the camera's own `OnOff` so HomeKit\n    can bind its `CameraOperatingModeIndicator` (a.k.a. \"Status Light\") characteristic to\n    it. Enable **Link Status Indicator** in the HomeKit plugin's per-camera settings to\n    activate the binding.\n- Toggling any of the three calls the matching `set_*` method with `{\"value\": \"on\"|\"off\"}`.\n\n## Notes / limitations\n\n- **Authentication** uses the cloud account password. All three endpoints take Basic auth\n  with the plain Kasa email as the username; the password format differs by endpoint —\n  receive uses base64(plaintext), talk and LINKIE2 use md5_hex(plaintext). The plugin\n  uses each in the right place.\n- LINKIE2 requests must include a `User-Agent: Kasa/...` header and the `Authorization`\n  header on the very first request — the camera silently drops requests that don't look\n  enough like the official app.\n- The camera presents a self-signed TLS certificate; certificate verification is disabled.\n- Auto-discovery sweeps the local /24 only. Larger subnets (e.g. /23) are skipped to\n  avoid flooding.\n\n## Development\n\n```bash\ngit clone https://github.com/sv-tools/scrypted-kasa-plugin.git\ncd scrypted-kasa-plugin\nnpm install\n```\n\nCommon scripts:\n\n| Command | Purpose |\n|---|---|\n| `npm run build` | Webpack bundle into `out/main.nodejs.js` (+ `plugin.zip`) |\n| `npm run fmt` | Format `src/**/*.ts` with prettier |\n| `npm run fmt:check` | Verify formatting — what CI runs |\n| `npm run lint` | ESLint |\n| `npm run lint:fix` | ESLint with auto-fix |\n| `npm run scrypted-deploy \u003chost\u003e` | Build and deploy to a Scrypted server |\n\nCI on every PR runs `fmt:check`, `lint`, and `build`. Releases publish to npm\nautomatically when a `v*` tag is pushed (e.g. `git tag v1.2.3 \u0026\u0026 git push --tags`).\nThe publish workflow signs the package with [npm provenance](https://docs.npmjs.com/generating-provenance-statements)\nvia GitHub OIDC.\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsv-tools%2Fscrypted-kasa-plugin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsv-tools%2Fscrypted-kasa-plugin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsv-tools%2Fscrypted-kasa-plugin/lists"}