refactor: 重大架构升级 — HomeID/RoomID int64化 + 设备驱动重构 + 结构化日志
主要变更: **类型系统重构** - HomeInfo.ID/RoomInfo.ID/HomeID 从 string 改为 int64,json tag 使用 ,string - GetHome/GetRooms/GetRoomDevices 接口同步修改 - 新增 int64FromStr 工具函数,更新 parseHomeInfo 解析逻辑 **新增 Speaker 设备** - 新增 KindSpeaker 类型 - classify 映射覆盖 speaker/bluetooth-speaker/alarm-clock/media-player/playback/music **设备驱动重构** - AC 驱动彻底重写:从硬编码常量改为基于 SPEC ValueList 的动态 valMapper - 所有设备(Fan/Humidifier/Light/OccupancySensor/Switch/TempHumiditySensor/Thermostat/Vacuum/WaterHeater/Cover)新增 GetState() 和 FetchState(ctx) 接口 - base.go 新增 specResolver.format() 方法 **MIoT 模块增强** - MIoTClient 新增 homeIDs 字段,支持按家庭过滤设备 - RefreshDevices 支持 homeIDs 参数 - SpecStdLib 引入 logger.Logger,log.Printf 全部迁移为结构化日志 **其他** - Config 新增 RedirectURL 和 HomeIDs 字段 - AuthInfo.ExpiresIn 类型从 int 改为 int32 - UUID 生成简化为 hex.EncodeToString - 新增 github.com/sirupsen/logrus 依赖 - .gitignore 新增 examples/*/data/ 忽略规则 - 删除已提交的 config.json 敏感配置
This commit is contained in:
+108
-216
@@ -8,6 +8,8 @@ import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"xiaomihome/logger"
|
||||
)
|
||||
|
||||
// ============================================================================
|
||||
@@ -26,25 +28,26 @@ type MIoTClient struct {
|
||||
ctrlMode CtrlMode
|
||||
|
||||
// === Network & Storage & Auth (aligned with Python) ===
|
||||
network interface{} // MIoTNetwork (optional, for network status monitoring)
|
||||
storage *MIoTStorage
|
||||
mipsSvc interface{} // MipsService (optional, for mDNS service discovery)
|
||||
http *MIoTHttpClient
|
||||
cert *MIoTCert
|
||||
miotLan interface{} // MIoTLan (optional, for LAN control)
|
||||
network interface{} // MIoTNetwork (optional, for network status monitoring)
|
||||
storage *MIoTStorage
|
||||
mipsSvc interface{} // MipsService (optional, for mDNS service discovery)
|
||||
http *MIoTHttpClient
|
||||
cert *MIoTCert
|
||||
miotLan interface{} // MIoTLan (optional, for LAN control)
|
||||
|
||||
// === OAuth client (aligned with Python miot_cloud.py MIoTOauthClient) ===
|
||||
oauth *MIoTOauthClient
|
||||
oauth *MIoTOauthClient
|
||||
homeIDs []string // 需要管理的家庭 ID 列表,nil/空=全部
|
||||
|
||||
// === Device list (aligned with Python _device_list_cache etc.) ===
|
||||
// Only store metadata map[string]interface{}, not *MIoTDevice objects
|
||||
// Python: _device_list_cache: dict[str, dict]
|
||||
deviceListCache map[string]map[string]interface{}
|
||||
deviceListCloud map[string]map[string]interface{}
|
||||
deviceListGateway map[string]map[string]interface{}
|
||||
deviceListLan map[string]map[string]interface{}
|
||||
deviceListCache map[string]map[string]interface{}
|
||||
deviceListCloud map[string]map[string]interface{}
|
||||
deviceListGateway map[string]map[string]interface{}
|
||||
deviceListLan map[string]map[string]interface{}
|
||||
deviceListUpdateTs int64
|
||||
deviceListMu sync.RWMutex
|
||||
deviceListMu sync.RWMutex
|
||||
|
||||
// === Subscription related (aligned with Python _sub_source_list / _sub_tree / _sub_device_state) ===
|
||||
subSourceList map[string]string // did -> source ("cloud"/"lan"/group_id)
|
||||
@@ -53,9 +56,9 @@ type MIoTClient struct {
|
||||
deviceStateSubs map[string]*deviceStateSub // did -> handler (H4)
|
||||
|
||||
// === Property refresh (aligned with Python _refresh_props_list etc.) ===
|
||||
refreshPropsList map[string]map[string]interface{} // key="did|siid|piid"
|
||||
refreshPropsTimer *time.Timer
|
||||
refreshRetryCount int
|
||||
refreshPropsList map[string]map[string]interface{} // key="did|siid|piid"
|
||||
refreshPropsTimer *time.Timer
|
||||
refreshRetryCount int
|
||||
|
||||
// === i18n (aligned with Python MIoTI18n) ===
|
||||
i18n *MIoTI18n
|
||||
@@ -65,10 +68,9 @@ type MIoTClient struct {
|
||||
devices map[string]*MIoTDevice // did → *MIoTDevice instance cache
|
||||
|
||||
// === Timers (aligned with Python) ===
|
||||
refreshTokenTimer *time.Timer
|
||||
refreshCertTimer *time.Timer
|
||||
refreshCloudDevicesTimer *time.Timer
|
||||
mipsLocalStateTimers map[string]*time.Timer
|
||||
refreshCertTimer *time.Timer
|
||||
refreshCloudDevicesTimer *time.Timer
|
||||
mipsLocalStateTimers map[string]*time.Timer
|
||||
showDevicesChangedNotifyTimer *time.Timer
|
||||
|
||||
// === MIPS clients (aligned with Python _mips_cloud / _mips_local) ===
|
||||
@@ -79,13 +81,13 @@ type MIoTClient struct {
|
||||
persistenceNotify func(notifyID, title, message string)
|
||||
|
||||
// === Config properties (M1: aligned with Python MIoTClient attributes) ===
|
||||
ActionDebug bool
|
||||
HideNonStandardEntities bool
|
||||
DisplayBinaryText bool
|
||||
DisplayBinaryBool bool
|
||||
CoverDeadZoneWidth int
|
||||
ActionDebug bool
|
||||
HideNonStandardEntities bool
|
||||
DisplayBinaryText bool
|
||||
DisplayBinaryBool bool
|
||||
CoverDeadZoneWidth int
|
||||
DisplayDevicesChangedNotify []string
|
||||
PersistentNotify func(string, string, string) // notifyID, title, message
|
||||
PersistentNotify func(string, string, string) // notifyID, title, message
|
||||
|
||||
// === Cloud API helpers ===
|
||||
// These fields align with Python miot_cloud.py MIoTHttpClient methods
|
||||
@@ -94,13 +96,13 @@ type MIoTClient struct {
|
||||
|
||||
// === Run control ===
|
||||
// Go uses context.Context instead of Python's asyncio loop
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
mu sync.RWMutex
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
mu sync.RWMutex
|
||||
refreshMu sync.Mutex // token 刷新互斥锁
|
||||
|
||||
// === Callbacks ===
|
||||
tokenRefreshCB func(accessToken, refreshToken string)
|
||||
// === Logger ===
|
||||
lgr logger.Logger
|
||||
}
|
||||
|
||||
// NewMIoTClient creates a new MIoTClient.
|
||||
@@ -108,28 +110,33 @@ type MIoTClient struct {
|
||||
func NewMIoTClient(entryID string, entryData map[string]interface{}, uid string, cloudServer string, ctrlMode CtrlMode) *MIoTClient {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
return &MIoTClient{
|
||||
entryID: entryID,
|
||||
entryData: entryData,
|
||||
uid: uid,
|
||||
cloudServer: cloudServer,
|
||||
ctrlMode: ctrlMode,
|
||||
deviceListCache: make(map[string]map[string]interface{}),
|
||||
deviceListCloud: make(map[string]map[string]interface{}),
|
||||
deviceListGateway: make(map[string]map[string]interface{}),
|
||||
deviceListLan: make(map[string]map[string]interface{}),
|
||||
subSourceList: make(map[string]string),
|
||||
subTree: NewMIoTMatcher(),
|
||||
subDeviceState: make(map[string]*MipsDeviceState),
|
||||
deviceStateSubs: make(map[string]*deviceStateSub),
|
||||
refreshPropsList: make(map[string]map[string]interface{}),
|
||||
mipsLocal: make(map[string]*MipsLocalClient),
|
||||
mipsLocalStateTimers: make(map[string]*time.Timer),
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
c := &MIoTClient{
|
||||
entryID: entryID,
|
||||
entryData: entryData,
|
||||
uid: uid,
|
||||
cloudServer: cloudServer,
|
||||
ctrlMode: ctrlMode,
|
||||
deviceListCache: make(map[string]map[string]interface{}),
|
||||
deviceListCloud: make(map[string]map[string]interface{}),
|
||||
deviceListGateway: make(map[string]map[string]interface{}),
|
||||
deviceListLan: make(map[string]map[string]interface{}),
|
||||
subSourceList: make(map[string]string),
|
||||
subTree: NewMIoTMatcher(),
|
||||
subDeviceState: make(map[string]*MipsDeviceState),
|
||||
deviceStateSubs: make(map[string]*deviceStateSub),
|
||||
refreshPropsList: make(map[string]map[string]interface{}),
|
||||
mipsLocal: make(map[string]*MipsLocalClient),
|
||||
mipsLocalStateTimers: make(map[string]*time.Timer),
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
lgr: logger.Default(),
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// SetLogger sets a custom logger for MIoTClient.
|
||||
func (c *MIoTClient) SetLogger(l logger.Logger) { c.lgr = l }
|
||||
|
||||
// Init initializes the MIoTClient.
|
||||
// Aligns with Python MIoTClient.init_async.
|
||||
func (c *MIoTClient) Init() error {
|
||||
@@ -149,7 +156,7 @@ func (c *MIoTClient) Init() error {
|
||||
if et, ok := authInfo["expires_ts"].(float64); ok {
|
||||
c.entryData["expires_ts"] = et
|
||||
}
|
||||
fmt.Printf("[MIoTClient] Loaded token from storage\n")
|
||||
c.lgr.Infof("[MIoTClient] Loaded token from storage\n")
|
||||
} else if accessToken != "" {
|
||||
// First run: write entryData token to storage
|
||||
expiresTS := float64(time.Now().Unix()) + 3600*24*30 // default 30 days
|
||||
@@ -161,16 +168,18 @@ func (c *MIoTClient) Init() error {
|
||||
"refresh_token": refreshToken,
|
||||
"expires_ts": expiresTS,
|
||||
}, true)
|
||||
fmt.Printf("[MIoTClient] Saved initial token to storage\n")
|
||||
c.lgr.Infof("[MIoTClient] Saved initial token to storage\n")
|
||||
}
|
||||
}
|
||||
|
||||
// Keep access_token in entryData for downstream use
|
||||
c.entryData["access_token"] = accessToken
|
||||
if c.entryData != nil {
|
||||
c.entryData["access_token"] = accessToken
|
||||
}
|
||||
|
||||
// 2. Load device cache
|
||||
if err := c.loadCacheDevice(); err != nil {
|
||||
fmt.Printf("[MIoTClient] Warning: failed to load device cache: %v\n", err)
|
||||
c.lgr.Warnf("[MIoTClient] Warning: failed to load device cache: %v\n", err)
|
||||
}
|
||||
|
||||
// 3.1 Initialize OAuth client
|
||||
@@ -178,27 +187,27 @@ func (c *MIoTClient) Init() error {
|
||||
oauthRedirectURL, _ := c.entryData["oauth_redirect_url"].(string)
|
||||
uuid, _ := c.entryData["uuid"].(string)
|
||||
c.oauth = NewMIoTOauthClient(OAUTH2_CLIENT_ID, oauthRedirectURL, c.cloudServer, uuid)
|
||||
fmt.Printf("[MIoTClient] OAuth client initialized\n")
|
||||
c.lgr.Infof("[MIoTClient] OAuth client initialized\n")
|
||||
}
|
||||
|
||||
// 3.2 Initialize Cert client
|
||||
cert, err := NewMIoTCert(c.storage, c.uid, c.cloudServer)
|
||||
if err != nil {
|
||||
fmt.Printf("[MIoTClient] Warning: cert init failed: %v\n", err)
|
||||
c.lgr.Warnf("[MIoTClient] Warning: cert init failed: %v\n", err)
|
||||
} else {
|
||||
c.cert = cert
|
||||
c.cert.VerifyCACert()
|
||||
fmt.Printf("[MIoTClient] Cert client initialized\n")
|
||||
c.lgr.Infof("[MIoTClient] Cert client initialized\n")
|
||||
}
|
||||
|
||||
// 4. Initialize HTTP client (use token from storage if available)
|
||||
if c.http == nil && accessToken != "" {
|
||||
httpClient, err := NewMIoTHttpClient(c.cloudServer, OAUTH2_CLIENT_ID, accessToken)
|
||||
if err != nil {
|
||||
fmt.Printf("[MIoTClient] Warning: failed to create HTTP client: %v\n", err)
|
||||
c.lgr.Warnf("[MIoTClient] Warning: failed to create HTTP client: %v\n", err)
|
||||
} else {
|
||||
c.http = httpClient
|
||||
fmt.Printf("[MIoTClient] HTTP client created\n")
|
||||
c.lgr.Infof("[MIoTClient] HTTP client created\n")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,9 +223,9 @@ func (c *MIoTClient) Init() error {
|
||||
// Connect asynchronously to not block Init
|
||||
c.mu.Unlock()
|
||||
if err := mips.Connect(); err != nil {
|
||||
fmt.Printf("[MIoTClient] Warning: MIPS cloud connect failed: %v\n", err)
|
||||
c.lgr.Warnf("[MIoTClient] Warning: MIPS cloud connect failed: %v\n", err)
|
||||
} else {
|
||||
fmt.Printf("[MIoTClient] MIPS cloud connected\n")
|
||||
c.lgr.Infof("[MIoTClient] MIPS cloud connected\n")
|
||||
}
|
||||
c.mu.Lock()
|
||||
}
|
||||
@@ -230,7 +239,7 @@ func (c *MIoTClient) Init() error {
|
||||
nw.SubNetworkStatus(key, c.onNetworkStatusChanged)
|
||||
status := nw.GetNetworkStatus()
|
||||
c.onNetworkStatusChanged(status)
|
||||
fmt.Printf("[MIoTClient] Network status subscribed (online=%v)\n", status)
|
||||
c.lgr.Infof("[MIoTClient] Network status subscribed (online=%v)\n", status)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,12 +258,12 @@ func (c *MIoTClient) Init() error {
|
||||
groupID, _ := info["group_id"].(string)
|
||||
if groupID != "" {
|
||||
svc.SubServiceChange(key, groupID, c.onMipsServiceStateChange)
|
||||
fmt.Printf("[MIoTClient] mDNS service subscribed: home=%s, group=%s\n", homeID, groupID)
|
||||
c.lgr.Infof("[MIoTClient] mDNS service subscribed: home=%s, group=%s\n", homeID, groupID)
|
||||
|
||||
// Check if service already discovered
|
||||
serviceData := svc.GetServices(groupID)
|
||||
if sd, exists := serviceData[groupID]; exists && sd.validService() {
|
||||
fmt.Printf("[MIoTClient] Central mips service scanned: %s, %v\n", homeID, sd)
|
||||
c.lgr.Infof("[MIoTClient] Central mips service scanned: %s, %v\n", homeID, sd)
|
||||
mips := NewMipsLocalClient(
|
||||
getVirtualDID(c.entryData),
|
||||
sd.Addresses[0],
|
||||
@@ -285,7 +294,7 @@ func (c *MIoTClient) Init() error {
|
||||
if lan.InitDone() {
|
||||
c.onMiotLanStateChange(true)
|
||||
}
|
||||
fmt.Printf("[MIoTClient] LAN control initialized\n")
|
||||
c.lgr.Infof("[MIoTClient] LAN control initialized\n")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -315,9 +324,9 @@ func (c *MIoTClient) Init() error {
|
||||
}
|
||||
c.i18n = NewMIoTI18n(lang)
|
||||
c.i18n.Init()
|
||||
fmt.Printf("[MIoTClient] i18n initialized (lang=%s)\n", lang)
|
||||
c.lgr.Infof("[MIoTClient] i18n initialized (lang=%s)\n", lang)
|
||||
|
||||
fmt.Printf("[MIoTClient] Initialized\n")
|
||||
c.lgr.Infof("[MIoTClient] Initialized\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -349,7 +358,7 @@ func (c *MIoTClient) Deinit() error {
|
||||
if c.mipsCloud != nil {
|
||||
c.mipsCloud.UnsubState(key)
|
||||
c.mipsCloud.Disconnect()
|
||||
fmt.Printf("[MIoTClient] MIPS cloud disconnected\n")
|
||||
c.lgr.Infof("[MIoTClient] MIPS cloud disconnected\n")
|
||||
}
|
||||
|
||||
// 4. Cancel refresh cloud devices timer
|
||||
@@ -397,11 +406,7 @@ func (c *MIoTClient) Deinit() error {
|
||||
}
|
||||
}
|
||||
|
||||
// 6. Stop token/cert timers
|
||||
if c.refreshTokenTimer != nil {
|
||||
c.refreshTokenTimer.Stop()
|
||||
c.refreshTokenTimer = nil
|
||||
}
|
||||
// 6. Stop cert timers
|
||||
if c.refreshCertTimer != nil {
|
||||
c.refreshCertTimer.Stop()
|
||||
c.refreshCertTimer = nil
|
||||
@@ -436,9 +441,9 @@ func (c *MIoTClient) Deinit() error {
|
||||
}
|
||||
deviceKey := fmt.Sprintf("%s_%s", c.uid, c.cloudServer)
|
||||
if err := c.storage.Save("miot_devices", deviceKey, deviceListForStorage); err != nil {
|
||||
fmt.Printf("[MIoTClient] Warning: failed to save devices on deinit: %v\n", err)
|
||||
c.lgr.Warnf("[MIoTClient] Warning: failed to save devices on deinit: %v\n", err)
|
||||
}
|
||||
fmt.Printf("[MIoTClient] Device cache saved to storage\n")
|
||||
c.lgr.Infof("[MIoTClient] Device cache saved to storage\n")
|
||||
}
|
||||
|
||||
// Cancel context
|
||||
@@ -449,7 +454,7 @@ func (c *MIoTClient) Deinit() error {
|
||||
// Clear subscription tree
|
||||
c.subTree.Clear()
|
||||
|
||||
fmt.Printf("[MIoTClient] Deinitialized\n")
|
||||
c.lgr.Infof("[MIoTClient] Deinitialized\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -488,7 +493,7 @@ func (c *MIoTClient) loadCacheDevice() error {
|
||||
c.deviceListCache[did] = infoMap
|
||||
}
|
||||
}
|
||||
fmt.Printf("[MIoTClient] Loaded %d devices from cache\n", len(deviceList))
|
||||
c.lgr.Infof("[MIoTClient] Loaded %d devices from cache\n", len(deviceList))
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -578,122 +583,9 @@ func (c *MIoTClient) UnsubDeviceState(did string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// SetTokenRefreshCallback sets a callback invoked when the background timer refreshes the token.
|
||||
func (c *MIoTClient) SetTokenRefreshCallback(cb func(accessToken, refreshToken string)) {
|
||||
c.tokenRefreshCB = cb
|
||||
}
|
||||
|
||||
// M2: RequestRefreshAuthInfo / RequestRefreshUserCert
|
||||
// ============================================================================
|
||||
|
||||
// RequestRefreshAuthInfo schedules an OAuth token refresh after delaySec seconds.
|
||||
// Uses MIHOME_CERT_EXPIRE_MARGIN to determine when refresh is needed.
|
||||
// Aligns with Python: __request_refresh_auth_info(delay_sec).
|
||||
func (c *MIoTClient) RequestRefreshAuthInfo(delaySec int) {
|
||||
if c.oauth == nil || c.storage == nil {
|
||||
return
|
||||
}
|
||||
d := time.Duration(delaySec) * time.Second
|
||||
c.mu.Lock()
|
||||
if c.refreshTokenTimer != nil {
|
||||
c.refreshTokenTimer.Stop()
|
||||
}
|
||||
c.refreshTokenTimer = time.AfterFunc(d, func() {
|
||||
c.refreshAuthInfo()
|
||||
})
|
||||
c.mu.Unlock()
|
||||
}
|
||||
|
||||
// refreshAuthInfo performs the actual token refresh logic.
|
||||
// Aligns with Python: refresh_oauth_info_async.
|
||||
func (c *MIoTClient) refreshAuthInfo() {
|
||||
c.refreshMu.Lock()
|
||||
defer c.refreshMu.Unlock()
|
||||
|
||||
if c.oauth == nil || c.storage == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Load current auth config
|
||||
authInfo := c.storage.LoadUserConfig(c.uid, c.cloudServer, nil)
|
||||
accessToken, _ := authInfo["access_token"].(string)
|
||||
refreshToken, _ := authInfo["refresh_token"].(string)
|
||||
expiresTS, _ := authInfo["expires_ts"].(float64)
|
||||
|
||||
// Check if refresh is needed: expires_ts - now <= MIHOME_REFRESH_THRESHOLD
|
||||
now := float64(time.Now().UnixMilli()) / 1000
|
||||
needRefresh := (expiresTS - now) <= float64(MIHOME_REFRESH_THRESHOLD)
|
||||
|
||||
if !needRefresh && accessToken != "" {
|
||||
// Schedule next check
|
||||
nextDelay := int(expiresTS - now - float64(MIHOME_REFRESH_THRESHOLD))
|
||||
if nextDelay < 60 {
|
||||
nextDelay = 60
|
||||
}
|
||||
c.RequestRefreshAuthInfo(nextDelay)
|
||||
fmt.Printf("[MIoTClient] Token still valid, recheck in %ds\n", nextDelay)
|
||||
return
|
||||
}
|
||||
|
||||
if refreshToken == "" && accessToken == "" {
|
||||
fmt.Printf("[MIoTClient] No refresh token available\n")
|
||||
return
|
||||
}
|
||||
|
||||
// Refresh access token
|
||||
result, err := c.oauth.RefreshAccessToken(refreshToken)
|
||||
if err != nil {
|
||||
fmt.Printf("[MIoTClient] Token refresh FAILED: %v\n", err)
|
||||
// Retry after delay
|
||||
c.RequestRefreshAuthInfo(RefreshAuthRetryDelay)
|
||||
return
|
||||
}
|
||||
|
||||
newAccessToken, _ := result["access_token"].(string)
|
||||
newRefreshToken, _ := result["refresh_token"].(string)
|
||||
var newExpiresTS float64
|
||||
switch v := result["expires_ts"].(type) {
|
||||
case float64: newExpiresTS = v
|
||||
case int64: newExpiresTS = float64(v)
|
||||
}
|
||||
|
||||
if newAccessToken == "" {
|
||||
fmt.Printf("[MIoTClient] Token refresh returned empty access_token\n")
|
||||
return
|
||||
}
|
||||
|
||||
// Update HTTP client
|
||||
if c.http != nil {
|
||||
c.http.UpdateHTTPHeader(c.cloudServer, OAUTH2_CLIENT_ID, newAccessToken)
|
||||
}
|
||||
|
||||
// Update MIPS cloud client
|
||||
if c.mipsCloud != nil {
|
||||
c.mipsCloud.UpdateAccessToken(newAccessToken)
|
||||
}
|
||||
|
||||
// Save new auth info
|
||||
newAuthInfo := map[string]interface{}{
|
||||
"access_token": newAccessToken,
|
||||
"refresh_token": newRefreshToken,
|
||||
"expires_ts": newExpiresTS,
|
||||
}
|
||||
c.storage.UpdateUserConfig(c.uid, c.cloudServer, newAuthInfo, true)
|
||||
|
||||
fmt.Printf("[MIoTClient] Token refreshed successfully\n")
|
||||
|
||||
// Notify callback (xiaomi layer uses this to persist to DB)
|
||||
if c.tokenRefreshCB != nil {
|
||||
c.tokenRefreshCB(newAccessToken, newRefreshToken)
|
||||
}
|
||||
|
||||
// Schedule next refresh
|
||||
nextDelay := int(newExpiresTS - now - float64(MIHOME_REFRESH_THRESHOLD))
|
||||
if nextDelay < 60 {
|
||||
nextDelay = 60
|
||||
}
|
||||
c.RequestRefreshAuthInfo(nextDelay)
|
||||
// SetHomeIDs sets the home IDs for device refresh scope. nil/empty = all homes.
|
||||
func (c *MIoTClient) SetHomeIDs(homeIDs []string) {
|
||||
c.homeIDs = homeIDs
|
||||
}
|
||||
|
||||
// RefreshAuthInfo 手动触发 token 刷新并同步返回结果(对齐 Python refresh_oauth_info_async)。
|
||||
@@ -755,7 +647,7 @@ func (c *MIoTClient) RefreshAuthInfo() (map[string]interface{}, error) {
|
||||
c.mipsCloud.UpdateAccessToken(ac)
|
||||
}
|
||||
|
||||
fmt.Printf("[MIoTClient] RefreshAuthInfo: token refreshed\n")
|
||||
c.lgr.Infof("[MIoTClient] RefreshAuthInfo: token refreshed\n")
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -786,7 +678,7 @@ func (c *MIoTClient) refreshUserCert() {
|
||||
|
||||
// Verify CA cert
|
||||
if ok, err := c.cert.VerifyCACert(); err != nil || !ok {
|
||||
fmt.Printf("[MIoTClient] CA cert verification FAILED: %v\n", err)
|
||||
c.lgr.Errorf("[MIoTClient] CA cert verification FAILED: %v\n", err)
|
||||
}
|
||||
|
||||
// Check remaining time
|
||||
@@ -798,14 +690,14 @@ func (c *MIoTClient) refreshUserCert() {
|
||||
nextDelay = 60
|
||||
}
|
||||
c.RequestRefreshUserCert(nextDelay)
|
||||
fmt.Printf("[MIoTClient] Cert still valid for %ds, recheck in %ds\n", remainSec, nextDelay)
|
||||
c.lgr.Infof("[MIoTClient] Cert still valid for %ds, recheck in %ds\n", remainSec, nextDelay)
|
||||
return
|
||||
}
|
||||
|
||||
// Load user key
|
||||
_, err := c.cert.LoadUserKey()
|
||||
if err != nil {
|
||||
fmt.Printf("[MIoTClient] Cert refresh: load key FAILED: %v\n", err)
|
||||
c.lgr.Errorf("[MIoTClient] Cert refresh: load key FAILED: %v\n", err)
|
||||
// Retry after delay
|
||||
c.RequestRefreshUserCert(3600)
|
||||
return
|
||||
@@ -814,7 +706,7 @@ func (c *MIoTClient) refreshUserCert() {
|
||||
// Generate CSR
|
||||
csr, err := c.cert.GenUserCSR()
|
||||
if err != nil {
|
||||
fmt.Printf("[MIoTClient] Cert refresh: gen CSR FAILED: %v\n", err)
|
||||
c.lgr.Errorf("[MIoTClient] Cert refresh: gen CSR FAILED: %v\n", err)
|
||||
c.RequestRefreshUserCert(3600)
|
||||
return
|
||||
}
|
||||
@@ -822,18 +714,18 @@ func (c *MIoTClient) refreshUserCert() {
|
||||
// Request cert from cloud
|
||||
certPEM, err := c.http.GetCentralCert(csr)
|
||||
if err != nil {
|
||||
fmt.Printf("[MIoTClient] Cert refresh: get cert from cloud FAILED: %v\n", err)
|
||||
c.lgr.Errorf("[MIoTClient] Cert refresh: get cert from cloud FAILED: %v\n", err)
|
||||
c.RequestRefreshUserCert(RefreshCertRetryDelay)
|
||||
return
|
||||
}
|
||||
|
||||
// Save cert
|
||||
if err := c.cert.SaveUserCert(certPEM); err != nil {
|
||||
fmt.Printf("[MIoTClient] Cert refresh: save cert FAILED: %v\n", err)
|
||||
c.lgr.Errorf("[MIoTClient] Cert refresh: save cert FAILED: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("[MIoTClient] Cert refreshed successfully\n")
|
||||
c.lgr.Infof("[MIoTClient] Cert refreshed successfully\n")
|
||||
|
||||
// Update MIPS local clients with new certs (if any are still connected)
|
||||
for _, mips := range c.mipsLocal {
|
||||
@@ -891,13 +783,13 @@ func (c *MIoTClient) ShowCentralStateChangedNotify(connected bool) {
|
||||
// Aligns with Python: __on_mips_cloud_state_changed.
|
||||
func (c *MIoTClient) onMipsCloudStateChanged(key string, connected bool) {
|
||||
if connected {
|
||||
fmt.Printf("[MIoTClient] MIPS cloud connected\n")
|
||||
c.lgr.Infof("[MIoTClient] MIPS cloud connected\n")
|
||||
// Refresh devices on reconnect
|
||||
c.ScheduleRefreshDevices(RefreshCloudDevicesDelay * time.Millisecond)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("[MIoTClient] MIPS cloud disconnected\n")
|
||||
c.lgr.Infof("[MIoTClient] MIPS cloud disconnected\n")
|
||||
|
||||
// Disconnect: mark all cloud devices offline
|
||||
c.deviceListMu.Lock()
|
||||
@@ -929,7 +821,7 @@ func (c *MIoTClient) onMipsCloudStateChanged(key string, connected bool) {
|
||||
// onNetworkStatusChanged handles network connectivity changes.
|
||||
// Aligns with Python: __on_network_status_changed.
|
||||
func (c *MIoTClient) onNetworkStatusChanged(status bool) {
|
||||
fmt.Printf("[MIoTClient] Network status changed: online=%v\n", status)
|
||||
c.lgr.Infof("[MIoTClient] Network status changed: online=%v\n", status)
|
||||
if status {
|
||||
// Network is back; trigger device refresh and reconnect MIPS
|
||||
if c.mipsCloud != nil && !c.mipsCloud.IsConnected() {
|
||||
@@ -942,15 +834,15 @@ func (c *MIoTClient) onNetworkStatusChanged(status bool) {
|
||||
// onMipsServiceStateChange handles mDNS service discoveries for central gateways.
|
||||
// Aligns with Python: __on_mips_service_state_change.
|
||||
func (c *MIoTClient) onMipsServiceStateChange(groupID string, state MipsServiceState, data map[string]interface{}) {
|
||||
fmt.Printf("[MIoTClient] MIPS service change: group=%s, state=%s\n", groupID, state)
|
||||
c.lgr.Infof("[MIoTClient] MIPS service change: group=%s, state=%s\n", groupID, state)
|
||||
|
||||
virtualDID := getVirtualDID(c.entryData)
|
||||
if virtualDID == "" {
|
||||
fmt.Printf("[MIoTClient] MIPS service: missing virtual_did, skip\n")
|
||||
c.lgr.Infof("[MIoTClient] MIPS service: missing virtual_did, skip\n")
|
||||
return
|
||||
}
|
||||
if c.cert == nil {
|
||||
fmt.Printf("[MIoTClient] MIPS service: cert not initialized, skip\n")
|
||||
c.lgr.Infof("[MIoTClient] MIPS service: cert not initialized, skip\n")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -959,7 +851,7 @@ func (c *MIoTClient) onMipsServiceStateChange(groupID string, state MipsServiceS
|
||||
// Create new MipsLocalClient
|
||||
host, _ := data["addresses"].([]interface{})
|
||||
if len(host) == 0 {
|
||||
fmt.Printf("[MIoTClient] MIPS service: no addresses for %s\n", groupID)
|
||||
c.lgr.Infof("[MIoTClient] MIPS service: no addresses for %s\n", groupID)
|
||||
return
|
||||
}
|
||||
hostStr, _ := host[0].(string)
|
||||
@@ -980,7 +872,7 @@ func (c *MIoTClient) onMipsServiceStateChange(groupID string, state MipsServiceS
|
||||
mips.SubState(groupID, c.onMipsLocalStateChanged)
|
||||
mips.Connect()
|
||||
c.mipsLocal[groupID] = mips
|
||||
fmt.Printf("[MIoTClient] MIPS local client created: %s @ %s:%d\n", groupID, hostStr, int(port))
|
||||
c.lgr.Infof("[MIoTClient] MIPS local client created: %s @ %s:%d\n", groupID, hostStr, int(port))
|
||||
|
||||
case MipsServiceUpdated:
|
||||
// Disconnect old, connect new
|
||||
@@ -1010,7 +902,7 @@ func (c *MIoTClient) onMipsServiceStateChange(groupID string, state MipsServiceS
|
||||
mips.SubState(groupID, c.onMipsLocalStateChanged)
|
||||
mips.Connect()
|
||||
c.mipsLocal[groupID] = mips
|
||||
fmt.Printf("[MIoTClient] MIPS local client updated: %s @ %s:%d\n", groupID, hostStr, int(port))
|
||||
c.lgr.Infof("[MIoTClient] MIPS local client updated: %s @ %s:%d\n", groupID, hostStr, int(port))
|
||||
|
||||
case MipsServiceRemoved:
|
||||
if mips, exists := c.mipsLocal[groupID]; exists {
|
||||
@@ -1018,7 +910,7 @@ func (c *MIoTClient) onMipsServiceStateChange(groupID string, state MipsServiceS
|
||||
mips.UnsubState(groupID)
|
||||
mips.Disconnect()
|
||||
delete(c.mipsLocal, groupID)
|
||||
fmt.Printf("[MIoTClient] MIPS local client removed: %s\n", groupID)
|
||||
c.lgr.Infof("[MIoTClient] MIPS local client removed: %s\n", groupID)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1027,7 +919,7 @@ func (c *MIoTClient) onMipsServiceStateChange(groupID string, state MipsServiceS
|
||||
// Aligns with Python: __on_mips_local_state_changed.
|
||||
// key is the groupID of the gateway.
|
||||
func (c *MIoTClient) onMipsLocalStateChanged(key string, connected bool) {
|
||||
fmt.Printf("[MIoTClient] MIPS local state changed: group=%s, connected=%v\n", key, connected)
|
||||
c.lgr.Infof("[MIoTClient] MIPS local state changed: group=%s, connected=%v\n", key, connected)
|
||||
|
||||
if connected {
|
||||
// Reconnect: pull gateway device list
|
||||
@@ -1074,7 +966,7 @@ func (c *MIoTClient) onMipsLocalStateChanged(key string, connected bool) {
|
||||
// onMiotLanStateChange handles LAN controller state transitions.
|
||||
// Aligns with Python: __on_miot_lan_state_change.
|
||||
func (c *MIoTClient) onMiotLanStateChange(initialized bool) {
|
||||
fmt.Printf("[MIoTClient] LAN state changed: initialized=%v\n", initialized)
|
||||
c.lgr.Infof("[MIoTClient] LAN state changed: initialized=%v\n", initialized)
|
||||
if initialized {
|
||||
c.deviceListMu.Lock()
|
||||
lan, ok := c.miotLan.(*MIoTLan)
|
||||
@@ -1134,7 +1026,7 @@ func (c *MIoTClient) onLanDeviceStateChanged(did string, state map[string]interf
|
||||
// Aligns with Python: __on_gw_device_list_changed.
|
||||
// Called when a MipsLocalClient reports that its device list has changed.
|
||||
func (c *MIoTClient) onGWDeviceListChanged() {
|
||||
fmt.Printf("[MIoTClient] GW device list changed\n")
|
||||
c.lgr.Infof("[MIoTClient] GW device list changed\n")
|
||||
|
||||
for groupID, mips := range c.mipsLocal {
|
||||
if mips == nil || !mips.IsConnected() {
|
||||
@@ -1144,7 +1036,7 @@ func (c *MIoTClient) onGWDeviceListChanged() {
|
||||
// Pull device list from gateway
|
||||
payload, err := mips.GetDevList("", MipsRequestTimeoutDefault*time.Millisecond)
|
||||
if err != nil {
|
||||
fmt.Printf("[MIoTClient] GW device list pull failed for %s: %v\n", groupID, err)
|
||||
c.lgr.Errorf("[MIoTClient] GW device list pull failed for %s: %v\n", groupID, err)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -1153,7 +1045,7 @@ func (c *MIoTClient) onGWDeviceListChanged() {
|
||||
Result map[string]interface{} `json:"result"`
|
||||
}
|
||||
if err := json.Unmarshal([]byte(payload), &result); err != nil {
|
||||
fmt.Printf("[MIoTClient] GW device list parse failed for %s: %v\n", groupID, err)
|
||||
c.lgr.Errorf("[MIoTClient] GW device list parse failed for %s: %v\n", groupID, err)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -1198,7 +1090,7 @@ func (c *MIoTClient) updateDevicesFromGW(groupID string, devices map[string]inte
|
||||
}
|
||||
|
||||
if changed {
|
||||
fmt.Printf("[MIoTClient] GW devices updated: group=%s, count=%d\n", groupID, len(devices))
|
||||
c.lgr.Infof("[MIoTClient] GW devices updated: group=%s, count=%d\n", groupID, len(devices))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user