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:
2026-07-13 10:40:20 +08:00
parent 8ffc9e4e44
commit b808e51b3e
68 changed files with 2506 additions and 688 deletions
+108 -216
View File
@@ -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))
}
}