feat: batch device property reads

This commit is contained in:
2026-07-22 09:45:16 +08:00
parent 3f05417b83
commit 013056d715
3 changed files with 211 additions and 0 deletions
+71
View File
@@ -22,6 +22,7 @@ const (
deviceSpecUA = "mijiaAPI/4.1.2"
deviceSpecMaxSize = 8 << 20
deviceCacheVersion = 2
deviceGetBatchSize = 20
)
var deviceSpecURL = "https://home.miot-spec.com/spec/"
@@ -705,6 +706,76 @@ func (device *Device) Get(ctx context.Context, name string) (any, error) {
return results[0].Value, nil
}
func (device *Device) GetMany(ctx context.Context, names []string) ([]DevicePropertyResult, error) {
if len(names) == 0 {
return []DevicePropertyResult{}, nil
}
type propertyIdentity struct {
did string
siid int
piid int
}
requests := make([]PropertyRequest, len(names))
seenNames := make(map[string]struct{}, len(names))
seenIdentities := make(map[propertyIdentity]struct{}, len(names))
for index, name := range names {
if _, duplicate := seenNames[name]; duplicate {
return nil, fmt.Errorf("重复的属性: %s", name)
}
seenNames[name] = struct{}{}
property, ok := device.properties[name]
if !ok {
return nil, fmt.Errorf("不支持的属性: %s", name)
}
if !strings.Contains(property.RW, "r") {
return nil, fmt.Errorf("属性 %s 不可读取", name)
}
identity := propertyIdentity{did: device.DID, siid: property.SIID, piid: property.PIID}
if _, duplicate := seenIdentities[identity]; duplicate {
return nil, fmt.Errorf("属性 %s 与其他请求使用重复的设备属性 identity", name)
}
seenIdentities[identity] = struct{}{}
requests[index] = PropertyRequest{DID: identity.did, SIID: identity.siid, PIID: identity.piid}
}
results := make([]DevicePropertyResult, len(names))
for start := 0; start < len(requests); start += deviceGetBatchSize {
end := min(start+deviceGetBatchSize, len(requests))
chunkResults, err := device.client.GetProperties(ctx, requests[start:end])
if err != nil {
return nil, err
}
byIdentity := make(map[propertyIdentity]PropertyResult, len(chunkResults))
requested := make(map[propertyIdentity]struct{}, end-start)
for _, request := range requests[start:end] {
requested[propertyIdentity{did: request.DID, siid: request.SIID, piid: request.PIID}] = struct{}{}
}
for _, result := range chunkResults {
identity := propertyIdentity{did: result.DID, siid: result.SIID, piid: result.PIID}
if _, expected := requested[identity]; !expected {
return nil, fmt.Errorf("get properties protocol error: unexpected identity (%s,%d,%d)", result.DID, result.SIID, result.PIID)
}
if _, duplicate := byIdentity[identity]; duplicate {
return nil, fmt.Errorf("get properties protocol error: duplicate identity (%s,%d,%d)", result.DID, result.SIID, result.PIID)
}
byIdentity[identity] = result
}
for index, request := range requests[start:end] {
identity := propertyIdentity{did: request.DID, siid: request.SIID, piid: request.PIID}
result, ok := byIdentity[identity]
if !ok {
return nil, fmt.Errorf("get properties protocol error: missing identity (%s,%d,%d)", request.DID, request.SIID, request.PIID)
}
results[start+index] = DevicePropertyResult{Name: names[start+index], Value: result.Value, Code: result.Code}
}
}
if err := device.wait(ctx); err != nil {
return nil, err
}
return results, nil
}
func (device *Device) Set(ctx context.Context, name string, value any) error {
property, ok := device.properties[name]
if !ok {