You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

251 lines
6.2 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. package models
  2. import (
  3. "context"
  4. "math"
  5. "strings"
  6. "time"
  7. )
  8. type Device struct {
  9. ID int `json:"id"`
  10. BridgeID int `json:"bridgeID"`
  11. InternalID string `json:"internalId"`
  12. Icon string `json:"icon"`
  13. Name string `json:"name"`
  14. Capabilities []DeviceCapability `json:"capabilities"`
  15. ButtonNames []string `json:"buttonNames"`
  16. DriverProperties map[string]interface{} `json:"driverProperties"`
  17. UserProperties map[string]string `json:"userProperties"`
  18. SceneAssignments []DeviceSceneAssignment `json:"sceneAssignments"`
  19. State DeviceState `json:"state"`
  20. Tags []string `json:"tags"`
  21. }
  22. type DeviceUpdate struct {
  23. Icon *string `json:"icon"`
  24. Name *string `json:"name"`
  25. UserProperties map[string]*string `json:"userProperties"`
  26. }
  27. // DeviceState contains optional state values that
  28. // - Power: Whether the device is powered on
  29. // - Color: Color value, if a color setting can be set on the device
  30. // - Intensity: e.g. brightness, range 0..=1
  31. // - Temperature: e.g. for thermostats
  32. type DeviceState struct {
  33. Power bool `json:"power"`
  34. Color ColorValue `json:"color,omitempty"`
  35. Intensity float64 `json:"intensity,omitempty"`
  36. Temperature float64 `json:"temperature"`
  37. }
  38. type DeviceScene struct {
  39. SceneID int `json:"sceneId"`
  40. Time time.Time `json:"time"`
  41. DurationMS int64 `json:"duration"`
  42. Tag string `json:"tag"`
  43. }
  44. type NewDeviceState struct {
  45. Power *bool `json:"power"`
  46. Color *string `json:"color"`
  47. Intensity *float64 `json:"intensity"`
  48. Temperature *int `json:"temperature"`
  49. }
  50. type DeviceCapability string
  51. type SaveMode int
  52. const (
  53. SMState SaveMode = 1
  54. SMProperties SaveMode = 2
  55. SMTags SaveMode = 4
  56. )
  57. type DeviceRepository interface {
  58. Find(ctx context.Context, id int) (*Device, error)
  59. FetchByReference(ctx context.Context, kind ReferenceKind, value string) ([]Device, error)
  60. Save(ctx context.Context, device *Device, mode SaveMode) error
  61. SaveMany(ctx context.Context, mode SaveMode, devices []Device) error
  62. Delete(ctx context.Context, device *Device) error
  63. }
  64. func DeviceCapabilitiesToStrings(caps []DeviceCapability) []string {
  65. res := make([]string, 0, len(caps))
  66. for _, cap := range caps {
  67. res = append(res, string(cap))
  68. }
  69. return res
  70. }
  71. var (
  72. DCPower DeviceCapability = "Power"
  73. DCColorHS DeviceCapability = "ColorHS"
  74. DCColorHSK DeviceCapability = "ColorHSK"
  75. DCColorKelvin DeviceCapability = "ColorKelvin"
  76. DCButtons DeviceCapability = "Buttons"
  77. DCPresence DeviceCapability = "Presence"
  78. DCIntensity DeviceCapability = "Intensity"
  79. DCTemperatureControl DeviceCapability = "TemperatureControl"
  80. DCTemperatureSensor DeviceCapability = "TemperatureSensor"
  81. )
  82. var Capabilities = []DeviceCapability{
  83. DCPower,
  84. DCColorHS,
  85. DCColorKelvin,
  86. DCButtons,
  87. DCPresence,
  88. DCIntensity,
  89. DCTemperatureControl,
  90. DCTemperatureSensor,
  91. }
  92. func (d *Device) ApplyUpdate(update DeviceUpdate) {
  93. if update.Name != nil {
  94. d.Name = *update.Name
  95. }
  96. if update.Icon != nil {
  97. d.Icon = *update.Icon
  98. }
  99. if d.UserProperties == nil {
  100. d.UserProperties = make(map[string]string)
  101. }
  102. for key, value := range update.UserProperties {
  103. if value != nil {
  104. d.UserProperties[key] = *value
  105. } else {
  106. delete(d.UserProperties, key)
  107. }
  108. }
  109. }
  110. func (d *Device) Validate() error {
  111. d.Name = strings.Trim(d.Name, " \t\n ")
  112. if d.Name == "" {
  113. return ErrInvalidName
  114. }
  115. newCaps := make([]DeviceCapability, 0, len(d.Capabilities))
  116. for _, currCap := range d.Capabilities {
  117. for _, validCap := range Capabilities {
  118. if currCap == validCap {
  119. newCaps = append(newCaps, currCap)
  120. break
  121. }
  122. }
  123. }
  124. d.Capabilities = newCaps
  125. return nil
  126. }
  127. func (d *Device) HasTag(tags ...string) bool {
  128. for _, c := range d.Tags {
  129. for _, c2 := range tags {
  130. if c == c2 {
  131. return true
  132. }
  133. }
  134. }
  135. return false
  136. }
  137. func (d *Device) HasCapability(capabilities ...DeviceCapability) bool {
  138. for _, c := range d.Capabilities {
  139. for _, c2 := range capabilities {
  140. if c == c2 {
  141. return true
  142. }
  143. }
  144. }
  145. return false
  146. }
  147. func (d *Device) SetState(newState NewDeviceState) error {
  148. if newState.Power != nil && d.HasCapability(DCPower) {
  149. d.State.Power = *newState.Power
  150. }
  151. if newState.Color != nil {
  152. parsed, err := ParseColorValue(*newState.Color)
  153. if err != nil {
  154. return err
  155. }
  156. if (parsed.IsKelvin() && d.HasCapability(DCColorKelvin, DCColorHSK)) || (parsed.IsHueSat() && d.HasCapability(DCColorHS)) {
  157. d.State.Color = parsed
  158. }
  159. }
  160. if newState.Intensity != nil && d.HasCapability(DCIntensity) {
  161. d.State.Intensity = *newState.Intensity
  162. }
  163. return nil
  164. }
  165. func (s *NewDeviceState) RelativeTo(device Device) NewDeviceState {
  166. n := NewDeviceState{}
  167. if s.Intensity != nil {
  168. intensity := device.State.Intensity * *s.Intensity
  169. n.Intensity = &intensity
  170. }
  171. if s.Color != nil {
  172. c, err := ParseColorValue(*s.Color)
  173. if err == nil {
  174. c.Hue = math.Mod(device.State.Color.Hue + c.Hue, 360)
  175. c.Saturation *= device.State.Color.Saturation
  176. c.Kelvin += device.State.Color.Kelvin
  177. }
  178. }
  179. return n
  180. }
  181. func (s *NewDeviceState) Interpolate(other NewDeviceState, fac float64) NewDeviceState {
  182. n := NewDeviceState{}
  183. if s.Power != nil && other.Power != nil {
  184. if fac >= 0.5 {
  185. n.Power = other.Power
  186. } else {
  187. n.Power = s.Power
  188. }
  189. }
  190. if s.Color != nil && other.Color != nil {
  191. sc, err := ParseColorValue(*s.Color)
  192. oc, err2 := ParseColorValue(*other.Color)
  193. if err == nil && err2 == nil {
  194. rc := ColorValue{}
  195. rc.Hue = interpolateFloat(sc.Hue, oc.Hue, fac)
  196. rc.Saturation = interpolateFloat(sc.Saturation, oc.Saturation, fac)
  197. rc.Kelvin = interpolateInt(sc.Kelvin, oc.Kelvin, fac)
  198. rcStr := rc.String()
  199. n.Color = &rcStr
  200. }
  201. }
  202. if s.Intensity != nil && other.Intensity != nil {
  203. n.Intensity = new(float64)
  204. *n.Intensity = interpolateFloat(*s.Intensity, *other.Intensity, fac)
  205. }
  206. return n
  207. }
  208. func interpolateFloat(a, b, fac float64) float64 {
  209. return (a * (1 - fac)) + (b * fac)
  210. }
  211. func interpolateInt(a, b int, fac float64) int {
  212. return int((float64(a) * (1 - fac)) + (float64(b) * fac))
  213. }