xiaomi: `midr.cateye.ph300` needs a MIoT wake action before `miss_get_vendor` returns keys
Camera
| name | Xiaomi Smart Cat Eye 2 (MJMY01BY) |
| model | midr.cateye.ph300 |
| fw_ver | 5.3.1_0347 |
| hw_ver | BL616_ECO |
| miio_ver | 0.0.9 |
| region | CN |
| go2rtc | v1.9.14 and master (c245815) |
| Not in #1982. Filing per the "create a new issue if the camera doesn't work" note. |
Summary
Three separate problems, in the order they are hit. The first two have fixes and are verified on this device; the third is where it still fails.
1. wakeup times out, wakeup_host works
Same as #2373. Back to back against home/rpc/{did} on this model:
| method | params | cloud response |
|---|---|---|
wakeup |
{"video":"1"} |
timeout |
wakeup_host |
{} |
ok (empty result) |
#2373 fixes this for midr.* and the fix is correct for ph300 too. |
2. miss_get_vendor needs a MIoT action first (not covered by any PR)
With only the RPC wake — including the fixed wakeup_host — the cloud refuses
to hand out P2P credentials:
miss_get_vendor ERR xiaomi: rpc response code errorThis is stable: it fails identically for support_vendors =
TUTK_CS2_MTP, TUTK, CS2, MTP, AGORA, TUTK_CS2, CS2_MTP, and at
t+0/3/5/8/12/20s after the wake (each attempt taking ~10s, i.e. a device-side
RPC timeout). /device/devicepass is no help either — it returns only
{"region":"CN"}, no p2p_id and no password, so the legacy fallback is dead
as well.
miIO.info succeeds throughout, so the device is awake and reachable the whole
time.
The missing step is a MIoT action. The spec for this model
(urn:miot-spec-v2:device:video-doorbell:0000A03A:midr-ph300:2) has:
siid=2 Video Doorbell aiid=3 Wake Up
siid=12 PP Stream aiid=1 Start P2P StreamCalling /miotspec/action with siid=2, aiid=3 returns code: 0 and
immediately unblocks the vendor negotiation:
action 2/3 OK {"code":0,...}
miss_get_vendor OK {"vendor":{"vendor":4,"vendor_params":{"p2p_id":"...",
"license":"...","crc_key":"...","init_string":"..."}},
"public_key":"...","sign":"...","region":"CN",
"miss_version":"3.2.12.1"}siid=12, aiid=1 ("Start P2P Stream") returns an error code
(-704002000 / -706012015, varies) and does not appear to be required —
2/3 alone is what flips it.
Note this differs from #2434, which uses service 7 action 1 for
loock.cateye.v06; the service ids are per-model, so this probably wants a
small per-model table rather than a constant.
Patch I used locally on top of #2373:
// miotWakeActions returns the MIoT (siid, aiid) actions that must be called to
// bring a model into a streamable state. Empty for models that don't need it.
func miotWakeActions(model string) [][2]int {
switch {
case strings.HasPrefix(model, "midr.") && strings.Contains(model, ".cateye."):
return [][2]int{{2, 3}, {12, 1}}
}
return nil
}
func miotAction(url *url.URL, siid, aiid int) error {
did := url.Query().Get("did")
params := fmt.Sprintf(`{"params":{"did":"%s","siid":%d,"aiid":%d,"in":[]}}`, did, siid, aiid)
_, err := cloudUserRequest(url.User, "/miotspec/action", params)
return err
}called from getCameraURL right after wakeUpCamera.
3. Still blocked: no LAN CS2 port, and the cloud relay times out
With keys in hand, go2rtc dials cs2 and fails:
xiaomi: dial xiaomi://***&model=midr.cateye.ph300&vendor=cs2
[rtsp] error="streams: read udp [::]:NNNNN: i/o timeout"The doorbell never answers the CS2 LAN search. Sending F1 30 00 00 to UDP
32108 from the same subnet gets no reply on any of:
- unicast to the device IP
- subnet-directed broadcast
- limited broadcast
255.255.255.255So this is the "no LAN port" class of device described in #2264. Applying #2264 on top does switch it to the relay path, but:
xiaomi: cs2 cloud-relay mode, redirecting to 47.236.156.107
[rtsp] error="streams: cs2 cloud: timeout waiting for 0x40 NAT-punch info"vendor_params here carries license, crc_key and init_string alongside
p2p_id. The init_string is 84 chars over the alphabet A–P, i.e. 42
nibble-encoded bytes, and the decoded bytes are not plaintext — no readable
addresses. Since #2264 uses a hardcoded relay pool, my guess is the correct
relay endpoints for this device come from init_string instead, but I have not
decoded it.
Happy to run further probes against this device if that helps — I have a small
Go harness on top of pkg/xiaomi that can call arbitrary cloud endpoints with
the account's credentials.
Extra data on the relay path (may help #2264)
Instrumenting the handshake shows the relay is reachable and does answer — it
just never sends 0x40:
[cs2dbg] handshake start relay=47.236.156.107 p2p_id=XM****-******-*****
[cs2dbg] relay=47.236.156.107 rx 8 bytes: f1210004fd000000
[cs2dbg] relay=47.236.156.107 rx 8 bytes: f1210004fd000000 (repeats, then timeout)So 0x21 here carries a 4-byte status fd 00 00 00 rather than acting as a
plain ACK. Replaying the captured 0x20/0xF9 pair against other hosts:
| host | reply |
|---|---|
| 47.236.156.107 | f1210004fd000000 |
54.84.37.235 (www.cs2-network.com) |
f1210004fd000000 |
| 47.88.30.183, 47.88.30.225 | silent |
| m1/m2/m3.iotcplatform.com | silent |
| Identical status from two unrelated CS2 master servers suggests the UID simply | |
| isn't registered on the ones being tried, i.e. the relay pool is wrong for this | |
device rather than the handshake being malformed. This device's p2p_id prefix |
|
is XMCN.., and its region is CN, while the hardcoded pool looks |
|
| international. | |
| I could not find a cloud endpoint that returns a server list — these all 404 on | |
api.io.mi.com: miss_get_server, get_p2p_server, miss_get_relay, |
|
range_get_vendor, p2p_info, get_relay_server, miss_server, |
|
miss_get_vendor_v2. That leaves init_string as the likely carrier of the |
|
| master-server list, matching how the CS2/TUTK SDK is initialised. | |
On init_string: 84 chars over A–P decode to 42 bytes either way round. |
|
42 = 7 × 6 exactly, which would fit seven IPv4:port entries, but no decoding I |
|
| tried yields plausible addresses or port 32100/32108, so it is presumably | |
obfuscated. The P2P_Proprietary_Encrypt cipher from #2264 (key |
|
SSD@cs2-network., both feedback directions, also keyed with the accompanying |
|
license and crc_key) does not produce structure either. |
Source: AlexxIT/go2rtc