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.

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