package bridge import ( "context" "fmt" "xiaomihome/xiaomi/devices" ) // ── 回调 ── // OnStateChanged 注册全局设备状态变化回调。state 是 devices 包的类型化 State 结构体, // 调用方自行 switch 类型(ACState / LightState / FanState ...)。 func (b *Bridge) OnStateChanged(handler func(id int64, did, deviceType, changed string, state interface{})) { b.mu.Lock() b.onStateChanged = handler b.mu.Unlock() } // StartReceiving 为所有设备注册 MQTT 消息接收回调(MQTT 订阅已在 miot 层完成)。 func (b *Bridge) StartReceiving() { b.mu.RLock() defer b.mu.RUnlock() for did, d := range b.dv { devType := deviceTypeName(d) switch ctrl := d.(type) { case devices.AirConditioner: ctrl.OnStateChanged(func(s devices.ACState, ch string) { b.fire(did, devType, ch, s) }) case devices.Light: ctrl.OnStateChanged(func(s devices.LightState, ch string) { b.fire(did, devType, ch, s) }) case devices.Switch: ctrl.OnStateChanged(func(ch int, on bool) { b.fire(did, devType, fmt.Sprintf("ch%d", ch), map[string]interface{}{"channel": ch, "on": on}) }) case devices.Fan: ctrl.OnStateChanged(func(s devices.FanState, ch string) { b.fire(did, devType, ch, s) }) case devices.Cover: ctrl.OnStateChanged(func(pos int) { b.fire(did, devType, devices.PropPosition, pos) }) case devices.Humidifier: ctrl.OnStateChanged(func(s devices.HumidifierState, ch string) { b.fire(did, devType, ch, s) }) case devices.Vacuum: ctrl.OnStateChanged(func(s devices.VacuumStatus, ch string) { b.fire(did, devType, ch, s) }) case devices.WaterHeater: ctrl.OnStateChanged(func(s devices.HeaterState, ch string) { b.fire(did, devType, ch, s) }) case devices.Thermostat: ctrl.OnStateChanged(func(s devices.HeaterState, ch string) { b.fire(did, devType, ch, s) }) case devices.TempHumiditySensor: ctrl.OnStateChanged(func(temp, hum float64) { b.fire(did, devType, "temperature_humidity", map[string]float64{"temperature": temp, "humidity": hum}) }) case devices.OccupancySensor: ctrl.OnStateChanged(func(occ bool, lux float64) { b.fire(did, devType, "occupancy", map[string]interface{}{"occupied": occ, "illuminance": lux}) }) case devices.Speaker: ctrl.OnStateChanged(func(s devices.SpeakerState, ch string) { b.fire(did, devType, ch, s) }) } } } func (b *Bridge) fire(did, devType, changed string, state interface{}) { b.mu.RLock() cb := b.onStateChanged b.mu.RUnlock() if cb != nil { cb(b.id, did, devType, changed, state) } } func deviceTypeNameRaw(d interface{}) string { if d == nil { return "unknown" } return deviceTypeName(d) } // ── 状态 ── // FetchAllStates 批量云端拉取所有设备状态,更新缓存。 func (b *Bridge) FetchAllStates(ctx context.Context) { b.mu.RLock() defer b.mu.RUnlock() for _, d := range b.dv { switch ctrl := d.(type) { case devices.AirConditioner: if s, err := ctrl.FetchState(ctx); err == nil { b.lgr.Infof("[bridge] %d AC state: on=%v mode=%s target=%.1f", b.id, s.On, s.Mode, s.TargetTemperature) } case devices.Light: if s, err := ctrl.FetchState(ctx); err == nil { b.lgr.Infof("[bridge] %d Light state: on=%v brightness=%d", b.id, s.On, s.Brightness) } case devices.Switch: if s, err := ctrl.FetchState(ctx); err == nil { b.lgr.Infof("[bridge] %d Switch state: %v", b.id, s) } case devices.Fan: if s, err := ctrl.FetchState(ctx); err == nil { b.lgr.Infof("[bridge] %d Fan state: on=%v level=%d", b.id, s.On, s.FanLevel) } case devices.Humidifier: if s, err := ctrl.FetchState(ctx); err == nil { b.lgr.Infof("[bridge] %d Humidifier state: on=%v hum=%d", b.id, s.On, s.TargetHumidity) } case devices.Vacuum: if s, err := ctrl.FetchState(ctx); err == nil { b.lgr.Infof("[bridge] %d Vacuum state: on=%v mode=%d", b.id, s.On, s.Mode) } case devices.WaterHeater: if s, err := ctrl.FetchState(ctx); err == nil { b.lgr.Infof("[bridge] %d Heater: on=%v target=%.1f", b.id, s.On, s.TargetTemp) } case devices.Thermostat: if s, err := ctrl.FetchState(ctx); err == nil { b.lgr.Infof("[bridge] %d Thermostat: target=%.1f current=%.1f", b.id, s.TargetTemp, s.CurrentTemp) } case devices.Cover: if pos, err := ctrl.FetchState(ctx); err == nil { b.lgr.Infof("[bridge] %d Cover: pos=%d", b.id, pos) } case devices.TempHumiditySensor: if t, h, err := ctrl.FetchState(ctx); err == nil { b.lgr.Infof("[bridge] %d Sensor: temp=%.1f hum=%.1f", b.id, t, h) } case devices.OccupancySensor: if o, l, err := ctrl.FetchState(ctx); err == nil { b.lgr.Infof("[bridge] %d Occupancy: occ=%v lux=%.0f", b.id, o, l) } } } }