package xiaomi import ( "context" "crypto/rand" "encoding/hex" "fmt" "time" "git.zeroonesoft.cn/golib/xiaomihome/miot" ) // TokenResult OAuth token 交换/刷新结果 type TokenResult struct { AccessToken string `json:"access_token"` RefreshToken string `json:"refresh_token"` ExpiresIn int32 `json:"expires_in"` ExpiresTS int64 `json:"expires_ts"` UID string `json:"uid"` } // AuthURL 生成小米 OAuth 授权页面 URL。 func AuthURL(cfg Config) string { uuid := cfg.UUID if uuid == "" { uuid = generateUUID() } redirectURL := cfg.RedirectURL if redirectURL == "" { redirectURL = miot.OAUTH_REDIRECT_URL } oa := miot.NewMIoTOauthClient(miot.OAUTH2_CLIENT_ID, redirectURL, "cn", uuid) return oa.GenAuthURL("", "", nil, false) } // ExchangeCode 用 authorization code 交换 access token。 func ExchangeCode(ctx context.Context, cfg Config, code string) (*TokenResult, error) { uuid := cfg.UUID if uuid == "" { uuid = generateUUID() } redirectURL := cfg.RedirectURL if redirectURL == "" { redirectURL = miot.OAUTH_REDIRECT_URL } oa := miot.NewMIoTOauthClient(miot.OAUTH2_CLIENT_ID, redirectURL, "cn", uuid) result, err := oa.GetAccessToken(code) if err != nil { return nil, fmt.Errorf("xiaomi: exchange token: %w", err) } expiresIn := intVal(result, "expires_in") return &TokenResult{ AccessToken: strVal(result, "access_token"), RefreshToken: strVal(result, "refresh_token"), ExpiresIn: int32(expiresIn), ExpiresTS: time.Now().Unix() + int64(expiresIn), UID: strVal(result, "uid"), }, nil } func generateUUID() string { b := make([]byte, 16) rand.Read(b) return hex.EncodeToString(b) }