feat: expose action input specifications

This commit is contained in:
2026-07-21 10:09:05 +08:00
parent 8fea0c3c11
commit 4ffd1b7eac
4 changed files with 64 additions and 10 deletions
+36 -9
View File
@@ -50,10 +50,11 @@ type ValueListItem struct {
}
type ActionSpec struct {
Name string `json:"name"`
Description string `json:"description"`
SIID int `json:"siid"`
AIID int `json:"aiid"`
Name string `json:"name"`
Description string `json:"description"`
SIID int `json:"siid"`
AIID int `json:"aiid"`
Inputs []PropertySpec `json:"inputs,omitempty"`
}
type DeviceSelector struct {
@@ -230,6 +231,7 @@ func parseDeviceInfoHTML(body []byte) (DeviceInfo, error) {
IID int `json:"iid"`
Type string `json:"type"`
Description string `json:"description"`
Inputs []int `json:"in"`
} `json:"actions"`
} `json:"services"`
} `json:"tree"`
@@ -245,6 +247,7 @@ func parseDeviceInfoHTML(body []byte) (DeviceInfo, error) {
propertyNames := make(map[string]struct{})
actionNames := make(map[string]struct{})
for _, service := range page.Props.Tree.Services {
serviceProperties := make(map[int]PropertySpec, len(service.Properties))
for _, property := range service.Properties {
propertyType := property.Format
if strings.HasPrefix(propertyType, "int") {
@@ -265,7 +268,9 @@ func parseDeviceInfoHTML(body []byte) (DeviceInfo, error) {
for index, item := range property.ValueList {
valueList[index] = ValueListItem{Value: item.Value, Description: item.Description, DescZhCN: page.Props.I18n.ZhCN[item.I18nKey]}
}
info.Properties = append(info.Properties, PropertySpec{Name: name, Description: description, Type: propertyType, RW: accessString(property.Access), Range: property.ValueRange, ValueList: valueList, SIID: service.IID, PIID: property.IID})
propertySpec := PropertySpec{Name: name, Description: description, Type: propertyType, RW: accessString(property.Access), Range: property.ValueRange, ValueList: valueList, SIID: service.IID, PIID: property.IID}
serviceProperties[property.IID] = propertySpec
info.Properties = append(info.Properties, propertySpec)
}
for _, action := range service.Actions {
name := action.Type
@@ -274,7 +279,15 @@ func parseDeviceInfoHTML(body []byte) (DeviceInfo, error) {
}
actionNames[name] = struct{}{}
description := localizedDescription(action.Description, page.Props.I18n.ZhCN[fmt.Sprintf("service:%03d:action:%03d", service.IID, action.IID)])
info.Actions = append(info.Actions, ActionSpec{Name: name, Description: description, SIID: service.IID, AIID: action.IID})
inputs := make([]PropertySpec, len(action.Inputs))
for index, propertyIID := range action.Inputs {
propertySpec, ok := serviceProperties[propertyIID]
if !ok {
return DeviceInfo{}, fmt.Errorf("action %q references unknown property IID %d", name, propertyIID)
}
inputs[index] = clonePropertySpec(propertySpec)
}
info.Actions = append(info.Actions, ActionSpec{Name: name, Description: description, SIID: service.IID, AIID: action.IID, Inputs: inputs})
}
}
return info, nil
@@ -553,6 +566,11 @@ func validateDeviceInfo(info DeviceInfo, model string) error {
if strings.TrimSpace(action.Name) == "" || action.SIID <= 0 || action.AIID <= 0 {
return fmt.Errorf("action %d is invalid", index)
}
for inputIndex, input := range action.Inputs {
if !validPropertyType(input.Type) || input.SIID <= 0 || input.PIID <= 0 || input.SIID != action.SIID {
return fmt.Errorf("action %d input %d is invalid", index, inputIndex)
}
}
}
return nil
}
@@ -620,9 +638,7 @@ func NewDevice(ctx context.Context, client *Client, selector DeviceSelector, opt
func (device *Device) Properties() map[string]PropertySpec {
properties := make(map[string]PropertySpec, len(device.properties))
for name, property := range device.properties {
property.Range = append([]json.Number(nil), property.Range...)
property.ValueList = append([]ValueListItem(nil), property.ValueList...)
properties[name] = property
properties[name] = clonePropertySpec(property)
}
return properties
}
@@ -630,11 +646,22 @@ func (device *Device) Properties() map[string]PropertySpec {
func (device *Device) Actions() map[string]ActionSpec {
actions := make(map[string]ActionSpec, len(device.actions))
for name, action := range device.actions {
inputs := make([]PropertySpec, len(action.Inputs))
for index, input := range action.Inputs {
inputs[index] = clonePropertySpec(input)
}
action.Inputs = inputs
actions[name] = action
}
return actions
}
func clonePropertySpec(property PropertySpec) PropertySpec {
property.Range = append([]json.Number(nil), property.Range...)
property.ValueList = append([]ValueListItem(nil), property.ValueList...)
return property
}
func (device *Device) Get(ctx context.Context, name string) (any, error) {
property, ok := device.properties[name]
if !ok {