feat: 初始化小米 IoT (MIoT) 智能家居 Go 库
- 实现 MIoT 客户端核心功能(MQTT 连接、设备管理、属性读写) - 支持云端 API 调用与局域网设备发现(mDNS) - 集成国际化(i18n)多语言支持 - 添加 MIoT 设备规约解析器(spec_parser) - 包含单元测试与使用示例
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
// E1 — OAuth authorization flow example.
|
||||
// Demonstrates: generate auth URL → get code → exchange for token → refresh token.
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"miot"
|
||||
"miot/examples/config"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 1. Load config (or use defaults)
|
||||
cfg, err := config.LoadConfig(config.DefaultPath())
|
||||
if err != nil {
|
||||
fmt.Printf("Load config error: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Ensure UUID is set (may be empty from old-format config)
|
||||
if cfg.UUID == "" {
|
||||
cfg.UUID = config.NewUUID()
|
||||
}
|
||||
|
||||
// 2. Create OAuth client (Go constructor appends "ha." internally)
|
||||
oauth := miot.NewMIoTOauthClient(
|
||||
miot.OAUTH2_CLIENT_ID,
|
||||
cfg.RedirectURL,
|
||||
cfg.CloudServer,
|
||||
cfg.UUID,
|
||||
)
|
||||
|
||||
// 3. Generate authorization URL
|
||||
authURL := oauth.GenAuthURL("", "", nil, false)
|
||||
fmt.Println("=== OAuth 授权 ===")
|
||||
fmt.Println("请打开以下URL完成授权:")
|
||||
fmt.Println(authURL)
|
||||
|
||||
// 4. Read authorization code from stdin
|
||||
fmt.Print("\n输入回调URL中的 code: ")
|
||||
var code string
|
||||
fmt.Scanln(&code)
|
||||
if code == "" {
|
||||
fmt.Println("未输入 code,退出")
|
||||
return
|
||||
}
|
||||
|
||||
// 5. Exchange code for access token
|
||||
result, err := oauth.GetAccessToken(code)
|
||||
if err != nil {
|
||||
fmt.Printf("获取 token 失败: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 6. Save tokens to config (aligns with Python auth_info structure)
|
||||
cfg.AuthInfo = config.AuthInfo{
|
||||
AccessToken: safeStr(result, "access_token"),
|
||||
RefreshToken: safeStr(result, "refresh_token"),
|
||||
MacKey: safeStr(result, "mac_key"),
|
||||
ExpiresIn: safeInt(result, "expires_in"),
|
||||
ExpiresTS: config.CalcExpiresTS(safeInt(result, "expires_in")),
|
||||
}
|
||||
|
||||
// 7. Get UID from home list (uid is in GetHomeInfos response, not token response)
|
||||
http, err := miot.NewMIoTHttpClient(cfg.CloudServer, miot.OAUTH2_CLIENT_ID, cfg.AuthInfo.AccessToken)
|
||||
if err == nil {
|
||||
defer http.Close()
|
||||
if homes, e := http.GetHomeInfos(); e == nil {
|
||||
cfg.UID = safeStr(homes, "uid")
|
||||
fmt.Printf("UID: %s\n", cfg.UID)
|
||||
}
|
||||
}
|
||||
|
||||
if err := config.SaveConfig(config.DefaultPath(), cfg); err != nil {
|
||||
fmt.Printf("保存 config 失败: %v\n", err)
|
||||
} else {
|
||||
fmt.Println("\n=== Token 已保存 ===")
|
||||
t := cfg.AuthInfo.AccessToken
|
||||
fmt.Printf("access_token: %s...\n", t[:min(20, len(t))])
|
||||
t = cfg.AuthInfo.RefreshToken
|
||||
fmt.Printf("refresh_token: %s...\n", t[:min(20, len(t))])
|
||||
fmt.Printf("uid: %s\n", cfg.UID)
|
||||
fmt.Printf("uuid: %s\n", cfg.UUID)
|
||||
}
|
||||
|
||||
// 8. Demonstrate token refresh
|
||||
fmt.Println("\n=== 刷新 Token ===")
|
||||
newResult, err := oauth.RefreshAccessToken(cfg.AuthInfo.RefreshToken)
|
||||
if err != nil {
|
||||
fmt.Printf("刷新 token 失败: %v\n", err)
|
||||
return
|
||||
}
|
||||
cfg.AuthInfo.AccessToken = safeStr(newResult, "access_token")
|
||||
cfg.AuthInfo.RefreshToken = safeStr(newResult, "refresh_token")
|
||||
cfg.AuthInfo.ExpiresTS = config.CalcExpiresTS(safeInt(newResult, "expires_in"))
|
||||
config.SaveConfig(config.DefaultPath(), cfg)
|
||||
fmt.Println("Token 已刷新并保存")
|
||||
}
|
||||
|
||||
func safeStr(m map[string]interface{}, key string) string {
|
||||
if v, ok := m[key]; ok {
|
||||
if s, ok := v.(string); ok {
|
||||
return s
|
||||
}
|
||||
switch n := v.(type) {
|
||||
case float64:
|
||||
return fmt.Sprintf("%.0f", n)
|
||||
case json.Number:
|
||||
return n.String()
|
||||
default:
|
||||
return fmt.Sprintf("%v", v)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func safeInt(m map[string]interface{}, key string) int {
|
||||
if v, ok := m[key]; ok {
|
||||
switch n := v.(type) {
|
||||
case float64:
|
||||
return int(n)
|
||||
case int:
|
||||
return n
|
||||
case json.Number:
|
||||
i, _ := n.Int64()
|
||||
return int(i)
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
Reference in New Issue
Block a user