Files
xiaomihome/miot/examples/cloud_oauth/main.go
T
4566704 3a44cb9e6a feat: 初始化小米 IoT (MIoT) 智能家居 Go 库
- 实现 MIoT 客户端核心功能(MQTT 连接、设备管理、属性读写)
- 支持云端 API 调用与局域网设备发现(mDNS)
- 集成国际化(i18n)多语言支持
- 添加 MIoT 设备规约解析器(spec_parser)
- 包含单元测试与使用示例
2026-06-28 22:05:49 +08:00

132 lines
3.4 KiB
Go

// 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
}