package devices import ( "context" "xiaomihome/xiaomi" ) // Thermostat 温控器统一控制接口 type Thermostat interface { Device TurnOn(ctx context.Context) error TurnOff(ctx context.Context) error IsOn(ctx context.Context) (bool, error) SetTargetTemp(ctx context.Context, temp float64) error GetTargetTemp(ctx context.Context) (float64, error) GetCurrentTemp(ctx context.Context) (float64, error) SetHeating(ctx context.Context, on bool) error IsHeating(ctx context.Context) (bool, error) OnStateChanged(handler func(state HeaterState, changed string)) (string, error) } type thermostatDevice struct { BaseDevice propOn siidPiid propTargetTemp siidPiid propCurrentTemp siidPiid propHeating siidPiid stateCache HeaterState } func NewThermostat(client *xiaomi.Client, info *xiaomi.DeviceInfo) (Thermostat, error) { d := &thermostatDevice{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("target-temperature"); ok { d.propTargetTemp = siidPiid{s, p, true} } if s, p, ok := resolver.find("temperature"); ok { d.propCurrentTemp = siidPiid{s, p, true} } if s, p, ok := resolver.find("heater"); ok { d.propHeating = siidPiid{s, p, true} } return d, nil } func (d *thermostatDevice) TurnOn(ctx context.Context) error { if !d.propOn.valid { return xiaomi.ErrNotSupported } return d.SetProp(ctx, d.propOn.siid, d.propOn.piid, true) } func (d *thermostatDevice) TurnOff(ctx context.Context) error { if !d.propOn.valid { return xiaomi.ErrNotSupported } return d.SetProp(ctx, d.propOn.siid, d.propOn.piid, false) } func (d *thermostatDevice) IsOn(ctx context.Context) (bool, error) { if !d.propOn.valid { return false, xiaomi.ErrNotSupported } prop, err := d.GetProp(ctx, d.propOn.siid, d.propOn.piid) if err != nil { return false, err } if b, ok := prop.Value.(bool); ok { return b, nil } return false, nil } func (d *thermostatDevice) SetTargetTemp(ctx context.Context, temp float64) error { if !d.propTargetTemp.valid { return xiaomi.ErrNotSupported } return d.SetProp(ctx, d.propTargetTemp.siid, d.propTargetTemp.piid, temp) } func (d *thermostatDevice) GetTargetTemp(ctx context.Context) (float64, error) { if !d.propTargetTemp.valid { return 0, xiaomi.ErrNotSupported } prop, err := d.GetProp(ctx, d.propTargetTemp.siid, d.propTargetTemp.piid) if err != nil { return 0, err } return toFloat(prop.Value), nil } func (d *thermostatDevice) GetCurrentTemp(ctx context.Context) (float64, error) { if !d.propCurrentTemp.valid { return 0, xiaomi.ErrNotSupported } prop, err := d.GetProp(ctx, d.propCurrentTemp.siid, d.propCurrentTemp.piid) if err != nil { return 0, err } return toFloat(prop.Value), nil } func (d *thermostatDevice) SetHeating(ctx context.Context, on bool) error { if !d.propHeating.valid { return xiaomi.ErrNotSupported } return d.SetProp(ctx, d.propHeating.siid, d.propHeating.piid, on) } func (d *thermostatDevice) IsHeating(ctx context.Context) (bool, error) { if !d.propHeating.valid { return false, xiaomi.ErrNotSupported } prop, err := d.GetProp(ctx, d.propHeating.siid, d.propHeating.piid) if err != nil { return false, err } if b, ok := prop.Value.(bool); ok { return b, nil } return false, nil } func (d *thermostatDevice) OnStateChanged(handler func(state HeaterState, changed string)) (string, error) { type field struct{ siid, piid int; name string; fill func(*HeaterState, interface{}) } var fields []field if d.propOn.valid { fields = append(fields, field{d.propOn.siid, d.propOn.piid, "on", func(s *HeaterState, v interface{}) { s.On, _ = v.(bool) }}) } if d.propTargetTemp.valid { fields = append(fields, field{d.propTargetTemp.siid, d.propTargetTemp.piid, "target-temperature", func(s *HeaterState, v interface{}) { s.TargetTemp = toFloat(v) }}) } if d.propCurrentTemp.valid { fields = append(fields, field{d.propCurrentTemp.siid, d.propCurrentTemp.piid, "temperature", func(s *HeaterState, v interface{}) { s.CurrentTemp = toFloat(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.stateCache, prop.Value) handler(d.stateCache, f.name) return } } }) }