// E1 — OAuth authorization flow example. // Demonstrates: generate auth URL → get code → exchange for token → refresh token. package main import ( "encoding/json" "fmt" "git.zeroonesoft.cn/golib/xiaomihome/miot" "git.zeroonesoft.cn/golib/xiaomihome/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 }