package models import ( "context" "strings" ) type Device struct { ID int `json:"id"` BridgeID int `json:"bridgeID"` InternalID string `json:"internalId"` Icon string `json:"icon"` Name string `json:"name"` Capabilities []DeviceCapability `json:"capabilities"` ButtonNames []string `json:"buttonNames"` DriverProperties map[string]interface{} `json:"driverProperties"` UserProperties map[string]string `json:"userProperties"` State DeviceState `json:"state"` Tags []string `json:"tags"` } type DeviceUpdate struct { Icon *string `json:"icon"` Name *string `json:"name"` UserProperties map[string]*string `json:"userProperties"` } // DeviceState contains optional state values that // - Power: Whether the device is powered on // - Color: Color value, if a color setting can be set on the device // - Intensity: e.g. brightness, range 0..=1 // - Temperature: e.g. for thermostats type DeviceState struct { Power bool `json:"power"` Color ColorValue `json:"color,omitempty"` Intensity float64 `json:"intensity,omitempty"` Temperature float64 `json:"temperature"` } type NewDeviceState struct { Power *bool `json:"power"` Color *string `json:"color"` Intensity *float64 `json:"intensity"` Temperature *int `json:"temperature"` } type DeviceCapability string type SaveMode int const ( SMState SaveMode = 1 SMProperties SaveMode = 2 SMTags SaveMode = 4 ) type DeviceRepository interface { Find(ctx context.Context, id int) (*Device, error) FetchByReference(ctx context.Context, kind ReferenceKind, value string) ([]Device, error) Save(ctx context.Context, device *Device, mode SaveMode) error Delete(ctx context.Context, device *Device) error } func DeviceCapabilitiesToStrings(caps []DeviceCapability) []string { res := make([]string, 0, len(caps)) for _, cap := range caps { res = append(res, string(cap)) } return res } var ( DCPower DeviceCapability = "Power" DCColorHS DeviceCapability = "ColorHS" DCColorHSK DeviceCapability = "ColorHSK" DCColorKelvin DeviceCapability = "ColorKelvin" DCButtons DeviceCapability = "Buttons" DCPresence DeviceCapability = "Presence" DCIntensity DeviceCapability = "Intensity" DCTemperatureControl DeviceCapability = "TemperatureControl" DCTemperatureSensor DeviceCapability = "TemperatureSensor" ) var Capabilities = []DeviceCapability{ DCPower, DCColorHS, DCColorKelvin, DCButtons, DCPresence, DCIntensity, DCTemperatureControl, DCTemperatureSensor, } func (d *Device) ApplyUpdate(update DeviceUpdate) { if update.Name != nil { d.Name = *update.Name } if update.Icon != nil { d.Icon = *update.Icon } if d.UserProperties == nil { d.UserProperties = make(map[string]string) } for key, value := range update.UserProperties { if value != nil { d.UserProperties[key] = *value } else { delete(d.UserProperties, key) } } } func (d *Device) Validate() error { d.Name = strings.Trim(d.Name, " \t\n ") if d.Name == "" { return ErrInvalidName } newCaps := make([]DeviceCapability, 0, len(d.Capabilities)) for _, currCap := range d.Capabilities { for _, validCap := range Capabilities { if currCap == validCap { newCaps = append(newCaps, currCap) break } } } d.Capabilities = newCaps return nil } func (d *Device) HasTag(tags ...string) bool { for _, c := range d.Tags { for _, c2 := range tags { if c == c2 { return true } } } return false } func (d *Device) HasCapability(capabilities ...DeviceCapability) bool { for _, c := range d.Capabilities { for _, c2 := range capabilities { if c == c2 { return true } } } return false } func (d *Device) SetState(newState NewDeviceState) error { if newState.Power != nil && d.HasCapability(DCPower) { d.State.Power = *newState.Power } if newState.Color != nil { parsed, err := ParseColorValue(*newState.Color) if err != nil { return err } if (parsed.IsKelvin() && d.HasCapability(DCColorKelvin, DCColorHSK)) || (parsed.IsHueSat() && d.HasCapability(DCColorHS)) { d.State.Color = parsed } } if newState.Intensity != nil && d.HasCapability(DCIntensity) { d.State.Intensity = *newState.Intensity } return nil }