Files
xiaomihome/xiaomi/devices/vacuum.go
T
4566704 2b7de69403 refactor(logger): 日志改用 logrus 全局包级调用,移除 Logger 注入接口
为什么:并入宿主项目后须与宿主日志体系一致——宿主统一用 pkg/logger 初始化
logrus 全局实例并直接 logrus.Xxx 包级调用;包一层 Logger 接口会让 logrus 的
caller 定位(报告调用文件:行号)失准。

改动:
- 删除 logger 包(Logger 接口 / Default / SetDefault,默认本就是 logrus.StandardLogger)
- 11 个结构体移除 lgr logger.Logger 字段、SetLogger 方法及构造中的 lgr 初始化
- 192 处 lgr.Xxxf 调用改为 logrus.Xxxf,日志消息文本保持不变
- xiaomi.Client 移除 Logger() 访问器,air_conditioner 回读日志去掉判空调用直连 logrus
- miot_i18n.go 两处标准库 log.Printf 改为 logrus.Errorf,统一日志出口
- go.mod:logrus 从 indirect 提升为直接依赖
- goimports/goformat 全树规范化:此前部分文件未 gofmt(单行 if、对齐),本次顺带
  格式化,纯空白/换行变化,无逻辑改动

验证:go build / go vet / go test ./... 全部通过(bridge、miot、xiaomi、devices、specs)
2026-09-15 20:16:26 +08:00

165 lines
4.8 KiB
Go

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) (VacuumProps, error)
OnPropsChanged(handler func(props VacuumProps, changed string)) (string, error)
GetProps() VacuumProps
FetchProps(ctx context.Context) (VacuumProps, error)
}
// VacuumProps 扫地机运行状态(字段名对齐 SPEC type)
type VacuumProps struct {
On bool `json:"on"`
Mode int `json:"mode"`
FanLevel int `json:"fanLevel"`
Battery int `json:"battery"`
}
type vacuumDevice struct {
BaseDevice
propOn siidPiid
propMode siidPiid
propFanLevel siidPiid
propBattery siidPiid
propsCache VacuumProps
}
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 {
_, err := d.Action(ctx, d.propOn.siid, 1, nil)
return err
}
func (d *vacuumDevice) Stop(ctx context.Context) error {
_, err := d.Action(ctx, d.propOn.siid, 0, nil)
return err
}
func (d *vacuumDevice) ReturnToDock(ctx context.Context) error {
_, err := d.Action(ctx, d.propOn.siid, 2, nil)
return err
}
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) (VacuumProps, error) {
// 云端实时查询
keys := []xiaomi.PropKey{}
add := func(sp siidPiid) {
if sp.valid {
keys = append(keys, xiaomi.PropKey{DID: d.info.DID, SIID: sp.siid, PIID: sp.piid})
}
}
add(d.propOn)
add(d.propMode)
add(d.propFanLevel)
add(d.propBattery)
props, err := d.client.GetProps(ctx, keys)
if err != nil {
return VacuumProps{}, err
}
status := VacuumProps{}
for _, p := range props {
switch {
case d.propOn.valid && p.SIID == d.propOn.siid && p.PIID == d.propOn.piid:
status.On, _ = p.Value.(bool)
case d.propMode.valid && p.SIID == d.propMode.siid && p.PIID == d.propMode.piid:
status.Mode = toInt(p.Value)
case d.propFanLevel.valid && p.SIID == d.propFanLevel.siid && p.PIID == d.propFanLevel.piid:
status.FanLevel = toInt(p.Value)
case d.propBattery.valid && p.SIID == d.propBattery.siid && p.PIID == d.propBattery.piid:
status.Battery = toInt(p.Value)
}
}
return status, nil
}
func (d *vacuumDevice) OnPropsChanged(handler func(props VacuumProps, changed string)) (string, error) {
type field struct {
siid, piid int
name string
fill func(*VacuumProps, interface{})
}
var fields []field
if d.propOn.valid {
fields = append(fields, field{d.propOn.siid, d.propOn.piid, "on", func(s *VacuumProps, v interface{}) { s.On, _ = v.(bool) }})
}
if d.propMode.valid {
fields = append(fields, field{d.propMode.siid, d.propMode.piid, "mode", func(s *VacuumProps, v interface{}) { s.Mode = toInt(v) }})
}
if d.propFanLevel.valid {
fields = append(fields, field{d.propFanLevel.siid, d.propFanLevel.piid, "fan-level", func(s *VacuumProps, v interface{}) { s.FanLevel = toInt(v) }})
}
if d.propBattery.valid {
fields = append(fields, field{d.propBattery.siid, d.propBattery.piid, "battery", func(s *VacuumProps, v interface{}) { s.Battery = toInt(v) }})
}
if len(fields) == 0 {
return "", xiaomi.ErrNotSupported
}
return d.SubProp(0, 0, func(did string, prop *xiaomi.PropertyValue) {
for _, f := range fields {
if f.siid == prop.SIID && f.piid == prop.PIID {
f.fill(&d.propsCache, prop.Value)
handler(d.propsCache, f.name)
return
}
}
})
}
func (d *vacuumDevice) GetProps() VacuumProps { return d.propsCache }
func (d *vacuumDevice) FetchProps(ctx context.Context) (VacuumProps, error) {
status, err := d.GetStatus(ctx)
if err != nil {
return d.propsCache, err
}
d.propsCache = status
return status, nil
}