diff --git a/cmd/ucrt/ucrt.go b/cmd/ucrt/ucrt.go index cd4ae86..e494fe6 100644 --- a/cmd/ucrt/ucrt.go +++ b/cmd/ucrt/ucrt.go @@ -4,8 +4,11 @@ import ( "os" "path/filepath" + "github.com/spf13/viper" "github.com/splattner/goucrt/pkg/cmd" "github.com/splattner/goucrt/pkg/cmd/ucrt" + + log "github.com/sirupsen/logrus" ) const RELEASEDATE string = "21.09.2023" @@ -14,6 +17,15 @@ func main() { baseName := filepath.Base(os.Args[0]) + viper.SetConfigName("config") + viper.AddConfigPath(".") + viper.SetEnvPrefix("UC_") + viper.AutomaticEnv() + + if err := viper.ReadInConfig(); err != nil { + log.WithError(err).Info("Unable to read config") + } + err := ucrt.NewCommand(baseName).Execute() cmd.CheckError(err) diff --git a/pkg/cmd/deconz/deconz.go b/pkg/cmd/deconz/deconz.go index 05410ac..99b173d 100644 --- a/pkg/cmd/deconz/deconz.go +++ b/pkg/cmd/deconz/deconz.go @@ -6,6 +6,7 @@ import ( log "github.com/sirupsen/logrus" "github.com/spf13/cobra" + "github.com/spf13/viper" "github.com/splattner/goucrt/pkg/client" "github.com/splattner/goucrt/pkg/cmd" "github.com/splattner/goucrt/pkg/integration" @@ -21,32 +22,15 @@ func NewCommand(rootCmd *cobra.Command) *cobra.Command { log.SetOutput(os.Stdout) - debug, _ := rootCmd.Flags().GetBool("debug") + debug := viper.GetBool("debug") if debug { log.SetLevel(log.DebugLevel) } else { log.SetLevel(log.InfoLevel) } - var config = make(integration.Config) - - listenPort, _ := rootCmd.Flags().GetInt("listenPort") - enableMDNS, _ := rootCmd.Flags().GetBool("mdns") - enableRegistration, _ := rootCmd.Flags().GetBool("registration") - registrationUsername, _ := rootCmd.Flags().GetString("registrationUsername") - registrationPin, _ := rootCmd.Flags().GetString("registrationPin") - websocketPath, _ := rootCmd.Flags().GetString("websocketPath") - remoteTwoIP, _ := rootCmd.Flags().GetString("remoteTwoIP") - remoteTwoPort, _ := rootCmd.Flags().GetInt("remoteTwoPort") - - config["listenport"] = listenPort - config["enableMDNS"] = enableMDNS - config["enableRegistration"] = enableRegistration - config["registrationUsername"] = registrationUsername - config["registrationPin"] = registrationPin - config["remoteTwoIP"] = remoteTwoIP - config["remoteTwoPort"] = remoteTwoPort - config["websocketPath"] = websocketPath + var config integration.Config + viper.Unmarshal(&config) i, err := integration.NewIntegration(config) cmd.CheckError(err) diff --git a/pkg/cmd/denonavr/denonavr.go b/pkg/cmd/denonavr/denonavr.go index 02ef5b3..640a6fa 100644 --- a/pkg/cmd/denonavr/denonavr.go +++ b/pkg/cmd/denonavr/denonavr.go @@ -6,6 +6,7 @@ import ( log "github.com/sirupsen/logrus" "github.com/spf13/cobra" + "github.com/spf13/viper" "github.com/splattner/goucrt/pkg/client" "github.com/splattner/goucrt/pkg/cmd" "github.com/splattner/goucrt/pkg/integration" @@ -21,32 +22,15 @@ func NewCommand(rootCmd *cobra.Command) *cobra.Command { log.SetOutput(os.Stdout) - debug, _ := rootCmd.Flags().GetBool("debug") + debug := viper.GetBool("debug") if debug { log.SetLevel(log.DebugLevel) } else { log.SetLevel(log.InfoLevel) } - var config = make(integration.Config) - - listenPort, _ := rootCmd.Flags().GetInt("listenPort") - enableMDNS, _ := rootCmd.Flags().GetBool("mdns") - enableRegistration, _ := rootCmd.Flags().GetBool("registration") - registrationUsername, _ := rootCmd.Flags().GetString("registrationUsername") - registrationPin, _ := rootCmd.Flags().GetString("registrationPin") - websocketPath, _ := rootCmd.Flags().GetString("websocketPath") - remoteTwoIP, _ := rootCmd.Flags().GetString("remoteTwoIP") - remoteTwoPort, _ := rootCmd.Flags().GetInt("remoteTwoPort") - - config["listenport"] = listenPort - config["enableMDNS"] = enableMDNS - config["enableRegistration"] = enableRegistration - config["registrationUsername"] = registrationUsername - config["registrationPin"] = registrationPin - config["remoteTwoIP"] = remoteTwoIP - config["remoteTwoPort"] = remoteTwoPort - config["websocketPath"] = websocketPath + var config integration.Config + viper.Unmarshal(&config) i, err := integration.NewIntegration(config) cmd.CheckError(err) diff --git a/pkg/cmd/shelly/shelly.go b/pkg/cmd/shelly/shelly.go index bd93dbe..8d54f4b 100644 --- a/pkg/cmd/shelly/shelly.go +++ b/pkg/cmd/shelly/shelly.go @@ -6,6 +6,7 @@ import ( log "github.com/sirupsen/logrus" "github.com/spf13/cobra" + "github.com/spf13/viper" "github.com/splattner/goucrt/pkg/client" "github.com/splattner/goucrt/pkg/cmd" "github.com/splattner/goucrt/pkg/integration" @@ -28,25 +29,8 @@ func NewCommand(rootCmd *cobra.Command) *cobra.Command { log.SetLevel(log.InfoLevel) } - var config = make(integration.Config) - - listenPort, _ := rootCmd.Flags().GetInt("listenPort") - enableMDNS, _ := rootCmd.Flags().GetBool("mdns") - enableRegistration, _ := rootCmd.Flags().GetBool("registration") - registrationUsername, _ := rootCmd.Flags().GetString("registrationUsername") - registrationPin, _ := rootCmd.Flags().GetString("registrationPin") - websocketPath, _ := rootCmd.Flags().GetString("websocketPath") - remoteTwoIP, _ := rootCmd.Flags().GetString("remoteTwoIP") - remoteTwoPort, _ := rootCmd.Flags().GetInt("remoteTwoPort") - - config["listenport"] = listenPort - config["enableMDNS"] = enableMDNS - config["enableRegistration"] = enableRegistration - config["registrationUsername"] = registrationUsername - config["registrationPin"] = registrationPin - config["remoteTwoIP"] = remoteTwoIP - config["remoteTwoPort"] = remoteTwoPort - config["websocketPath"] = websocketPath + var config integration.Config + viper.Unmarshal(&config) i, err := integration.NewIntegration(config) cmd.CheckError(err) diff --git a/pkg/cmd/tasmota/tasmota.go b/pkg/cmd/tasmota/tasmota.go index 7ca0068..93647f6 100644 --- a/pkg/cmd/tasmota/tasmota.go +++ b/pkg/cmd/tasmota/tasmota.go @@ -6,6 +6,7 @@ import ( log "github.com/sirupsen/logrus" "github.com/spf13/cobra" + "github.com/spf13/viper" "github.com/splattner/goucrt/pkg/client" "github.com/splattner/goucrt/pkg/cmd" "github.com/splattner/goucrt/pkg/integration" @@ -28,25 +29,8 @@ func NewCommand(rootCmd *cobra.Command) *cobra.Command { log.SetLevel(log.InfoLevel) } - var config = make(integration.Config) - - listenPort, _ := rootCmd.Flags().GetInt("listenPort") - enableMDNS, _ := rootCmd.Flags().GetBool("mdns") - enableRegistration, _ := rootCmd.Flags().GetBool("registration") - registrationUsername, _ := rootCmd.Flags().GetString("registrationUsername") - registrationPin, _ := rootCmd.Flags().GetString("registrationPin") - websocketPath, _ := rootCmd.Flags().GetString("websocketPath") - remoteTwoIP, _ := rootCmd.Flags().GetString("remoteTwoIP") - remoteTwoPort, _ := rootCmd.Flags().GetInt("remoteTwoPort") - - config["listenport"] = listenPort - config["enableMDNS"] = enableMDNS - config["enableRegistration"] = enableRegistration - config["registrationUsername"] = registrationUsername - config["registrationPin"] = registrationPin - config["remoteTwoIP"] = remoteTwoIP - config["remoteTwoPort"] = remoteTwoPort - config["websocketPath"] = websocketPath + var config integration.Config + viper.Unmarshal(&config) i, err := integration.NewIntegration(config) cmd.CheckError(err) diff --git a/pkg/cmd/ucrt/ucrt.go b/pkg/cmd/ucrt/ucrt.go index 9fca777..fdb65f8 100644 --- a/pkg/cmd/ucrt/ucrt.go +++ b/pkg/cmd/ucrt/ucrt.go @@ -2,6 +2,7 @@ package ucrt import ( "github.com/spf13/cobra" + "github.com/spf13/viper" "github.com/splattner/goucrt/pkg/cmd/deconz" "github.com/splattner/goucrt/pkg/cmd/denonavr" "github.com/splattner/goucrt/pkg/cmd/shelly" @@ -16,16 +17,43 @@ func NewCommand(name string) *cobra.Command { } rootCmd.PersistentFlags().IntP("listenPort", "l", 8080, "the port this integration is listening for websocket connection from the remote") + viper.BindPFlag("listenPort", rootCmd.PersistentFlags().Lookup("listenPort")) + viper.BindEnv("listenPort", "UC_INTEGRATION_LISTEN_PORT") + rootCmd.PersistentFlags().String("websocketPath", "/ws", "path where this integration is available for websocket connections") + viper.BindPFlag("websocketPath", rootCmd.PersistentFlags().Lookup("websocketPath")) + viper.BindEnv("websocketPath", "UC_INTEGRATION_WEBSOCKET_PATH") + + rootCmd.PersistentFlags().Bool("disableMDNS", false, "Disable integration advertisement via mDNS") + viper.BindPFlag("disableMDNS", rootCmd.PersistentFlags().Lookup("disableMDNS")) + viper.BindEnv("disableMDNS", "UC_DISABLE_MDNS_PUBLISH") + + rootCmd.PersistentFlags().String("remoteTwoIP", "", "IP Address of your Remote Two instance (disables Remote Two discovery)") + viper.BindPFlag("remoteTwoIP", rootCmd.PersistentFlags().Lookup("remoteTwoIP")) + viper.BindEnv("remoteTwoIP", "UC_RT_HOST") + + rootCmd.PersistentFlags().Int("remoteTwoPort", 80, "Port of your Remote Two instance (disables Remote Two discovery)") + viper.BindPFlag("remoteTwoPort", rootCmd.PersistentFlags().Lookup("remoteTwoPort")) + viper.BindEnv("remoteTwoPort", "UC_RT_PORT") - rootCmd.PersistentFlags().Bool("mdns", true, "Enable integration advertisement via mDNS") rootCmd.PersistentFlags().Bool("registration", false, "Enable driver registration on the Remote Two instead of mDNS advertisement") + viper.BindPFlag("registration", rootCmd.PersistentFlags().Lookup("registration")) + viper.BindEnv("registration", "UC_ENABLE_REGISTRATION") + rootCmd.PersistentFlags().String("registrationUsername", "web-configurator", "Username of the RemoteTwo for driver registration") + viper.BindPFlag("registrationUsername", rootCmd.PersistentFlags().Lookup("registrationUsername")) + viper.BindEnv("registrationUsername", "UC_REGISTRATION_USERNAME") + rootCmd.PersistentFlags().String("registrationPin", "", "Pin of the RemoteTwo for driver registration") - rootCmd.PersistentFlags().String("remoteTwoIP", "", "IP Address of your Remote Two instance (disables Remote Two discovery)") - rootCmd.PersistentFlags().Int("remoteTwoPort", 80, "Port of your Remote Two instance (disables Remote Two discovery)") + viper.BindPFlag("registrationPin", rootCmd.PersistentFlags().Lookup("registrationPin")) + viper.BindEnv("registrationUsername", "UC_REGISTRATION_PIN") rootCmd.PersistentFlags().Bool("debug", false, "Enable debug log level") + viper.BindPFlag("debug", rootCmd.PersistentFlags().Lookup("debug")) + + rootCmd.PersistentFlags().String("ucconfighome", "./", "Configuration directory to save the user configuration from the driver setup") + viper.BindPFlag("ucconfighome", rootCmd.PersistentFlags().Lookup("ucconfighome")) + viper.BindEnv("ucconfighome", "UC_CONFIG_HOME") rootCmd.AddCommand( denonavr.NewCommand(rootCmd), diff --git a/pkg/integration/advertisement.go b/pkg/integration/advertisement.go index e73ff18..2b41f85 100644 --- a/pkg/integration/advertisement.go +++ b/pkg/integration/advertisement.go @@ -14,10 +14,10 @@ func (i *Integration) startAdvertising() { "name=" + i.Metadata.Name.En, "developer=" + i.Metadata.Developer.Name, "ver=" + i.Metadata.Version, - "ws_path=" + i.Config["websocketPath"].(string), + "ws_path=" + i.Config.WebsocketPath, } - server, err := zeroconf.Register(i.Metadata.DriverId, "_uc-integration._tcp", "local.", i.Config["listenport"].(int), txt, nil) + server, err := zeroconf.Register(i.Metadata.DriverId, "_uc-integration._tcp", "local.", i.Config.ListenPort, txt, nil) if err != nil { panic(err) } diff --git a/pkg/integration/config.go b/pkg/integration/config.go index 4191355..2047e72 100644 --- a/pkg/integration/config.go +++ b/pkg/integration/config.go @@ -1,4 +1,15 @@ package integration // Generic string key/value config map to store configuration option -type Config map[string]interface{} +type Config struct { + ListenPort int `mapstructure:"listenPort"` + DisableMDNS bool `mapstructure:"disableMDNS"` + EnableRegistration bool `mapstructure:"enableRegistration"` + RegistrationUsername string `mapstructure:"registrationUsername"` + RegistrationPin string `mapstructure:"registrationPin"` + WebsocketPath string `mapstructure:"websocketPath"` + ConfigHome string `mapstructure:"ucconfighome"` + RemoteTwoHost string `mapstructure:"remoteTwoIP"` + RemoteTwoPort int `mapstructure:"remoteTwoPort"` + IgnoreEntitySubscription bool +} diff --git a/pkg/integration/events.go b/pkg/integration/events.go index dcd801c..97664d6 100644 --- a/pkg/integration/events.go +++ b/pkg/integration/events.go @@ -202,7 +202,7 @@ func (i *Integration) SendEntityChangeEvent(e interface{}) { log.WithField("subscribedEtities", i.SubscribedEntities).Debug("Currently subscribed entities") // Only send the event when remote is subscribed to - if (i.Config["ignoreEntitySubscription"] != nil && i.Config["ignoreEntitySubscription"].(bool)) || slices.Contains(i.SubscribedEntities, entity_id) { + if i.Config.IgnoreEntitySubscription || slices.Contains(i.SubscribedEntities, entity_id) { var res interface{} now := time.Now() diff --git a/pkg/integration/integration.go b/pkg/integration/integration.go index d0d1a32..df06498 100644 --- a/pkg/integration/integration.go +++ b/pkg/integration/integration.go @@ -47,7 +47,7 @@ func NewIntegration(config Config) (*Integration, error) { i := Integration{ Config: config, - listenAddress: fmt.Sprintf(":%d", config["listenport"].(int)), + listenAddress: fmt.Sprintf(":%d", config.ListenPort), deviceState: DisconnectedDeviceState, DeviceId: "", // I think device_id is not yet implemented in Remote TV, used for multi-device integrati @@ -79,15 +79,15 @@ func (i *Integration) Run() error { return fmt.Errorf("Metadata not set") } - http.HandleFunc(i.Config["websocketPath"].(string), i.wsEndpoint) + http.HandleFunc(i.Config.WebsocketPath, i.wsEndpoint) //MDNS - if i.Config["enableMDNS"].(bool) { + if !i.Config.DisableMDNS { go i.startAdvertising() } // Register the integration - if i.Config["enableRegistration"].(bool) && i.Config["registrationPin"].(string) != "" { + if i.Config.EnableRegistration && i.Config.RegistrationPin != "" { go i.registerIntegration() } @@ -135,7 +135,7 @@ func (i *Integration) SetDriverSetupState(event_Type DriverSetupEventType, state // TODO: handle location via ENV's func (i *Integration) LoadSetupData() { - file, err := os.ReadFile(i.Metadata.DriverId + ".json") + file, err := os.ReadFile(i.Config.ConfigHome + i.Metadata.DriverId + ".json") if err != nil { log.WithError(err).Info("Cannot read setupDataFile") i.SetupData = make(SetupData) @@ -151,5 +151,5 @@ func (i *Integration) PersistSetupData() { log.WithField("SetupData", i.SetupData).Info("Persist setup data") file, _ := json.MarshalIndent(i.SetupData, "", " ") - _ = os.WriteFile(i.Metadata.DriverId+".json", file, 0644) + _ = os.WriteFile(i.Config.ConfigHome+i.Metadata.DriverId+".json", file, 0644) } diff --git a/pkg/integration/register.go b/pkg/integration/register.go index d5753c6..b8462e3 100644 --- a/pkg/integration/register.go +++ b/pkg/integration/register.go @@ -35,8 +35,8 @@ type DriverRegistration struct { func (i *Integration) registerIntegration() { // Use configured IP for registration instead of Remote Two discovery - if i.Config["remoteTwoIP"].(string) != "" && i.Config["remoteTwoPort"].(int) > 0 { - i.registerWithRemoteTwo(i.Config["remoteTwoIP"].(string), i.Config["remoteTwoPort"].(int)) + if i.Config.RegistrationPin != "" && i.Config.RemoteTwoPort > 0 { + i.registerWithRemoteTwo(i.Config.RemoteTwoHost, i.Config.RemoteTwoPort) } else { entries := make(chan *zeroconf.ServiceEntry) @@ -78,7 +78,7 @@ func (i *Integration) registerIntegration() { func (i *Integration) registerWithRemoteTwo(remoteTwoIP string, remoteTwoPort int) { myip := GetLocalIP() - driverURL := "ws://" + myip + i.listenAddress + i.Config["websocketPath"].(string) + driverURL := "ws://" + myip + i.listenAddress + i.Config.WebsocketPath remoteTwoURL := "http://" + remoteTwoIP + ":" + fmt.Sprint(remoteTwoPort) driverRegistration := DriverRegistration{ @@ -107,7 +107,7 @@ func (i *Integration) registerWithRemoteTwo(remoteTwoIP string, remoteTwoPort in req.Header.Set("Content-Type", "application/json") // Authentication wit the Remote Two - credentials := b64.StdEncoding.EncodeToString([]byte(i.Config["registrationUsername"].(string) + ":" + i.Config["registrationPin"].(string))) + credentials := b64.StdEncoding.EncodeToString([]byte(i.Config.RegistrationUsername + ":" + i.Config.RegistrationPin)) req.Header.Set("Authorization", "Basic "+credentials) // send the request