From 9f1bb489c29fa591275b9daaad2f027f54f5a0b3 Mon Sep 17 00:00:00 2001 From: Nick Kugaevsky Date: Tue, 21 Jul 2026 09:10:09 +0500 Subject: [PATCH] xiaomi: fix wake method for midr doorbells MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The generic "wakeup" RPC times out on midr.cateye.sd400 (Xiaomi Smart Doorbell 4 Pro) — the device never acks it. These models wake with the "wakeup_host" method and empty params instead. Other cateye doorbells keep the previous "wakeup" call to avoid regressions. --- internal/xiaomi/xiaomi.go | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/internal/xiaomi/xiaomi.go b/internal/xiaomi/xiaomi.go index 1e578420c..4b4474081 100644 --- a/internal/xiaomi/xiaomi.go +++ b/internal/xiaomi/xiaomi.go @@ -93,10 +93,10 @@ func cloudUserRequest(user *url.Userinfo, apiURL, params string) ([]byte, error) func getCameraURL(url *url.URL) (string, error) { model := url.Query().Get("model") - // It is not known which models need to be awakened. - // Probably all the doorbells and all the battery cameras. - if strings.Contains(model, ".cateye.") { - _ = wakeUpCamera(url) + // Battery-powered models sleep and must be woken via a cloud RPC before + // the P2P connection can be established. + if method, params, ok := wakeParams(model); ok { + _ = wakeUpCamera(url, method, params) } // The getMissURL request has a fallback to getP2PURL. @@ -211,10 +211,27 @@ func getVendorName(i byte) string { return fmt.Sprintf("%d", i) } -func wakeUpCamera(url *url.URL) error { - const params = `{"id":1,"method":"wakeup","params":{"video":"1"}}` +// wakeParams returns the miIO cloud RPC method and params used to wake a +// sleeping battery-powered model, and ok=false for always-on models that don't +// need it. Firmware families use different method names and payloads. +// +// Verified on midr.cateye.sd400 (Xiaomi Smart Doorbell 4 Pro), which does not +// respond to the generic "wakeup" call. Other doorbells keep the previous +// best-effort "wakeup" to avoid regressions. +func wakeParams(model string) (method, params string, ok bool) { + switch { + case strings.HasPrefix(model, "midr."): + return "wakeup_host", "{}", true + case strings.Contains(model, ".cateye."): + return "wakeup", `{"video":"1"}`, true + } + return "", "", false +} + +func wakeUpCamera(url *url.URL, method, params string) error { did := url.Query().Get("did") - _, err := cloudUserRequest(url.User, "/home/rpc/"+did, params) + body := fmt.Sprintf(`{"id":1,"method":%q,"params":%s}`, method, params) + _, err := cloudUserRequest(url.User, "/home/rpc/"+did, body) return err }