package devices import ( "context" "xiaomihome/xiaomi" ) // CoverProps 窗帘完整状态。 type CoverProps struct { Position int `json:"position"` // 当前位置 0-100 Status int `json:"status"` // 运动状态:0=停止 1=开启中 2=关闭中 Fault int `json:"fault"` // 故障码,0=无故障 } // Cover 窗帘/晾衣架统一控制接口 type Cover interface { Device Open(ctx context.Context) error Close(ctx context.Context) error Stop(ctx context.Context) error SetPosition(ctx context.Context, pct int) error GetPosition(ctx context.Context) (int, error) OnPropsChanged(handler func(props CoverProps)) (string, error) GetProps() CoverProps FetchProps(ctx context.Context) (CoverProps, error) } type coverDevice struct { BaseDevice propMotor siidPiid // motor-control(write) propPosition siidPiid // current-position(read) propTarget siidPiid // target-position(write,SetPosition 用) propStatus siidPiid // status(read) propFault siidPiid // fault(read) propsCache CoverProps } const ( coverMotorOpen = 1 coverMotorClose = 0 coverMotorStop = 2 ) func NewCover(client *xiaomi.Client, info *xiaomi.DeviceInfo) (Cover, error) { d := &coverDevice{BaseDevice: NewBaseDevice(client, info)} resolver := d.getSpecResolver() if resolver == nil { return nil, xiaomi.ErrNoSPEC } siid, piid, ok := resolver.find("motor-control") if !ok { return nil, xiaomi.ErrNotSupported } d.propMotor = siidPiid{siid, piid, true} if s, p, ok := resolver.find("current-position"); ok { d.propPosition = siidPiid{s, p, true} } if s, p, ok := resolver.find("target-position"); ok { d.propTarget = siidPiid{s, p, true} } if s, p, ok := resolver.find("status"); ok { d.propStatus = siidPiid{s, p, true} } if s, p, ok := resolver.find("fault"); ok { d.propFault = siidPiid{s, p, true} } return d, nil } func (d *coverDevice) Open(ctx context.Context) error { if !d.propMotor.valid { return xiaomi.ErrNotSupported } return d.SetProp(ctx, d.propMotor.siid, d.propMotor.piid, coverMotorOpen) } func (d *coverDevice) Close(ctx context.Context) error { if !d.propMotor.valid { return xiaomi.ErrNotSupported } return d.SetProp(ctx, d.propMotor.siid, d.propMotor.piid, coverMotorClose) } func (d *coverDevice) Stop(ctx context.Context) error { if !d.propMotor.valid { return xiaomi.ErrNotSupported } return d.SetProp(ctx, d.propMotor.siid, d.propMotor.piid, coverMotorStop) } // SetPosition 设定目标位置,写 target-position(可写属性)。 // 老设备无 target-position 时回退写 current-position。 func (d *coverDevice) SetPosition(ctx context.Context, pct int) error { if d.propTarget.valid { return d.SetProp(ctx, d.propTarget.siid, d.propTarget.piid, pct) } if d.propPosition.valid { return d.SetProp(ctx, d.propPosition.siid, d.propPosition.piid, pct) } return xiaomi.ErrNotSupported } func (d *coverDevice) GetPosition(ctx context.Context) (int, error) { if !d.propPosition.valid { return 0, xiaomi.ErrNotSupported } prop, err := d.GetProp(ctx, d.propPosition.siid, d.propPosition.piid) if err != nil { return 0, err } return toInt(prop.Value), nil } func (d *coverDevice) OnPropsChanged(handler func(props CoverProps)) (string, error) { if !d.propPosition.valid && !d.propStatus.valid && !d.propFault.valid { return "", xiaomi.ErrNotSupported } return d.SubProp(0, 0, func(did string, prop *xiaomi.PropertyValue) { switch { case d.propPosition.valid && prop.SIID == d.propPosition.siid && prop.PIID == d.propPosition.piid: d.propsCache.Position = toInt(prop.Value) case d.propStatus.valid && prop.SIID == d.propStatus.siid && prop.PIID == d.propStatus.piid: d.propsCache.Status = toInt(prop.Value) case d.propFault.valid && prop.SIID == d.propFault.siid && prop.PIID == d.propFault.piid: d.propsCache.Fault = toInt(prop.Value) default: return // 其他属性不触发回调 } handler(d.propsCache) }) } func (d *coverDevice) GetProps() CoverProps { return d.propsCache } func (d *coverDevice) FetchProps(ctx context.Context) (CoverProps, error) { keys := []xiaomi.PropKey{} if d.propPosition.valid { keys = append(keys, xiaomi.PropKey{DID: d.info.DID, SIID: d.propPosition.siid, PIID: d.propPosition.piid}) } if d.propStatus.valid { keys = append(keys, xiaomi.PropKey{DID: d.info.DID, SIID: d.propStatus.siid, PIID: d.propStatus.piid}) } if d.propFault.valid { keys = append(keys, xiaomi.PropKey{DID: d.info.DID, SIID: d.propFault.siid, PIID: d.propFault.piid}) } if len(keys) == 0 { return d.propsCache, xiaomi.ErrNotSupported } props, err := d.client.GetProps(ctx, keys) if err != nil { return d.propsCache, err } for _, p := range props { switch { case d.propPosition.valid && p.SIID == d.propPosition.siid && p.PIID == d.propPosition.piid: d.propsCache.Position = toInt(p.Value) case d.propStatus.valid && p.SIID == d.propStatus.siid && p.PIID == d.propStatus.piid: d.propsCache.Status = toInt(p.Value) case d.propFault.valid && p.SIID == d.propFault.siid && p.PIID == d.propFault.piid: d.propsCache.Fault = toInt(p.Value) } } return d.propsCache, nil }