package devices import ( "context" "xiaomihome/xiaomi" ) // Vacuum 扫地机统一控制接口 type Vacuum interface { Device Start(ctx context.Context) error Stop(ctx context.Context) error ReturnToDock(ctx context.Context) error SetFanLevel(ctx context.Context, level int) error GetFanLevel(ctx context.Context) (int, error) GetStatus(ctx context.Context) (VacuumStatus, error) } // VacuumStatus 扫地机运行状态 type VacuumStatus struct { Power bool `json:"power"` Mode int `json:"mode"` // 0=idle, 1=sweep, 2=mop, 3=sweep+mop, 4=charging FanLevel int `json:"fan_level"` Battery int `json:"battery"` // 0-100 } type vacuumDevice struct { BaseDevice propOn siidPiid // "on" — start/stop control propMode siidPiid // "mode" — 0=charge(dock), 1=sweep, etc. propFanLevel siidPiid // "fan-level" (suction power) propBattery siidPiid // "battery-level" } // NewVacuum creates a Vacuum control for the given device. func NewVacuum(client *xiaomi.Client, info *xiaomi.DeviceInfo) (Vacuum, error) { d := &vacuumDevice{ BaseDevice: NewBaseDevice(client, info), } resolver := d.getSpecResolver() if resolver == nil { return nil, xiaomi.ErrNoSPEC } siid, piid, ok := resolver.find("on") if !ok { return nil, xiaomi.ErrNotSupported } d.propOn = siidPiid{siid, piid, true} if s, p, ok := resolver.find("mode"); ok { d.propMode = siidPiid{s, p, true} } if s, p, ok := resolver.find("fan-level"); ok { d.propFanLevel = siidPiid{s, p, true} } if s, p, ok := resolver.find("battery-level"); ok { d.propBattery = siidPiid{s, p, true} } return d, nil } func (d *vacuumDevice) Start(ctx context.Context) error { if !d.propOn.valid { return xiaomi.ErrNotSupported } return d.SetProp(ctx, d.propOn.siid, d.propOn.piid, true) } func (d *vacuumDevice) Stop(ctx context.Context) error { if !d.propOn.valid { return xiaomi.ErrNotSupported } return d.SetProp(ctx, d.propOn.siid, d.propOn.piid, false) } func (d *vacuumDevice) ReturnToDock(ctx context.Context) error { if !d.propMode.valid { // Try using on=false as fallback return d.Stop(ctx) } return d.SetProp(ctx, d.propMode.siid, d.propMode.piid, 0) } func (d *vacuumDevice) SetFanLevel(ctx context.Context, level int) error { if !d.propFanLevel.valid { return xiaomi.ErrNotSupported } return d.SetProp(ctx, d.propFanLevel.siid, d.propFanLevel.piid, level) } func (d *vacuumDevice) GetFanLevel(ctx context.Context) (int, error) { if !d.propFanLevel.valid { return 0, xiaomi.ErrNotSupported } prop, err := d.GetProp(ctx, d.propFanLevel.siid, d.propFanLevel.piid) if err != nil { return 0, err } return toInt(prop.Value), nil } func (d *vacuumDevice) GetStatus(ctx context.Context) (VacuumStatus, error) { status := VacuumStatus{} if d.propOn.valid { prop, err := d.GetProp(ctx, d.propOn.siid, d.propOn.piid) if err == nil { if b, ok := prop.Value.(bool); ok { status.Power = b } } } if d.propMode.valid { prop, err := d.GetProp(ctx, d.propMode.siid, d.propMode.piid) if err == nil { status.Mode = toInt(prop.Value) } } if d.propFanLevel.valid { prop, err := d.GetProp(ctx, d.propFanLevel.siid, d.propFanLevel.piid) if err == nil { status.FanLevel = toInt(prop.Value) } } if d.propBattery.valid { prop, err := d.GetProp(ctx, d.propBattery.siid, d.propBattery.piid) if err == nil { status.Battery = toInt(prop.Value) } } return status, nil }