package xiaomi import ( "context" "fmt" "sync" "xiaomihome/miot" ) // Client 高级封装客户端(包装 miot.MIoTClient + miot.MIoTHttpClient) type Client struct { cfg Config inner *miot.MIoTClient http *miot.MIoTHttpClient mipsCloud *miot.MipsCloudClient specParser *miot.MIoTSpecParser deviceMu sync.RWMutex devices map[string]*miot.MIoTDevice propSubs map[string]string eventSubs map[string]string mu sync.RWMutex } // NewClient 创建并初始化客户端(零 miot import,一个 Config 搞定)。 func NewClient(ctx context.Context, cfg Config) (*Client, error) { entryData := map[string]interface{}{ "access_token": cfg.AccessToken, } miotClient := miot.NewMIoTClient("xiaomi_sdk", entryData, cfg.UID, "cn", miot.CtrlModeAuto) miotClient.SetStorage(miot.NewMIoTStorage("./data")) miotClient.SetSpecParser(miot.NewMIoTSpecParser("en", miot.NewMIoTStorage("./data"), "./data/specs")) httpClient, err := miot.NewMIoTHttpClient("cn", miot.OAUTH2_CLIENT_ID, cfg.AccessToken) if err != nil { return nil, fmt.Errorf("xiaomi: http: %w", err) } miotClient.SetHTTPClient(httpClient) // MIPS client ID 使用 config 中的 UUID,空则自动生成 mipsUUID := cfg.UUID if mipsUUID == "" { mipsUUID = generateUUID() } mipsCloud := miot.NewMipsCloudClient( "ssl://cn-ha.mqtt.io.mi.com:8883", "ha."+mipsUUID, miot.OAUTH2_CLIENT_ID, cfg.AccessToken, ) miotClient.SetMipsCloud(mipsCloud) if err := miotClient.Init(); err != nil { return nil, fmt.Errorf("xiaomi: init: %w", err) } // 初始刷新设备列表(对齐 full_client 示例) _ = miotClient.RefreshDevices() // 连接 MIPS(对齐 full_client 示例 line 83) if err := mipsCloud.Connect(); err != nil { return nil, fmt.Errorf("xiaomi: mips connect: %w", err) } return &Client{ cfg: cfg, inner: miotClient, http: httpClient, mipsCloud: mipsCloud, specParser: miot.NewMIoTSpecParser("en", miot.NewMIoTStorage("./data"), "./data/specs"), devices: make(map[string]*miot.MIoTDevice), propSubs: make(map[string]string), eventSubs: make(map[string]string), }, nil } // Close 关闭客户端。 func (c *Client) Close() error { if c.mipsCloud != nil { c.mipsCloud.Disconnect() } if c.inner != nil { return c.inner.Deinit() } return nil } func (c *Client) getOrCreateDevice(did string, info map[string]interface{}) *miot.MIoTDevice { c.deviceMu.Lock() defer c.deviceMu.Unlock() if dev, ok := c.devices[did]; ok { return dev } // 从 device info 获取 URN 和 model 用于 SPEC 加载 urn, _ := info["urn"].(string) model, _ := info["model"].(string) var spec *miot.MIoTSpecInstance if c.specParser != nil && urn != "" { s, err := c.specParser.Parse(urn, model) if err == nil { spec = s } else { fmt.Printf("[xiaomi] spec parse failed for %s (%s): %v\n", did, urn, err) } } dev := miot.NewMIoTDevice(c.inner, info, spec) c.devices[did] = dev return dev } func (c *Client) getDevice(did string) *miot.MIoTDevice { c.deviceMu.RLock() defer c.deviceMu.RUnlock() return c.devices[did] }