如何通过Go-CoAP获取飞利浦AC3033净化器状态无需物理操作?
飞利浦AC3033空气净化器COAP协议获取状态问题
我希望通过COAP协议连接飞利浦AC3033空气净化器,获取其当前统计数据/状态。
成功获取设备信息的Go代码
package main import ( "context" "log" "time" "github.com/plgd-dev/go-coap/v3/udp" ) func main() { co, err := udp.Dial("192.168.0.12:5683") if err != nil { log.Fatalf("Error dialing: %v", err) } ctx, cancel := context.WithTimeout(context.Background(), 2 * time.Second) defer cancel() resp, err := co.Get(ctx, "/sys/dev/info") if err != nil { log.Fatalf("Error sending request: %v", err) } log.Printf("Response payload: %+v", resp) }
返回的设备信息:
{ "product_id": "<id>", "device_id": "<id>", "name": "Living Room", "type": "AC3033", "modelid":"AC3033/14", "swversion":"Ms3411", "option":"1" }
获取状态失败的尝试
尝试调用/sys/dev/status接口获取统计数据,代码如下:
package main import ( "context" "log" "time" "github.com/plgd-dev/go-coap/v3/udp" ) func main() { co, err := udp.Dial("192.168.0.12:5683") if err != nil { log.Fatalf("Error dialing: %v", err) } ctx, cancel := context.WithTimeout(context.Background(), 2 * time.Second) defer cancel() resp, err := co.Get(ctx, "/sys/dev/status") if err != nil { log.Fatalf("Error sending request: %v", err) } log.Printf("Response payload: %+v", resp) }
执行后挂起2秒报错:
2024/12/07 22:20:36 Error sending request: cannot write request: cannot write request: context deadline exceeded
使用aioairctrl工具同样出现问题:
pip install aioairctrl aioairctrl --host 192.168.0.12 status --json
命令会挂起,直到按下设备上的按钮才会输出当前统计数据/状态。
问题
是否有无需物理操作设备即可获取其当前统计数据的方法?
解决方案
飞利浦这类设备的COAP接口默认需要设备处于本地控制授权状态,物理按键操作实际是触发了临时授权窗口。要绕过物理操作,有以下几种可行方式:
开启永久本地控制权限
部分飞利浦空气净化器支持在设备WiFi设置菜单中开启"允许本地API访问"类选项,开启后无需每次按键授权,可直接通过COAP接口获取状态。携带配对授权密钥请求
设备首次配对WiFi时会生成本地访问授权密钥,可通过飞利浦官方APP调试模式或设备WiFi配置信息导出。在COAP请求中携带该密钥作为认证参数即可:package main import ( "context" "io" "log" "time" "github.com/plgd-dev/go-coap/v3/message" "github.com/plgd-dev/go-coap/v3/udp" ) func main() { co, err := udp.Dial("192.168.0.12:5683") if err != nil { log.Fatalf("Error dialing: %v", err) } defer co.Close() ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() // 创建请求并添加授权参数 req, err := co.NewGetRequest(ctx, "/sys/dev/status") if err != nil { log.Fatalf("Failed to create request: %v", err) } // 替换<你的授权密钥>为实际密钥 req.AddOption(message.URIQuery, "auth=<你的授权密钥>") req.SetOption(message.ContentFormat, message.JSON) resp, err := co.Do(req) if err != nil { log.Fatalf("Error sending request: %v", err) } defer resp.Body.Close() data, err := io.ReadAll(resp.Body) if err != nil { log.Fatalf("Error reading response: %v", err) } log.Printf("Response payload: %s", string(data)) }订阅设备状态推送
飞利浦设备支持COAP的Observe机制,发起观察请求订阅/sys/dev/status后,设备会主动推送状态更新,无需主动查询(部分设备仅首次订阅时需一次授权):package main import ( "context" "io" "log" "github.com/plgd-dev/go-coap/v3/udp" ) func main() { co, err := udp.Dial("192.168.0.12:5683") if err != nil { log.Fatalf("Error dialing: %v", err) } defer co.Close() ctx, cancel := context.WithCancel(context.Background()) defer cancel() // 发起观察请求,接收状态推送 err = co.Observe(ctx, "/sys/dev/status", func(req *udp.Request) { if req.Body != nil { data, _ := io.ReadAll(req.Body) log.Printf("Received status update: %s", string(data)) } }) if err != nil { log.Fatalf("Error observing: %v", err) } // 保持程序运行以持续接收推送 select {} }模拟授权广播包(进阶)
抓包分析物理按键按下时设备发送的本地广播信号,模拟该广播包触发临时授权窗口,之后再发起COAP请求。此方式需抓包分析设备协议,实现难度较高。
内容的提问来源于stack exchange,提问作者yellowhat
相关产品推荐
相关产品推荐

