config.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. package presence
  2. import (
  3. "context"
  4. "fmt"
  5. "net"
  6. "net/url"
  7. "os"
  8. "regexp"
  9. "strings"
  10. "time"
  11. "goa.design/clue/log"
  12. "gopkg.in/yaml.v3"
  13. "douglasthrift.net/presence/wrap"
  14. )
  15. type (
  16. Config struct {
  17. Interval time.Duration `yaml:"interval"`
  18. Interfaces []string `yaml:"interfaces"`
  19. MACAddresses []string `yaml:"mac_addresses"`
  20. PingCount uint `yaml:"ping_count"`
  21. IFTTT IFTTT `yaml:"ifttt"`
  22. }
  23. IFTTT struct {
  24. BaseURL string `yaml:"base_url"`
  25. Key string `yaml:"key"`
  26. Events Events `yaml:"events"`
  27. }
  28. Events struct {
  29. Present Event `yaml:"present"`
  30. Absent Event `yaml:"absent"`
  31. }
  32. Event struct {
  33. Event string `yaml:"event"`
  34. Value1 string `yaml:"value1"`
  35. Value2 string `yaml:"value2"`
  36. Value3 string `yaml:"value3"`
  37. }
  38. )
  39. const (
  40. defaultBaseURL = "https://maker.ifttt.com"
  41. defaultPresentEvent = "presence_detected"
  42. defaultAbsentEvent = "absence_detected"
  43. )
  44. var (
  45. eventName = regexp.MustCompile("^[_a-zA-Z]+$")
  46. )
  47. func ParseConfig(name string, wNet wrap.Net) (*Config, error) {
  48. return ParseConfigWithContext(context.Background(), name, wNet)
  49. }
  50. func ParseConfigWithContext(ctx context.Context, name string, wNet wrap.Net) (*Config, error) {
  51. f, err := os.Open(name)
  52. if err != nil {
  53. return nil, err
  54. }
  55. defer func() { _ = f.Close() }()
  56. d := yaml.NewDecoder(f)
  57. d.KnownFields(true)
  58. c := &Config{}
  59. err = d.Decode(c)
  60. if err != nil {
  61. return nil, err
  62. }
  63. if c.Interval < 0 {
  64. return nil, fmt.Errorf("negative interval (%v)", c.Interval)
  65. } else if c.Interval == 0 {
  66. c.Interval = 30 * time.Second
  67. }
  68. log.Print(ctx, log.KV{K: "msg", V: "interval"}, log.KV{K: "value", V: c.Interval})
  69. if len(c.Interfaces) == 0 {
  70. ifs, err := wNet.Interfaces()
  71. if err != nil {
  72. return nil, err
  73. }
  74. c.Interfaces = make([]string, 0, len(ifs))
  75. for _, i := range ifs {
  76. c.Interfaces = append(c.Interfaces, i.Name)
  77. }
  78. } else {
  79. for _, i := range c.Interfaces {
  80. _, err = wNet.InterfaceByName(i)
  81. if err != nil {
  82. return nil, fmt.Errorf("interface %v: %w", i, err)
  83. }
  84. }
  85. }
  86. log.Print(ctx, log.KV{K: "msg", V: "interfaces"}, log.KV{K: "value", V: c.Interfaces})
  87. if len(c.MACAddresses) == 0 {
  88. return nil, fmt.Errorf("no MAC addresses")
  89. }
  90. as := make(map[string]bool, len(c.MACAddresses))
  91. for i, a := range c.MACAddresses {
  92. hw, err := net.ParseMAC(a)
  93. if err != nil {
  94. return nil, err
  95. }
  96. a = hw.String()
  97. if as[a] {
  98. return nil, fmt.Errorf("duplicate MAC address (%v)", a)
  99. }
  100. as[a] = true
  101. c.MACAddresses[i] = a
  102. }
  103. log.Print(ctx, log.KV{K: "msg", V: "MAC addresses"}, log.KV{K: "value", V: c.MACAddresses})
  104. if c.PingCount == 0 {
  105. c.PingCount = 1
  106. }
  107. log.Print(ctx, log.KV{K: "msg", V: "ping count"}, log.KV{K: "value", V: c.PingCount})
  108. if c.IFTTT.BaseURL == "" {
  109. c.IFTTT.BaseURL = defaultBaseURL
  110. } else if _, err := url.Parse(c.IFTTT.BaseURL); err != nil {
  111. return nil, fmt.Errorf("IFTTT base URL: %w", err)
  112. }
  113. log.Print(ctx, log.KV{K: "msg", V: "IFTTT base URL"}, log.KV{K: "value", V: c.IFTTT.BaseURL})
  114. if c.IFTTT.Key == "" {
  115. return nil, fmt.Errorf("no IFTTT key")
  116. }
  117. log.Print(ctx, log.KV{K: "msg", V: "IFTTT key"}, log.KV{K: "value", V: strings.Repeat("*", len(c.IFTTT.Key))})
  118. if c.IFTTT.Events.Present.Event == "" {
  119. c.IFTTT.Events.Present.Event = defaultPresentEvent
  120. } else if !eventName.MatchString(c.IFTTT.Events.Present.Event) {
  121. return nil, fmt.Errorf("invalid IFTTT present event name: %#v", c.IFTTT.Events.Present.Event)
  122. }
  123. log.Print(ctx, log.KV{K: "msg", V: "IFTTT present event"}, log.KV{K: "value", V: c.IFTTT.Events.Present},
  124. log.KV{K: "value1", V: c.IFTTT.Events.Present.Value1},
  125. log.KV{K: "value2", V: c.IFTTT.Events.Present.Value2},
  126. log.KV{K: "value3", V: c.IFTTT.Events.Present.Value3})
  127. if c.IFTTT.Events.Absent.Event == "" {
  128. c.IFTTT.Events.Absent.Event = defaultAbsentEvent
  129. } else if !eventName.MatchString(c.IFTTT.Events.Absent.Event) {
  130. return nil, fmt.Errorf("invalid IFTTT absent event name: %#v", c.IFTTT.Events.Absent.Event)
  131. }
  132. log.Print(ctx, log.KV{K: "msg", V: "IFTTT absent event"}, log.KV{K: "value", V: c.IFTTT.Events.Absent},
  133. log.KV{K: "value1", V: c.IFTTT.Events.Absent.Value1},
  134. log.KV{K: "value2", V: c.IFTTT.Events.Absent.Value2},
  135. log.KV{K: "value3", V: c.IFTTT.Events.Absent.Value3})
  136. return c, nil
  137. }