Tapo (Rust/Python library) 現已支援 TP-Link TPAP 協定
Tapo (Rust/Python library) now speaks TP-Link's TPAP protocol
Tapo library 現已支援 TP-Link TPAP 協定,解除第三方相容開關限制。
- 版本:v0.11.1,新增 TPAP 支援、H200/H500 影像錄影下載、計時與排程功能。
本次更新讓開源 Tapo library 支援 TPAP 協定,解除第三方相容開關限制,並加入多機種與錄影下載功能,對 IoT 開發者提升操作便利性。
你的指令碼已經在幾個月內不斷開關 Tapo 插座。然後插座悄悄更新韌體,下一個請求回傳 403 Forbidden。你的程式碼沒有任何變動。
原因是一個名為「Third-Party Compatibility」的開關,隱藏在 Tapo 應用程式的 Me > Third-Party Services。自從韌體 1.4.0 之後,插座只有在此開關開啟時,才會以舊方式與第三方客戶端通訊。它一次次讓人頭疼。至少有九個問題敘述相同的情況,其中包括 #441、#449 和 #473。
截至 tapo v0.11.1,我的非官方 Rust 與 Python 客戶端支援 TP-Link Tapo 裝置,該開關可以保持關閉,僅有少數例外在下文說明。
這是近四個月工作成果的重點,三個版本在一週內相繼發布:v0.10.0 於 9 月 28 日、v0.11.0 於 10 月 2 日以及 v0.11.1 於 10 月 4 日。途中,庫還新增了一個裝置族群以及兩項 Tapo 應用程式的功能,這些功能是大家一直在請求的。changelog 裡列出所有細節。本篇文章將重點說明,並附上範例。
這裡的內容同時適用於 Rust 與 Python 版本。Python 套件是對 Rust crate 的輕量封裝,因此所有變更一次性同步於兩者,且使用相同版本號。以下範例以 Rust 為主,Python 呼叫則對應相同功能。
我們是如何走到這裡的
這是三年內第三次,安全性變更悄悄將第三方客戶端鎖在 Tapo 裝置之外。此開關正是第二次事件遺留下來的。
2023:燈泡與插座。 今年二月,來自卡塔尼亞大學與倫敦皇家霍洛威大學的三名研究人員 報告了四個缺陷,這些缺陷涉及 Tapo 裝置與應用程式的通訊方式,起始於 L530E 燈泡。距離內的人可接管受害者的 Tapo 帳號並取得其 Wi‑Fi 密碼。幾個月後,韌體更新開始將裝置原本的 AES 協議換成新協議 KLAP。TP‑Link 從未說明兩者有關聯,也未公告此變更。"這是錯誤還是故意?若是故意,為什麼?" 一個論壇討論串 提問,並被鎖定,TP‑Link 未給予回覆。每個第三方客戶端,包括此庫,都必須學習 KLAP。
2024:相機。 2023 年 11 月,負責 Tapo 相機 Home Assistant 整合的 Juraj Nyíri 向 TP‑Link 報告了一項漏洞。TP‑Link 已修復此漏洞,並於 2024 年 4 月,新韌體的相機停止接受整合程式的登入。Nyíri 建立了一個迴避方案,經由 TP‑Link 的雲端並請求發布許可。TP‑Link 審核程式碼並拒絕。其後在 2024 年 12 月,八個月後才發布一個切換,位於 Tapo 應用程式中,可將舊的本地登入重新啟用:Third‑Party Compatibility。整合程式的釋出說明稱其為 為本地控制的勝利
2025:插頭與燈光再次出現。 2025年10月,韌體1.4.0推出另一個新協議TPAP,並將插頭放在同一個開關後面。燈光則於2026年前半年(韌體1.4.1至1.4.3)跟進。這一次,出口在門關閉之前就已存在:該開關自2024年12月起已在Tapo應用程式中。只是不啟用。TP-Link的FAQ說此功能「預設已停用以確保安全」,並指出開啟它「可能降低您的裝置安全性」。當剛剛損壞的插頭擁有者在論壇上詢問時,他們被告知Home Assistant「不是Tapo產品的官方支援第三方平臺」。TPAP本身從未被檔案化。
該開關是一個帶友善名稱的安全降級。 開啟它會把舊的登入方式恢復取代新登入方式,而新登入方式更好。以下進一步說明。
TPAP支援
在最近的 Tapo 韌體中,第三方相容性開關決定裝置使用哪個協議。燈、插頭、延長線或 H100 hub 在開啟時使用 KLAP,關閉時使用 TPAP。相機從未使用 KLAP。它們的舊協議是 AES SSL,且其中一些在開關關閉時會拒絕此協議。此函式庫不支援 TPAP,因此只能連線到開關為開啟狀態的裝置。
v0.11.1 新增 TPAP。需要 TPAP 的燈、插頭、延長線、hub 以及相機現在可以在開關關閉時使用,無論是透過 IP 位址連線還是透過 discover_devices。您的程式碼不需要更改:客戶端會自動判斷裝置使用的協議並以該協議登入。
值得了解的三件事:
- 錯誤密碼會鎖定裝置。 若多次登入失敗,TPAP 裝置會暫時拒絕所有登入。函式庫會將錯誤密碼報告為
TPAP_CREDENTIALS,將鎖定報告為TPAP_AUTH_ATTEMPTS_LIMIT。不要在迴圈中重試任何一個。 - 相機集線器尚未支援 TPAP。 H200(在 v0.10 新增,見下文)在韌體 1.7.5 上無論開關開啟與否都會宣告 AES SSL,並且函式庫會以此進行登入。
- 部分相機也尚未支援 TPAP。 這取決於型號與韌體。C220 與 C510W 在韌體 1.3.4 時支援 TPAP,因此在關閉開關時仍能運作。C210 在韌體 1.5.2 時不支援,故函式庫改以 AES SSL 登入,且相機在開關關閉時拒絕此登入。暫時僅在開關開啟時能運作。
一個協定到來,另一個離開。原始的 AES 協定,即 KLAP 在 2023 年取代的那個,仍保留在函式庫中,該函式庫會透過 IP 位址探測每一盞燈與插頭,以判斷它們需要的是那個或是 KLAP。已久未有韌體搭載此協定,於是 v0.11.0 移除它。AES SSL,即相機與相機集線器使用的協定,則相當相似:同樣的加密封包,但使用 HTTPS 且登入方式不同。此協定仍保留。
TPAP 為何更安全的協定
能忽略開關是一件好事。更有趣的是 TPAP 的登入方式。
KLAP 透過交換由憑證及兩個以明文傳送的隨機值組成的雜湊值,證明雙方都知道你的認證資訊。這樣可將密碼本身留在網路之外,但任何在網路上擷取單一次登入的使用者,都可將其帶回並以其硬體能力快速測試密碼猜測。會話金鑰同樣來自相同的元件,因此一次正確猜測亦能解密之後所有傳輸內容。
TPAP 以 SPAKE2+ (RFC 9383) 進行登入,這是一種密碼驗證金鑰交換。兩件事改變:
- 記錄的登入對猜測毫無用處。 在交換中沒有任何東西可以離線比對候選密碼。測試猜測的唯一方式是一次一個地嘗試對裝置本身,正如上面提到的鎖定功能所阻止的。
- 已記錄的流量保持私密。 每個會話的金鑰依賴於雙方在該登入時自行產生的機密,且永不傳送。稍後學到密碼的人仍無法解密他們先前捕獲的會話。
當開關開啟時,燈或插座仍會廣播 KLAP,並且庫會透過它進行登入,因此這兩者僅在關閉時才有效。若網路上沒有其他裝置需要第三方相容性,現在就有充分理由將其關閉。
同時新增
TPAP 是最大的變更,但並非唯一。
Camera hubs: H200 和 H500
直到 v0.10,庫只能與 H100 互動。H200 和 H500 是另一種型態。它們像 H100 那樣與感測器和開關配對,但也能與相機配對並儲存錄影。
現在兩者皆有一個處理器,可用 h200 或 h500 在 ApiClient 上建立。discover_devices 也能找到它們,並返回可直接使用的結果,而非報錯。
與相機集線器配對的感測器和開關運作方式與在 H100 上完全相同,透過 get_child_device_list 以及型別化的子處理器(t100、t31x 等)。新功能是錄影:您可以列出與集線器配對的相機、查詢有錄影的日期、在時間範圍內列出錄影,並下載其中一個作為可播放的 MPEG-TS 片段。
use tapo::ApiClient;
let hub = ApiClient::new("<tapo-username>", "<tapo-password>")
.h200("<hub ip address>")
.await?;
let end_time = chrono::Utc::now();
let start_time = end_time - chrono::Duration::days(7);
for camera in hub.get_general_device_list().await? {
if !camera.hub_storage_enabled {
continue;
}
let recordings = hub
.get_recordings(camera.device_id.clone(), start_time, end_time)
.await?;
if let Some(recording) = recordings.first() {
let mut media = Vec::new();
hub.download_recording(
camera.device_id.clone(),
recording.start_time,
recording.end_time,
&mut media,
)
.await?;
std::fs::write("recording.ts", &media)?;
}
}download_recording 可寫入任何 AsyncWrite,因此片段可以像上面那樣寫入緩衝區,或直接寫入檔案。在 Python 中則接受檔案路徑。所有時間均為 UTC:Rust 中的 DateTime<Utc> 以及 Python 中的時區感知 datetime。
完整範例可在倉庫中找到,分別為 Rust 與 Python。
我並未擁有任何一個集線器,因此若沒有 @dominiquefournier(在自己的 H200 上進行大約三十輪測試)以及 @supermimai(在 H500 上測試)這些人,這些功能都不會存在。謝謝你們兩位。
插座排程與計時器
Tapo 應用自始至終為插座提供「Schedule」與「Timer」。自 v0.10 起,庫也在 PlugHandler 與 PlugEnergyMonitoringHandler 上提供,兩者皆由 @Hueburtsonly 貢獻。
排程規則可在一天中的某個時間、或相對於日出或日落的偏移量觸發,單次或在指定工作日執行。規則存在於插座內部,並以其自身時鐘觸發,因而即使您的指令碼、伺服器或網路連線中斷,它們仍會繼續運作。
use tapo::ApiClient;
use tapo::requests::{DaysOfWeek, ScheduleRule};
use tapo::responses::PowerState;
let device = ApiClient::new("<tapo-username>", "<tapo-password>")
.p110("<device ip address>")
.await?;
// Off at 23:30 on Mondays and Wednesdays.
let rule =
ScheduleRule::clock_weekly(23, 30, DaysOfWeek::MON | DaysOfWeek::WED, PowerState::Off)?;
let late_night = device.add_schedule_rule(rule).await?;
// On every day, an hour after sunset.
let rule = ScheduleRule::sunset_weekly(60, DaysOfWeek::EVERY_DAY, PowerState::On)?;
device.add_schedule_rule(rule).await?;
// Off on weekdays, 30 minutes before sunrise.
let rule = ScheduleRule::sunrise_weekly(-30, DaysOfWeek::WEEKDAYS, PowerState::Off)?;
device.add_schedule_rule(rule).await?;
// Rules read back from the device are read-only. `to_editable` turns one into
// a rule that can be changed and sent back.
let edit = late_night.to_editable()?.with_enabled(false);
device.edit_schedule_rule(edit).await?;還有 clock_once、sunrise_once 與 sunset_once 用於一次性觸發的規則,get_schedule_rules 與 get_max_schedule_rules 以檢視裝置上內容及剩餘空間,remove_schedule_rule 與 remove_all_schedule_rules 用於清理。
計時器是較簡單的同類:set_timer 觸發一個從一秒到 24 小時的倒數計時,倒數結束後插座開關切換。get_timer 讀取其狀態,clear_timer 取消。插座一次只能持有一個已啟用的計時器,因此 set_timer 會取代先前已啟用的。
MCP 伺服器
tapo-mcp,將 Tapo 裝置暴露給 AI 代理的 MCP 伺服器,已採納兩項更改。自 v0.5.3 起,它列出 H200 與 H500 相機集線器及其配對的感測器,並且能與已關閉 Third-Party Compatibility 的裝置一起使用。目前 Plug 的排程、計時器與錄影下載僅限於庫內使用。
升級前
由 v0.9 開始,請預期會有幾項重大變更。遺留的 AES 協議已移除,觸發日誌與溫度紀錄的欄位名稱已更新,Python 列舉值不再與整數相等。changelog 列出了所有變更。
接下來是什麼
以下是幾項待辦事項:
- MCP 伺服器將有更多功能。 首先是能源使用量與發現結果快取,若有需求,錄影下載也可能跟進。
- H110 集線器。 @skoky 正在進行一個 pull request,將 H110 加入,該集線器同時擔任紅外遙控器。
- 更多相機。 目前庫中僅有支援可平移與傾斜相機的處理程式。計畫新增對固定相機的支援,先從普及的 C120 開始。
- 裝置模擬器。 我真的想提升測試品質。今天大部分庫只能對實體硬體進行驗證,而我並不擁有其中一些硬體,例如相機集線器。模擬器若能模擬裝置的回應,將能在測試者操作前捕捉到登入失敗或回應錯誤。
- 或許還會有 Node.js 包裝層。 這是長期計畫,並非承諾。使用與產生 Python 套件相同的方法,或能將庫移植至 Node.js。
在此期間,如果此庫是你保持 Third-Party Compatibility 開啟的唯一原因,請升級並關閉它。若裝置仍拒絕登入,開啟問題,並提供其型號與韌體。類似的報告才是 C210 取得警告的原因,也是 H200 得到支援的途徑。
AI 使用免責宣告
英語不是我的第一語言,我在任何語言上都不是優秀的作家,所以我使用 AI 來潤飾我的寫作。想法、設定、錯誤與觀點皆屬於我。它以半破碎英語的詳細筆記傳達給 AI,AI 只修正語言。它不提供思考,雖然是可靠的研究助手。
來源:hackernews100 · mihai.dinculescu.dev