123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 |
- package presence
- import (
- "context"
- "fmt"
- "net"
- "net/url"
- "os"
- "regexp"
- "strings"
- "time"
- "goa.design/clue/log"
- "gopkg.in/yaml.v3"
- "douglasthrift.net/presence/wrap"
- )
- type (
- Config struct {
- Interval time.Duration `yaml:"interval"`
- Interfaces []string `yaml:"interfaces"`
- MACAddresses []string `yaml:"mac_addresses"`
- PingCount uint `yaml:"ping_count"`
- IFTTT IFTTT `yaml:"ifttt"`
- }
- IFTTT struct {
- BaseURL string `yaml:"base_url"`
- Key string `yaml:"key"`
- Events Events `yaml:"events"`
- }
- Events struct {
- Present string `yaml:"present"`
- Absent string `yaml:"absent"`
- }
- )
- const (
- defaultBaseURL = "https://maker.ifttt.com"
- defaultPresentEvent = "presence_detected"
- defaultAbsentEvent = "absence_detected"
- )
- var (
- eventName = regexp.MustCompile("^[_a-zA-Z]+$")
- )
- func ParseConfig(name string, wNet wrap.Net) (*Config, error) {
- return ParseConfigWithContext(context.Background(), name, wNet)
- }
- func ParseConfigWithContext(ctx context.Context, name string, wNet wrap.Net) (*Config, error) {
- f, err := os.Open(name)
- if err != nil {
- return nil, err
- }
- defer f.Close()
- d := yaml.NewDecoder(f)
- d.KnownFields(true)
- c := &Config{}
- err = d.Decode(c)
- if err != nil {
- return nil, err
- }
- if c.Interval < 0 {
- return nil, fmt.Errorf("negative interval (%v)", c.Interval)
- } else if c.Interval == 0 {
- c.Interval = 30 * time.Second
- }
- log.Print(ctx, log.KV{K: "msg", V: "interval"}, log.KV{K: "value", V: c.Interval})
- if len(c.Interfaces) == 0 {
- ifs, err := wNet.Interfaces()
- if err != nil {
- return nil, err
- }
- c.Interfaces = make([]string, 0, len(ifs))
- for _, i := range ifs {
- c.Interfaces = append(c.Interfaces, i.Name)
- }
- } else {
- for _, i := range c.Interfaces {
- _, err = wNet.InterfaceByName(i)
- if err != nil {
- return nil, fmt.Errorf("interface %v: %w", i, err)
- }
- }
- }
- log.Print(ctx, log.KV{K: "msg", V: "interfaces"}, log.KV{K: "value", V: c.Interfaces})
- if len(c.MACAddresses) == 0 {
- return nil, fmt.Errorf("no MAC addresses")
- }
- as := make(map[string]bool, len(c.MACAddresses))
- for i, a := range c.MACAddresses {
- hw, err := net.ParseMAC(a)
- if err != nil {
- return nil, err
- }
- a = hw.String()
- if as[a] {
- return nil, fmt.Errorf("duplicate MAC address (%v)", a)
- }
- as[a] = true
- c.MACAddresses[i] = a
- }
- log.Print(ctx, log.KV{K: "msg", V: "MAC addresses"}, log.KV{K: "value", V: c.MACAddresses})
- if c.PingCount == 0 {
- c.PingCount = 1
- }
- log.Print(ctx, log.KV{K: "msg", V: "ping count"}, log.KV{K: "value", V: c.PingCount})
- if c.IFTTT.BaseURL == "" {
- c.IFTTT.BaseURL = defaultBaseURL
- } else if _, err := url.Parse(c.IFTTT.BaseURL); err != nil {
- return nil, fmt.Errorf("IFTTT base URL: %w", err)
- }
- log.Print(ctx, log.KV{K: "msg", V: "IFTTT base URL"}, log.KV{K: "value", V: c.IFTTT.BaseURL})
- if c.IFTTT.Key == "" {
- return nil, fmt.Errorf("no IFTTT key")
- }
- log.Print(ctx, log.KV{K: "msg", V: "IFTTT key"}, log.KV{K: "value", V: strings.Repeat("*", len(c.IFTTT.Key))})
- if c.IFTTT.Events.Present == "" {
- c.IFTTT.Events.Present = defaultPresentEvent
- } else if !eventName.MatchString(c.IFTTT.Events.Present) {
- return nil, fmt.Errorf("invalid IFTTT present event name: %#v", c.IFTTT.Events.Present)
- }
- log.Print(ctx, log.KV{K: "msg", V: "IFTTT present event"}, log.KV{K: "value", V: c.IFTTT.Events.Present})
- if c.IFTTT.Events.Absent == "" {
- c.IFTTT.Events.Absent = defaultAbsentEvent
- } else if !eventName.MatchString(c.IFTTT.Events.Absent) {
- return nil, fmt.Errorf("invalid IFTTT absent event name: %#v", c.IFTTT.Events.Absent)
- }
- log.Print(ctx, log.KV{K: "msg", V: "IFTTT absent event"}, log.KV{K: "value", V: c.IFTTT.Events.Absent})
- return c, nil
- }
|