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 }