Files
xiaomihome/miot/examples/config/config.go
T
4566704 cdc459b5ff fix(xiaomihome): 模块路径改为 git.zeroonesoft.cn/golib/xiaomihome
- 改动:go.mod module 声明与全部内部导入路径补全为完整域名路径,README 同步
- 原因:裸模块名无法被 go get 解析,发版前必须修正为 GOPRIVATE 可拉取路径
2026-10-05 08:53:23 +08:00

148 lines
4.3 KiB
Go

// Package config provides shared configuration for all MIoT examples.
// Aligns with Python miot_client.py: entry_data + miot_storage.py: auth_info.
package config
import (
"crypto/rand"
"encoding/hex"
"encoding/json"
"os"
"time"
"git.zeroonesoft.cn/golib/xiaomihome/miot"
)
// AuthInfo mirrors the OAuth token stored in Python miot_config/{uid}_{cloud_server}.dict.
// Python saves this via update_user_config -> {auth_info: {...}}.
type AuthInfo struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
MacKey string `json:"mac_key"`
ExpiresIn int `json:"expires_in"`
ExpiresTS int64 `json:"expires_ts"`
}
// Config mirrors Python entry_data (HA config_entry.data) + auth_info from storage.
// Python splits these: entry_data is in HA config, auth_info is in miot_config/*.dict.
// For standalone examples, we combine both into one file.
type Config struct {
// === auth_info (from OAuth flow, saved by E1) ===
AuthInfo AuthInfo `json:"auth_info"`
// === entry_data required fields (Python MIoTClient.__init__ validates these) ===
UID string `json:"uid"` // Xiaomi account user ID
CloudServer string `json:"cloud_server"` // Region: cn/de/i2/ru/sg/us
UUID string `json:"uuid"` // MQTT client ID seed (Python: SHA256 of HA instance UUID)
RedirectURL string `json:"redirect_url"` // OAuth callback URL
// === entry_data optional fields ===
Language string `json:"language"` // integration_language: en/zh-Hans/de etc.
CtrlMode string `json:"ctrl_mode"` // auto/cloud/lan
StoragePath string `json:"storage_path"` // data directory for miot_storage
NickName string `json:"nick_name"` // display name in Xiaomi Home
// internal
configPath string // path to this config file
}
// EntryData returns the map[string]interface{} representation for NewMIoTClient.
// Aligns with Python entry_data dict passed to MIoTClient.__init__.
func (c *Config) EntryData() map[string]interface{} {
return map[string]interface{}{
"access_token": c.AuthInfo.AccessToken,
}
}
// GetCtrlMode converts CtrlMode string to Go CtrlMode type.
func (c *Config) GetCtrlMode() miot.CtrlMode {
switch c.CtrlMode {
case "gateway":
return miot.CtrlModeGateway
case "lan":
return miot.CtrlModeLAN
default:
return miot.CtrlModeCloud
}
}
// LoadConfig reads a JSON config file at path.
func LoadConfig(path string) (*Config, error) {
if path == "" {
return SampleConfig(), nil
}
data, err := os.ReadFile(path)
if err != nil {
if os.IsNotExist(err) {
return SampleConfig(), nil
}
return nil, err
}
cfg := &Config{}
if err := json.Unmarshal(data, cfg); err != nil {
return nil, err
}
cfg.configPath = path
return cfg, nil
}
// SaveConfig writes the config as JSON to path.
func SaveConfig(path string, cfg *Config) error {
cfg.configPath = path
data, err := json.MarshalIndent(cfg, "", " ")
if err != nil {
return err
}
return os.WriteFile(path, data, 0644)
}
// DefaultPath returns the shared config file path (examples/config.json).
func DefaultPath() string {
return "../config.json"
}
// SampleConfig returns a Config with defaults, matching Python const.py defaults.
func SampleConfig() *Config {
return &Config{
CloudServer: miot.DEFAULT_CLOUD_SERVER,
RedirectURL: miot.OAUTH_REDIRECT_URL,
Language: miot.DEFAULT_INTEGRATION_LANGUAGE,
CtrlMode: miot.DEFAULT_CTRL_MODE,
NickName: miot.DEFAULT_NICK_NAME,
UUID: newUUID(),
}
}
// newUUID generates a random UUID string (Python uses SHA256 of HA instance UUID).
func NewUUID() string {
b := make([]byte, 16)
rand.Read(b)
return hex.EncodeToString(b)
}
func newUUID() string {
return NewUUID()
}
// TokenValid returns true if the access token exists and hasn't expired.
// Python: checks expires_ts > time.time().
func (a *AuthInfo) TokenValid() bool {
if a.AccessToken == "" {
return false
}
if a.ExpiresTS > 0 {
return time.Now().Unix() < a.ExpiresTS
}
return true
}
// TOKEN_EXPIRES_TS_RATIO is the safety margin applied to token expiry (Python: 0.7).
const TOKEN_EXPIRES_TS_RATIO = 0.7
// CalcExpiresTS calculates expires_ts from expires_in (seconds from now, with ratio).
// Python: int(time.time() + expires_in * TOKEN_EXPIRES_TS_RATIO).
func CalcExpiresTS(expiresIn int) int64 {
return time.Now().Unix() + int64(float64(expiresIn)*TOKEN_EXPIRES_TS_RATIO)
}