package devices import ( "context" "fmt" "xiaomihome/xiaomi" ) // TempHumiditySensor 温湿度传感器读取接口(只读)。 type TempHumiditySensor interface { Device GetTemperature(ctx context.Context) (float64, error) GetHumidity(ctx context.Context) (float64, error) OnPropsChanged(handler func(temp, humidity float64)) (string, error) GetProps() (float64, float64) FetchProps(ctx context.Context) (float64, float64, error) } type tempHumidityDevice struct { BaseDevice propTemp siidPiid propHumidity siidPiid propsCache [2]float64 // [0]=temperature, [1]=humidity } func NewTempHumiditySensor(client *xiaomi.Client, info *xiaomi.DeviceInfo) (TempHumiditySensor, error) { if info == nil { return nil, xiaomi.ErrDeviceNotFound } miotDev := client.GetMIoTDevice(info.DID) if miotDev == nil { return nil, fmt.Errorf("%w: device not initialized", xiaomi.ErrDeviceNotFound) } spec := miotDev.SpecInstance() if spec == nil { return nil, fmt.Errorf("%w: device has no SPEC information", xiaomi.ErrNotSupported) } d := &tempHumidityDevice{BaseDevice: BaseDevice{client: client, info: info}} for _, svc := range spec.Services { for _, prop := range svc.Properties { switch propertyShortName(prop.Type) { case "temperature": if !d.propTemp.valid { d.propTemp = siidPiid{svc.IID, prop.PIID, true} } case "relative-humidity": if !d.propHumidity.valid { d.propHumidity = siidPiid{svc.IID, prop.PIID, true} } } } } if !d.propTemp.valid && !d.propHumidity.valid { return nil, fmt.Errorf("%w: no temperature or humidity property in SPEC", xiaomi.ErrNotSupported) } return d, nil } func (d *tempHumidityDevice) GetTemperature(ctx context.Context) (float64, error) { if !d.propTemp.valid { return 0, xiaomi.ErrNotSupported } prop, err := d.GetProp(ctx, d.propTemp.siid, d.propTemp.piid) if err != nil { return 0, err } return toFloat(prop.Value), nil } func (d *tempHumidityDevice) GetHumidity(ctx context.Context) (float64, error) { if !d.propHumidity.valid { return 0, xiaomi.ErrNotSupported } prop, err := d.GetProp(ctx, d.propHumidity.siid, d.propHumidity.piid) if err != nil { return 0, err } return toFloat(prop.Value), nil } func (d *tempHumidityDevice) OnPropsChanged(handler func(temp, humidity float64)) (string, error) { if !d.propTemp.valid && !d.propHumidity.valid { return "", xiaomi.ErrNotSupported } return d.SubProp(0, 0, func(did string, prop *xiaomi.PropertyValue) { switch { case d.propTemp.valid && prop.SIID == d.propTemp.siid && prop.PIID == d.propTemp.piid: d.propsCache[0] = toFloat(prop.Value) case d.propHumidity.valid && prop.SIID == d.propHumidity.siid && prop.PIID == d.propHumidity.piid: d.propsCache[1] = toFloat(prop.Value) } handler(d.propsCache[0], d.propsCache[1]) }) } func (d *tempHumidityDevice) GetProps() (float64, float64) { return d.propsCache[0], d.propsCache[1] } func (d *tempHumidityDevice) FetchProps(ctx context.Context) (float64, float64, error) { keys := []xiaomi.PropKey{} if d.propTemp.valid { keys = append(keys, xiaomi.PropKey{DID: d.info.DID, SIID: d.propTemp.siid, PIID: d.propTemp.piid}) } if d.propHumidity.valid { keys = append(keys, xiaomi.PropKey{DID: d.info.DID, SIID: d.propHumidity.siid, PIID: d.propHumidity.piid}) } props, err := d.client.GetProps(ctx, keys) if err != nil { return d.propsCache[0], d.propsCache[1], err } state := [2]float64{d.propsCache[0], d.propsCache[1]} for _, p := range props { switch { case d.propTemp.valid && p.SIID == d.propTemp.siid && p.PIID == d.propTemp.piid: state[0] = toFloat(p.Value) case d.propHumidity.valid && p.SIID == d.propHumidity.siid && p.PIID == d.propHumidity.piid: state[1] = toFloat(p.Value) } } d.propsCache = state return state[0], state[1], nil }