github.com/martinohmann/rfoutlet@v1.2.1-0.20220707195255-8a66aa411105/internal/config/config.go (about)

     1  // Package config provides the config file schema and utilities to load the
     2  // config into concrete outlet and outlet group types.
     3  package config
     4  
     5  import (
     6  	"io"
     7  	"io/ioutil"
     8  	"os"
     9  
    10  	"github.com/ghodss/yaml"
    11  	"github.com/imdario/mergo"
    12  	"github.com/martinohmann/rfoutlet/internal/outlet"
    13  	"github.com/martinohmann/rfoutlet/internal/schedule"
    14  	"github.com/martinohmann/rfoutlet/pkg/gpio"
    15  )
    16  
    17  const (
    18  	// DefaultListenAddress defines the default address to listen on.
    19  	DefaultListenAddress = ":3333"
    20  
    21  	// DefaultTransmitPin defines the default gpio pin for transmitting rf codes.
    22  	DefaultTransmitPin uint = 17
    23  
    24  	// DefaultReceivePin defines the default gpio pin for receiving rf codes.
    25  	DefaultReceivePin uint = 27
    26  
    27  	// DefaultProtocol defines the default rf protocol.
    28  	DefaultProtocol int = 1
    29  
    30  	// DefaultPulseLength defines the default pulse length.
    31  	DefaultPulseLength uint = 189
    32  )
    33  
    34  // DefaultConfig contains the default values which are chosen if a file is
    35  // omitted in the config file.
    36  var DefaultConfig = Config{
    37  	ListenAddress: DefaultListenAddress,
    38  	GPIO: GPIOConfig{
    39  		ReceivePin:         DefaultReceivePin,
    40  		TransmitPin:        DefaultTransmitPin,
    41  		DefaultPulseLength: DefaultPulseLength,
    42  		DefaultProtocol:    DefaultProtocol,
    43  		TransmissionCount:  gpio.DefaultTransmissionCount,
    44  	},
    45  }
    46  
    47  // Config is the structure of the config file.
    48  type Config struct {
    49  	ListenAddress    string              `json:"listenAddress"`
    50  	StateFile        string              `json:"stateFile"`
    51  	DetectStateDrift bool                `json:"detectStateDrift"`
    52  	GPIO             GPIOConfig          `json:"gpio"`
    53  	OutletGroups     []OutletGroupConfig `json:"outletGroups"`
    54  }
    55  
    56  // GPIOConfig is the structure of the gpio config section.
    57  type GPIOConfig struct {
    58  	ReceivePin         uint `json:"receivePin"`
    59  	TransmitPin        uint `json:"transmitPin"`
    60  	DefaultPulseLength uint `json:"defaultPulseLength"`
    61  	DefaultProtocol    int  `json:"defaultProtocol"`
    62  	TransmissionCount  int  `json:"transmissionCount"`
    63  }
    64  
    65  // OutletGroupConfig is the structure of the config for a single outlet group.
    66  type OutletGroupConfig struct {
    67  	ID          string         `json:"id"`
    68  	DisplayName string         `json:"displayName"`
    69  	Outlets     []OutletConfig `json:"outlets"`
    70  }
    71  
    72  // OutletConfig is the structure of the config for a single outlet.
    73  type OutletConfig struct {
    74  	ID          string `json:"id"`
    75  	DisplayName string `json:"displayName"`
    76  	CodeOn      uint64 `json:"codeOn"`
    77  	CodeOff     uint64 `json:"codeOff"`
    78  	Protocol    int    `json:"protocol"`
    79  	PulseLength uint   `json:"pulseLength"`
    80  }
    81  
    82  // BuildOutletGroups builds outlet groups from c.
    83  func (c Config) BuildOutletGroups() []*outlet.Group {
    84  	groups := make([]*outlet.Group, len(c.OutletGroups))
    85  
    86  	for i, gc := range c.OutletGroups {
    87  		outlets := make([]*outlet.Outlet, len(gc.Outlets))
    88  
    89  		for j, oc := range gc.Outlets {
    90  			o := &outlet.Outlet{
    91  				ID:          oc.ID,
    92  				DisplayName: oc.DisplayName,
    93  				CodeOn:      oc.CodeOn,
    94  				CodeOff:     oc.CodeOff,
    95  				Protocol:    oc.Protocol,
    96  				PulseLength: oc.PulseLength,
    97  				Schedule:    schedule.New(),
    98  				State:       outlet.StateOff,
    99  			}
   100  
   101  			if o.DisplayName == "" {
   102  				o.DisplayName = o.ID
   103  			}
   104  
   105  			if o.PulseLength == 0 {
   106  				o.PulseLength = c.GPIO.DefaultPulseLength
   107  			}
   108  
   109  			if o.Protocol == 0 {
   110  				o.Protocol = c.GPIO.DefaultProtocol
   111  			}
   112  
   113  			outlets[j] = o
   114  		}
   115  
   116  		g := &outlet.Group{
   117  			ID:          gc.ID,
   118  			DisplayName: gc.DisplayName,
   119  			Outlets:     outlets,
   120  		}
   121  
   122  		if g.DisplayName == "" {
   123  			g.DisplayName = g.ID
   124  		}
   125  
   126  		groups[i] = g
   127  	}
   128  
   129  	return groups
   130  }
   131  
   132  // LoadWithDefaults loads config from file and merges in the default config for
   133  // unset fields.
   134  func LoadWithDefaults(file string) (*Config, error) {
   135  	config, err := Load(file)
   136  	if err != nil {
   137  		return nil, err
   138  	}
   139  
   140  	err = mergo.Merge(config, DefaultConfig)
   141  	if err != nil {
   142  		return nil, err
   143  	}
   144  
   145  	return config, nil
   146  }
   147  
   148  // Load loads the config from a file.
   149  func Load(file string) (*Config, error) {
   150  	f, err := os.Open(file)
   151  	if err != nil {
   152  		return nil, err
   153  	}
   154  
   155  	return LoadWithReader(f)
   156  }
   157  
   158  // LoadWithReader loads the config using reader.
   159  func LoadWithReader(r io.Reader) (*Config, error) {
   160  	c, err := ioutil.ReadAll(r)
   161  	if err != nil {
   162  		return nil, err
   163  	}
   164  
   165  	config := &Config{}
   166  
   167  	err = yaml.Unmarshal(c, &config)
   168  	if err != nil {
   169  		return nil, err
   170  	}
   171  
   172  	return config, nil
   173  }