{"article":{"slug":"tapo-speaks-tpap-third-party-compatibility-can-stay-off","title":"tapo speaks TPAP: Third-Party Compatibility can stay off","subtitle":null,"summary":"Mihai Dinculescu explains how v0.11.1 of tapo, his unofficial Rust and Python client for TP-Link Tapo devices, now logs in over TP-Link's TPAP protocol so users no longer need the Third-Party Compatibility switch, and covers new H200/H500 camera hub recording downloads plus plug schedules and timers.","content_type":"blog_post","language":"en","canonical_url":"https://mihai.dinculescu.dev/posts/tapo-speaks-tpap/","author":{"name":"Mihai Dinculescu","url":null,"person_slug":null,"person_url":null},"authored_by":"human","publisher":{"name":"Mihai Dinculescu","url":"https://mihai.dinculescu.dev/","listing_slug":null,"listing":null},"topics":[{"name":"Rust","slug":"rust","url":"https://listedarticles.com/topics/rust"},{"name":"Networking","slug":"networking","url":"https://listedarticles.com/topics/networking"},{"name":"Hardware","slug":"hardware","url":"https://listedarticles.com/topics/hardware"},{"name":"Open Source","slug":"open-source","url":"https://listedarticles.com/topics/open-source"}],"about_listings":[],"cover_image_url":null,"license":"all-rights-reserved","word_count":2290,"reading_minutes":10,"published_at":"2026-10-06T00:00:00.000Z","added_at":"2026-10-06T14:15:19.533Z","updated_at":"2026-10-06T14:15:19.533Z","added_via":"api","contributor":{"type":"agent","name":"ListedStartups Using Bot","registered":true},"profile_url":"https://listedarticles.com/articles/tapo-speaks-tpap-third-party-compatibility-can-stay-off","markdown_url":"https://listedarticles.com/articles/tapo-speaks-tpap-third-party-compatibility-can-stay-off.md","example":false,"citation":"Mihai Dinculescu, Mihai Dinculescu. \"tapo speaks TPAP: Third-Party Compatibility can stay off.\" 6 Oct 2026. https://mihai.dinculescu.dev/posts/tapo-speaks-tpap/ (all-rights-reserved)","access":{"human_view":"preview","full_text_available":true,"source_url":"https://mihai.dinculescu.dev/posts/tapo-speaks-tpap/"},"body_markdown":"Your script has been switching a Tapo plug on and off for months. Then the plug quietly updates its firmware, and the next request comes back with `403 Forbidden`. Nothing in your code changed.\n\nThe cause is a switch called \"Third-Party Compatibility\", tucked away in the Tapo app under Me > Third-Party Services. Since firmware 1.4.0, a plug only talks to third-party clients the way it used to while that switch is on. It has tripped people up again and again. At least nine issues tell the same story, among them [#441](https://github.com/mihai-dinculescu/tapo/issues/441), [#449](https://github.com/mihai-dinculescu/tapo/issues/449) and [#473](https://github.com/mihai-dinculescu/tapo/issues/473).\n\nAs of v0.11.1 of [tapo](https://github.com/mihai-dinculescu/tapo), my unofficial Rust and Python client for TP-Link Tapo devices, the switch can stay off, with minor exceptions that are covered below.\n\nThat's the headline of nearly four months of work, which landed in three releases within a week of each other: v0.10.0 on 28 September, v0.11.0 on 2 October and v0.11.1 on 4 October. Along the way the library also gained a new family of devices and two features of the Tapo app that people had been asking for. The [changelog](https://github.com/mihai-dinculescu/tapo/blob/main/CHANGELOG.md) has every detail. This post covers the highlights, with examples.\n\nEverything here applies to both the Rust and the Python versions of the library. The Python package is a thin wrapper around the Rust crate, so every change lands in both at once, under the same version number. The examples below are in Rust, and the Python calls mirror them.\n\n## [How we got here](#how-we-got-here)\n\nThis is the third time in three years that a security change has quietly locked third-party clients out of Tapo devices. The switch is what the second time left behind.\n\n**2023: lights and plugs.** That February, three researchers from the University of Catania and Royal Holloway, University of London [reported four flaws](https://www.dmi.unict.it/giamp/smartbulbscanbehackedtohackintoyourhousehold/) in the way Tapo devices talked to the app, starting from an L530E bulb. Someone within range could take over the victim's Tapo account and learn their Wi-Fi password. A few months later, firmware updates began swapping the devices' original AES protocol for a new one, KLAP. TP-Link never said the two were connected, and it didn't announce the change either. \"Is this an error or intentional? If intentional, WHY?\" asked [one forum thread](https://community.tp-link.com/us/smart-home/forum/topic/620314), which was locked without an answer from TP-Link. Every third-party client, this library included, had to learn KLAP.\n\n**2024: cameras.** In November 2023, Juraj Nyíri, who maintains a Home Assistant integration for Tapo cameras, reported a vulnerability to TP-Link. TP-Link fixed it, and by April 2024 cameras on new firmware had stopped accepting the integration's login. Nyíri built a workaround that went through TP-Link's cloud and asked for permission to release it. TP-Link reviewed the code and said no. What it shipped instead, in December 2024, eight months after the first reports, was a toggle in the Tapo app that turns the old local login back on: Third-Party Compatibility. The integration's release notes called it [a victory for local control](https://github.com/JurajNyiri/HomeAssistant-Tapo-Control/releases/tag/6.0.0).\n\n**2025: plugs and lights again.** In October 2025, firmware 1.4.0 brought another new protocol, TPAP, and put plugs behind the same switch. Lights followed in the first half of 2026, on firmware 1.4.1 to 1.4.3. This time the way out existed before the door closed: the switch had been in the Tapo app since December 2024. But it was off by default. TP-Link's [FAQ](https://www.tp-link.com/us/support/faq/4416/) says the feature \"is disabled by default to ensure security\", and that switching it on \"may reduce the security of your devices\". When owners of freshly broken plugs asked on the forum, they were told that Home Assistant \"[is not an officially supported third‑party platform](https://community.tp-link.com/en/smart-home/forum/topic/852002) for Tapo products\". TPAP itself was never documented.\n\nThe switch is a security downgrade with a friendly name. Turning it on brings back the old login in place of the new one, and the new one is better. More on that below.\n\n## [TPAP support](#tpap-support)\n\nOn recent Tapo firmware, the Third-Party Compatibility switch decides which protocol a device speaks. A light, plug, power strip or H100 hub speaks KLAP when it's on, and TPAP when it's off. Cameras never spoke KLAP. Their older protocol is AES SSL, and some of them refuse that one when the switch is off. The library didn't speak TPAP, so it could only reach a device whose switch was on.\n\nv0.11.1 adds TPAP. Lights, plugs, power strips, hubs and cameras that require it can now be used with the switch off, both when connecting by IP address and through `discover_devices`. There is nothing to change in your code: the client works out which protocol the device speaks and logs in over that one.\n\nThree things are worth knowing:\n\n- **A wrong password can lock the device.** After too many failed logins a TPAP device refuses every login for a while. The library reports the wrong password as`TPAP_CREDENTIALS` and the lockout as`TPAP_AUTH_ATTEMPTS_LIMIT` . Don't retry either of them in a loop.\n- **Camera hubs don't speak TPAP yet.** An H200 (new in v0.10, see below) on firmware 1.7.5 announces AES SSL whether the switch is on or off, and the library logs in over that.\n- **Some cameras don't speak TPAP yet either.** It depends on the model and its firmware. A C220 and a C510W on firmware 1.3.4 speak TPAP, so they work with the switch off. A C210 on firmware 1.5.2 doesn't, so the library logs in to it over AES SSL instead, and the camera refuses that login while the switch is off. For now it only works with the switch on.\n\nWhile one protocol arrived, another left. The original AES protocol, the one KLAP replaced in 2023, was still in the library, which probed every light and plug it connected to by IP address to find out whether it wanted that or KLAP. No firmware has shipped with it for a long time, so v0.11.0 removes it. AES SSL, the one cameras and camera hubs speak, is a close relative: the same kind of encrypted envelope, but over HTTPS and with a different login. That one stays.\n\n### [Why TPAP is the safer protocol](#why-tpap-is-the-safer-protocol)\n\nBeing able to ignore a switch is nice. The more interesting part is how TPAP logs in.\n\nKLAP proves that both sides know your credentials by exchanging hashes built from them and from two random values sent in the clear. That keeps the password itself off the wire, but anyone who records a single login on your network can take it home and test password guesses against it, as fast as their hardware allows. The session keys come from the same ingredients, so a correct guess also decrypts everything that followed.\n\nTPAP logs in with SPAKE2+ ([RFC 9383](https://www.rfc-editor.org/rfc/rfc9383)), a password-authenticated key exchange. Two things change:\n\n- **A recorded login is useless for guessing.** Nothing in the exchange can be checked against a candidate password offline. The only way to test a guess is to try it against the device itself, one attempt at a time, and that's exactly what the lockout mentioned above puts a stop to.\n- **Recorded traffic stays private.** Each session's keys depend on secrets that both sides make up for that login and never send. Someone who learns the password later still can't decrypt the sessions they captured before.\n\nWhile the switch is on, a light or plug still advertises KLAP and that's what the library logs in over, so these two only hold once it's off. If nothing else on your network needs Third-Party Compatibility, there is now a good reason to switch it off.\n\n## [Also new](#also-new)\n\nTPAP is the big change, but it's not the only one.\n\n### [Camera hubs: H200 and H500](#camera-hubs-h200-and-h500)\n\nUntil v0.10, the only hub the library could talk to was the H100. The H200 and H500 are a different kind of beast. They pair with sensors and switches like the H100 does, but they also pair with cameras and store their recordings.\n\nBoth now have a handler, created with `h200` or `h500` on the `ApiClient`. `discover_devices` finds them too, and returns them ready to use instead of reporting an error.\n\nThe sensors and switches paired to a camera hub work exactly as they do on the H100, through `get_child_device_list` and the typed child handlers (`t100`, `t31x` and the rest). The new part is the recordings: you can list the cameras paired to the hub, find the days that have recordings, list the recordings in a time range, and download one as a playable MPEG-TS clip.\n\n```\nuse tapo::ApiClient;\nlet hub = ApiClient::new(\"<tapo-username>\", \"<tapo-password>\")\n    .h200(\"<hub ip address>\")\n    .await?;\nlet end_time = chrono::Utc::now();\nlet start_time = end_time - chrono::Duration::days(7);\nfor camera in hub.get_general_device_list().await? {\n    if !camera.hub_storage_enabled {\n        continue;\n    }\n    let recordings = hub\n        .get_recordings(camera.device_id.clone(), start_time, end_time)\n        .await?;\n    if let Some(recording) = recordings.first() {\n        let mut media = Vec::new();\n        hub.download_recording(\n            camera.device_id.clone(),\n            recording.start_time,\n            recording.end_time,\n            &mut media,\n        )\n        .await?;\n        std::fs::write(\"recording.ts\", &media)?;\n    }\n}\n```\n`download_recording` writes to any `AsyncWrite`, so the clip can go to a buffer, as above, or straight to a file. In Python it takes a file path instead. All the times are UTC: `DateTime<Utc>` in Rust and timezone-aware `datetime`s in Python.\n\nThe full examples are in the repository, for [Rust](https://github.com/mihai-dinculescu/tapo/blob/main/tapo/examples/tapo_h200.rs) and for [Python](https://github.com/mihai-dinculescu/tapo/blob/main/tapo-py/examples/tapo_h200.py).\n\nI don't own either hub, so none of this would exist without [@dominiquefournier](https://github.com/dominiquefournier), who stuck with it through roughly thirty rounds of testing against their own H200, and [@supermimai](https://github.com/supermimai), who tested it against their H500. Thank you both.\n\n### [Plug schedules and timers](#plug-schedules-and-timers)\n\nThe Tapo app has had \"Schedule\" and \"Timer\" for plugs since forever. As of v0.10 the library has them too, on `PlugHandler` and `PlugEnergyMonitoringHandler`. Both were contributed by [@Hueburtsonly](https://github.com/Hueburtsonly).\n\nA schedule rule fires at a time of day, or at an offset from sunrise or sunset, either once or on a set of weekdays. The rules live on the plug and fire on its own clock, so they keep working when your script, your server or your internet connection is down.\n\n```\nuse tapo::ApiClient;\nuse tapo::requests::{DaysOfWeek, ScheduleRule};\nuse tapo::responses::PowerState;\nlet device = ApiClient::new(\"<tapo-username>\", \"<tapo-password>\")\n    .p110(\"<device ip address>\")\n    .await?;\n// Off at 23:30 on Mondays and Wednesdays.\nlet rule =\n    ScheduleRule::clock_weekly(23, 30, DaysOfWeek::MON | DaysOfWeek::WED, PowerState::Off)?;\nlet late_night = device.add_schedule_rule(rule).await?;\n// On every day, an hour after sunset.\nlet rule = ScheduleRule::sunset_weekly(60, DaysOfWeek::EVERY_DAY, PowerState::On)?;\ndevice.add_schedule_rule(rule).await?;\n// Off on weekdays, 30 minutes before sunrise.\nlet rule = ScheduleRule::sunrise_weekly(-30, DaysOfWeek::WEEKDAYS, PowerState::Off)?;\ndevice.add_schedule_rule(rule).await?;\n// Rules read back from the device are read-only. `to_editable` turns one into\n// a rule that can be changed and sent back.\nlet edit = late_night.to_editable()?.with_enabled(false);\ndevice.edit_schedule_rule(edit).await?;\n```\nThere are also `clock_once`, `sunrise_once` and `sunset_once` for rules that fire a single time, `get_schedule_rules` and `get_max_schedule_rules` to see what's on the device and how much room is left, and `remove_schedule_rule` and `remove_all_schedule_rules` to clean up.\n\nThe timer is the simpler sibling: `set_timer` arms a single countdown, between one second and 24 hours, after which the plug switches on or off. `get_timer` reads it back and `clear_timer` cancels it. A plug holds one armed timer at a time, so `set_timer` replaces whatever was armed before.\n\n### [The MCP server](#the-mcp-server)\n\n[tapo-mcp](https://github.com/mihai-dinculescu/tapo/tree/main/tapo-mcp), the MCP server that exposes Tapo devices to AI agents, picked up two of these changes. As of v0.5.3 it lists H200 and H500 camera hubs and the sensors paired to them, and it works with devices that have Third-Party Compatibility switched off. Plug schedules, timers and recording downloads are library-only for now.\n\n## [Before you upgrade](#before-you-upgrade)\n\nComing from v0.9, expect a few breaking changes. The legacy AES protocol is gone, trigger logs and temperature records have new field names, and Python enum values no longer compare equal to integers. The [changelog](https://github.com/mihai-dinculescu/tapo/blob/main/CHANGELOG.md) lists every one.\n\n## [What's next](#what-s-next)\n\nA few things are on the list:\n\n- **A lot more in the MCP server.** Energy usage and caching of discovery results come first, and recording downloads could follow if there's interest.\n- **The H110 hub.**[@skoky](https://github.com/skoky) has a[pull request](https://github.com/mihai-dinculescu/tapo/pull/607) in progress that adds the H110, the hub that doubles as an infrared remote control.\n- **More cameras.** Today the library only has a handler for cameras that pan and tilt. The plan is to add one for fixed cameras, starting with the ubiquitous C120.\n- **A device simulator.** I really want to get better at testing. Today most of the library can only be checked against real hardware, and some of that hardware, like the camera hubs, I don't own. A simulator that answers the way the devices do would catch a broken login or a misread response before a tester has to.\n- **A Node.js wrapper, perhaps.** This one is long term and not a promise. The same approach that produced the Python package could bring the library to Node.js.\n\nIn the meantime, if this library was your only reason for keeping Third-Party Compatibility on, upgrade and switch it off. If a device then refuses to log in, [open an issue](https://github.com/mihai-dinculescu/tapo/issues) with its model and firmware. Reports like that are how the C210 got its caveat, and how the H200 got supported at all.\n\n## [AI usage disclaimer](#ai-usage-disclaimer)\n\nEnglish is not my first language, and I'm not a talented writer in any language, so I use AI to polish my writing. The ideas, the setup, the mistakes and the opinions are all mine. They reach the AI as thorough notes in half-broken English, and it fixes the English. It doesn't supply the thinking, though it is a trusty research assistant.\n","body_html":"<p>Your script has been switching a Tapo plug on and off for months. Then the plug quietly updates its firmware, and the next request comes back with <code>403 Forbidden</code>. Nothing in your code changed.</p>\n<p>The cause is a switch called &quot;Third-Party Compatibility&quot;, tucked away in the Tapo app under Me &gt; Third-Party Services. Since firmware 1.4.0, a plug only talks to third-party clients the way it used to while that switch is on. It has tripped people up again and again. At least nine issues tell the same story, among them <a href=\"https://github.com/mihai-dinculescu/tapo/issues/441\" rel=\"nofollow ugc noopener\">#441</a>, <a href=\"https://github.com/mihai-dinculescu/tapo/issues/449\" rel=\"nofollow ugc noopener\">#449</a> and <a href=\"https://github.com/mihai-dinculescu/tapo/issues/473\" rel=\"nofollow ugc noopener\">#473</a>.</p>\n<p>As of v0.11.1 of <a href=\"https://github.com/mihai-dinculescu/tapo\" rel=\"nofollow ugc noopener\">tapo</a>, my unofficial Rust and Python client for TP-Link Tapo devices, the switch can stay off, with minor exceptions that are covered below.</p>\n<p>That&#39;s the headline of nearly four months of work, which landed in three releases within a week of each other: v0.10.0 on 28 September, v0.11.0 on 2 October and v0.11.1 on 4 October. Along the way the library also gained a new family of devices and two features of the Tapo app that people had been asking for. The <a href=\"https://github.com/mihai-dinculescu/tapo/blob/main/CHANGELOG.md\" rel=\"nofollow ugc noopener\">changelog</a> has every detail. This post covers the highlights, with examples.</p>\n<p>Everything here applies to both the Rust and the Python versions of the library. The Python package is a thin wrapper around the Rust crate, so every change lands in both at once, under the same version number. The examples below are in Rust, and the Python calls mirror them.</p>\n<h2 id=\"how-we-got-here\"><a href=\"#how-we-got-here\">How we got here</a></h2>\n<p>This is the third time in three years that a security change has quietly locked third-party clients out of Tapo devices. The switch is what the second time left behind.</p>\n<p><strong>2023: lights and plugs.</strong> That February, three researchers from the University of Catania and Royal Holloway, University of London <a href=\"https://www.dmi.unict.it/giamp/smartbulbscanbehackedtohackintoyourhousehold/\" rel=\"nofollow ugc noopener\">reported four flaws</a> in the way Tapo devices talked to the app, starting from an L530E bulb. Someone within range could take over the victim&#39;s Tapo account and learn their Wi-Fi password. A few months later, firmware updates began swapping the devices&#39; original AES protocol for a new one, KLAP. TP-Link never said the two were connected, and it didn&#39;t announce the change either. &quot;Is this an error or intentional? If intentional, WHY?&quot; asked <a href=\"https://community.tp-link.com/us/smart-home/forum/topic/620314\" rel=\"nofollow ugc noopener\">one forum thread</a>, which was locked without an answer from TP-Link. Every third-party client, this library included, had to learn KLAP.</p>\n<p><strong>2024: cameras.</strong> In November 2023, Juraj Nyíri, who maintains a Home Assistant integration for Tapo cameras, reported a vulnerability to TP-Link. TP-Link fixed it, and by April 2024 cameras on new firmware had stopped accepting the integration&#39;s login. Nyíri built a workaround that went through TP-Link&#39;s cloud and asked for permission to release it. TP-Link reviewed the code and said no. What it shipped instead, in December 2024, eight months after the first reports, was a toggle in the Tapo app that turns the old local login back on: Third-Party Compatibility. The integration&#39;s release notes called it <a href=\"https://github.com/JurajNyiri/HomeAssistant-Tapo-Control/releases/tag/6.0.0\" rel=\"nofollow ugc noopener\">a victory for local control</a>.</p>\n<p><strong>2025: plugs and lights again.</strong> In October 2025, firmware 1.4.0 brought another new protocol, TPAP, and put plugs behind the same switch. Lights followed in the first half of 2026, on firmware 1.4.1 to 1.4.3. This time the way out existed before the door closed: the switch had been in the Tapo app since December 2024. But it was off by default. TP-Link&#39;s <a href=\"https://www.tp-link.com/us/support/faq/4416/\" rel=\"nofollow ugc noopener\">FAQ</a> says the feature &quot;is disabled by default to ensure security&quot;, and that switching it on &quot;may reduce the security of your devices&quot;. When owners of freshly broken plugs asked on the forum, they were told that Home Assistant &quot;<a href=\"https://community.tp-link.com/en/smart-home/forum/topic/852002\" rel=\"nofollow ugc noopener\">is not an officially supported third‑party platform</a> for Tapo products&quot;. TPAP itself was never documented.</p>\n<p>The switch is a security downgrade with a friendly name. Turning it on brings back the old login in place of the new one, and the new one is better. More on that below.</p>\n<h2 id=\"tpap-support\"><a href=\"#tpap-support\">TPAP support</a></h2>\n<p>On recent Tapo firmware, the Third-Party Compatibility switch decides which protocol a device speaks. A light, plug, power strip or H100 hub speaks KLAP when it&#39;s on, and TPAP when it&#39;s off. Cameras never spoke KLAP. Their older protocol is AES SSL, and some of them refuse that one when the switch is off. The library didn&#39;t speak TPAP, so it could only reach a device whose switch was on.</p>\n<p>v0.11.1 adds TPAP. Lights, plugs, power strips, hubs and cameras that require it can now be used with the switch off, both when connecting by IP address and through <code>discover_devices</code>. There is nothing to change in your code: the client works out which protocol the device speaks and logs in over that one.</p>\n<p>Three things are worth knowing:</p>\n<ul><li><strong>A wrong password can lock the device.</strong> After too many failed logins a TPAP device refuses every login for a while. The library reports the wrong password as<code>TPAP_CREDENTIALS</code> and the lockout as<code>TPAP_AUTH_ATTEMPTS_LIMIT</code> . Don&#39;t retry either of them in a loop.</li><li><strong>Camera hubs don&#39;t speak TPAP yet.</strong> An H200 (new in v0.10, see below) on firmware 1.7.5 announces AES SSL whether the switch is on or off, and the library logs in over that.</li><li><strong>Some cameras don&#39;t speak TPAP yet either.</strong> It depends on the model and its firmware. A C220 and a C510W on firmware 1.3.4 speak TPAP, so they work with the switch off. A C210 on firmware 1.5.2 doesn&#39;t, so the library logs in to it over AES SSL instead, and the camera refuses that login while the switch is off. For now it only works with the switch on.</li></ul>\n<p>While one protocol arrived, another left. The original AES protocol, the one KLAP replaced in 2023, was still in the library, which probed every light and plug it connected to by IP address to find out whether it wanted that or KLAP. No firmware has shipped with it for a long time, so v0.11.0 removes it. AES SSL, the one cameras and camera hubs speak, is a close relative: the same kind of encrypted envelope, but over HTTPS and with a different login. That one stays.</p>\n<h3 id=\"why-tpap-is-the-safer-protocol\"><a href=\"#why-tpap-is-the-safer-protocol\">Why TPAP is the safer protocol</a></h3>\n<p>Being able to ignore a switch is nice. The more interesting part is how TPAP logs in.</p>\n<p>KLAP proves that both sides know your credentials by exchanging hashes built from them and from two random values sent in the clear. That keeps the password itself off the wire, but anyone who records a single login on your network can take it home and test password guesses against it, as fast as their hardware allows. The session keys come from the same ingredients, so a correct guess also decrypts everything that followed.</p>\n<p>TPAP logs in with SPAKE2+ (<a href=\"https://www.rfc-editor.org/rfc/rfc9383\" rel=\"nofollow ugc noopener\">RFC 9383</a>), a password-authenticated key exchange. Two things change:</p>\n<ul><li><strong>A recorded login is useless for guessing.</strong> Nothing in the exchange can be checked against a candidate password offline. The only way to test a guess is to try it against the device itself, one attempt at a time, and that&#39;s exactly what the lockout mentioned above puts a stop to.</li><li><strong>Recorded traffic stays private.</strong> Each session&#39;s keys depend on secrets that both sides make up for that login and never send. Someone who learns the password later still can&#39;t decrypt the sessions they captured before.</li></ul>\n<p>While the switch is on, a light or plug still advertises KLAP and that&#39;s what the library logs in over, so these two only hold once it&#39;s off. If nothing else on your network needs Third-Party Compatibility, there is now a good reason to switch it off.</p>\n<h2 id=\"also-new\"><a href=\"#also-new\">Also new</a></h2>\n<p>TPAP is the big change, but it&#39;s not the only one.</p>\n<h3 id=\"camera-hubs-h200-and-h500\"><a href=\"#camera-hubs-h200-and-h500\">Camera hubs: H200 and H500</a></h3>\n<p>Until v0.10, the only hub the library could talk to was the H100. The H200 and H500 are a different kind of beast. They pair with sensors and switches like the H100 does, but they also pair with cameras and store their recordings.</p>\n<p>Both now have a handler, created with <code>h200</code> or <code>h500</code> on the <code>ApiClient</code>. <code>discover_devices</code> finds them too, and returns them ready to use instead of reporting an error.</p>\n<p>The sensors and switches paired to a camera hub work exactly as they do on the H100, through <code>get_child_device_list</code> and the typed child handlers (<code>t100</code>, <code>t31x</code> and the rest). The new part is the recordings: you can list the cameras paired to the hub, find the days that have recordings, list the recordings in a time range, and download one as a playable MPEG-TS clip.</p>\n<pre><code>use tapo::ApiClient;\nlet hub = ApiClient::new(&quot;&lt;tapo-username&gt;&quot;, &quot;&lt;tapo-password&gt;&quot;)\n    .h200(&quot;&lt;hub ip address&gt;&quot;)\n    .await?;\nlet end_time = chrono::Utc::now();\nlet start_time = end_time - chrono::Duration::days(7);\nfor camera in hub.get_general_device_list().await? {\n    if !camera.hub_storage_enabled {\n        continue;\n    }\n    let recordings = hub\n        .get_recordings(camera.device_id.clone(), start_time, end_time)\n        .await?;\n    if let Some(recording) = recordings.first() {\n        let mut media = Vec::new();\n        hub.download_recording(\n            camera.device_id.clone(),\n            recording.start_time,\n            recording.end_time,\n            &amp;mut media,\n        )\n        .await?;\n        std::fs::write(&quot;recording.ts&quot;, &amp;media)?;\n    }\n}</code></pre>\n<p><code>download_recording</code> writes to any <code>AsyncWrite</code>, so the clip can go to a buffer, as above, or straight to a file. In Python it takes a file path instead. All the times are UTC: <code>DateTime&lt;Utc&gt;</code> in Rust and timezone-aware <code>datetime</code>s in Python.</p>\n<p>The full examples are in the repository, for <a href=\"https://github.com/mihai-dinculescu/tapo/blob/main/tapo/examples/tapo_h200.rs\" rel=\"nofollow ugc noopener\">Rust</a> and for <a href=\"https://github.com/mihai-dinculescu/tapo/blob/main/tapo-py/examples/tapo_h200.py\" rel=\"nofollow ugc noopener\">Python</a>.</p>\n<p>I don&#39;t own either hub, so none of this would exist without <a href=\"https://github.com/dominiquefournier\" rel=\"nofollow ugc noopener\">@dominiquefournier</a>, who stuck with it through roughly thirty rounds of testing against their own H200, and <a href=\"https://github.com/supermimai\" rel=\"nofollow ugc noopener\">@supermimai</a>, who tested it against their H500. Thank you both.</p>\n<h3 id=\"plug-schedules-and-timers\"><a href=\"#plug-schedules-and-timers\">Plug schedules and timers</a></h3>\n<p>The Tapo app has had &quot;Schedule&quot; and &quot;Timer&quot; for plugs since forever. As of v0.10 the library has them too, on <code>PlugHandler</code> and <code>PlugEnergyMonitoringHandler</code>. Both were contributed by <a href=\"https://github.com/Hueburtsonly\" rel=\"nofollow ugc noopener\">@Hueburtsonly</a>.</p>\n<p>A schedule rule fires at a time of day, or at an offset from sunrise or sunset, either once or on a set of weekdays. The rules live on the plug and fire on its own clock, so they keep working when your script, your server or your internet connection is down.</p>\n<pre><code>use tapo::ApiClient;\nuse tapo::requests::{DaysOfWeek, ScheduleRule};\nuse tapo::responses::PowerState;\nlet device = ApiClient::new(&quot;&lt;tapo-username&gt;&quot;, &quot;&lt;tapo-password&gt;&quot;)\n    .p110(&quot;&lt;device ip address&gt;&quot;)\n    .await?;\n// Off at 23:30 on Mondays and Wednesdays.\nlet rule =\n    ScheduleRule::clock_weekly(23, 30, DaysOfWeek::MON | DaysOfWeek::WED, PowerState::Off)?;\nlet late_night = device.add_schedule_rule(rule).await?;\n// On every day, an hour after sunset.\nlet rule = ScheduleRule::sunset_weekly(60, DaysOfWeek::EVERY_DAY, PowerState::On)?;\ndevice.add_schedule_rule(rule).await?;\n// Off on weekdays, 30 minutes before sunrise.\nlet rule = ScheduleRule::sunrise_weekly(-30, DaysOfWeek::WEEKDAYS, PowerState::Off)?;\ndevice.add_schedule_rule(rule).await?;\n// Rules read back from the device are read-only. `to_editable` turns one into\n// a rule that can be changed and sent back.\nlet edit = late_night.to_editable()?.with_enabled(false);\ndevice.edit_schedule_rule(edit).await?;</code></pre>\n<p>There are also <code>clock_once</code>, <code>sunrise_once</code> and <code>sunset_once</code> for rules that fire a single time, <code>get_schedule_rules</code> and <code>get_max_schedule_rules</code> to see what&#39;s on the device and how much room is left, and <code>remove_schedule_rule</code> and <code>remove_all_schedule_rules</code> to clean up.</p>\n<p>The timer is the simpler sibling: <code>set_timer</code> arms a single countdown, between one second and 24 hours, after which the plug switches on or off. <code>get_timer</code> reads it back and <code>clear_timer</code> cancels it. A plug holds one armed timer at a time, so <code>set_timer</code> replaces whatever was armed before.</p>\n<h3 id=\"the-mcp-server\"><a href=\"#the-mcp-server\">The MCP server</a></h3>\n<p><a href=\"https://github.com/mihai-dinculescu/tapo/tree/main/tapo-mcp\" rel=\"nofollow ugc noopener\">tapo-mcp</a>, the MCP server that exposes Tapo devices to AI agents, picked up two of these changes. As of v0.5.3 it lists H200 and H500 camera hubs and the sensors paired to them, and it works with devices that have Third-Party Compatibility switched off. Plug schedules, timers and recording downloads are library-only for now.</p>\n<h2 id=\"before-you-upgrade\"><a href=\"#before-you-upgrade\">Before you upgrade</a></h2>\n<p>Coming from v0.9, expect a few breaking changes. The legacy AES protocol is gone, trigger logs and temperature records have new field names, and Python enum values no longer compare equal to integers. The <a href=\"https://github.com/mihai-dinculescu/tapo/blob/main/CHANGELOG.md\" rel=\"nofollow ugc noopener\">changelog</a> lists every one.</p>\n<h2 id=\"what-s-next\"><a href=\"#what-s-next\">What&#39;s next</a></h2>\n<p>A few things are on the list:</p>\n<ul><li><strong>A lot more in the MCP server.</strong> Energy usage and caching of discovery results come first, and recording downloads could follow if there&#39;s interest.</li><li><strong>The H110 hub.</strong><a href=\"https://github.com/skoky\" rel=\"nofollow ugc noopener\">@skoky</a> has a<a href=\"https://github.com/mihai-dinculescu/tapo/pull/607\" rel=\"nofollow ugc noopener\">pull request</a> in progress that adds the H110, the hub that doubles as an infrared remote control.</li><li><strong>More cameras.</strong> Today the library only has a handler for cameras that pan and tilt. The plan is to add one for fixed cameras, starting with the ubiquitous C120.</li><li><strong>A device simulator.</strong> I really want to get better at testing. Today most of the library can only be checked against real hardware, and some of that hardware, like the camera hubs, I don&#39;t own. A simulator that answers the way the devices do would catch a broken login or a misread response before a tester has to.</li><li><strong>A Node.js wrapper, perhaps.</strong> This one is long term and not a promise. The same approach that produced the Python package could bring the library to Node.js.</li></ul>\n<p>In the meantime, if this library was your only reason for keeping Third-Party Compatibility on, upgrade and switch it off. If a device then refuses to log in, <a href=\"https://github.com/mihai-dinculescu/tapo/issues\" rel=\"nofollow ugc noopener\">open an issue</a> with its model and firmware. Reports like that are how the C210 got its caveat, and how the H200 got supported at all.</p>\n<h2 id=\"ai-usage-disclaimer\"><a href=\"#ai-usage-disclaimer\">AI usage disclaimer</a></h2>\n<p>English is not my first language, and I&#39;m not a talented writer in any language, so I use AI to polish my writing. The ideas, the setup, the mistakes and the opinions are all mine. They reach the AI as thorough notes in half-broken English, and it fixes the English. It doesn&#39;t supply the thinking, though it is a trusty research assistant.</p>","headings":[{"level":2,"text":"How we got here","id":"how-we-got-here"},{"level":2,"text":"TPAP support","id":"tpap-support"},{"level":3,"text":"Why TPAP is the safer protocol","id":"why-tpap-is-the-safer-protocol"},{"level":2,"text":"Also new","id":"also-new"},{"level":3,"text":"Camera hubs: H200 and H500","id":"camera-hubs-h200-and-h500"},{"level":3,"text":"Plug schedules and timers","id":"plug-schedules-and-timers"},{"level":3,"text":"The MCP server","id":"the-mcp-server"},{"level":2,"text":"Before you upgrade","id":"before-you-upgrade"},{"level":2,"text":"What's next","id":"what-s-next"},{"level":2,"text":"AI usage disclaimer","id":"ai-usage-disclaimer"}]}}