diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml new file mode 100644 index 0000000..27d1e79 --- /dev/null +++ b/.github/workflows/lint.yaml @@ -0,0 +1,24 @@ +name: golangci-lint +on: + push: + branches: [main] + tags: ["v*"] + pull_request: + +jobs: + golang-lint: + name: lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-go@v2 + with: + go-version: ^1.21 + + - name: golangci-lint + uses: golangci/golangci-lint-action@v2 + with: + version: latest + skip-go-installation: true + # Optional: show only new issues if it's a pull request. The default value is `false`. + # only-new-issues: true diff --git a/.github/workflows/publish_executables.yaml b/.github/workflows/publish_executables.yaml new file mode 100644 index 0000000..dfbf405 --- /dev/null +++ b/.github/workflows/publish_executables.yaml @@ -0,0 +1,62 @@ +name: publish executables +on: + push: + branches: [main] + tags: ["v*"] + +jobs: + setup: + runs-on: ubuntu-latest + + steps: + - name: checkout the source code + uses: actions/checkout@v2 + + - uses: actions/setup-go@v2 + with: + go-version: ^1.21 + + - name: Cache go modules + uses: actions/cache@v2 + with: + path: ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + + - name: build executable bundles + run: ./scripts/generate_executables.sh + + - name: generate a build timestamp and sha256sum files + run: | + cd builds + echo `date -u +'%Y%m%d%H%M%S'` > ./build_timestamp.txt + echo `date -u +'%Y-%m-%dT%H:%M:%S%:z'` >> ./build_timestamp.txt + sha256sum *.tar.gz > ./SHA256SUMS.txt + sha256sum *.zip >> ./SHA256SUMS.txt + + - name: update release notes and executables + if: startsWith(github.ref, 'refs/tags/') # executes only for new release + uses: softprops/action-gh-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} + with: + files: | + builds/*.tar.gz + builds/*.zip + builds/build_timestamp.txt + builds/SHA256SUMS.txt + + - name: Update executables for main branch changes + if: startsWith(github.ref, 'refs/heads/main') # executes only for changes in main + uses: "marvinpinto/action-automatic-releases@latest" + with: + repo_token: "${{ secrets.GH_TOKEN }}" + automatic_release_tag: development + prerelease: true + title: "Development Build - Pre Release" + files: | + builds/*.tar.gz + builds/*.zip + builds/build_timestamp.txt + builds/SHA256SUMS.txt diff --git a/cli/command/device/device.go b/cli/command/device/device.go new file mode 100644 index 0000000..64b626e --- /dev/null +++ b/cli/command/device/device.go @@ -0,0 +1,78 @@ +package device + +import ( + "fmt" + "strings" + + rootCmd "github.com/mycontroller-org/esphome_api/cli/command/root" + "github.com/mycontroller-org/server/v2/pkg/utils/printer" + "github.com/spf13/cobra" +) + +func init() { + rootCmd.AddCommand(deviceContextCmd) + rootCmd.AddCommand(getDevicesCmd) +} + +var deviceContextCmd = &cobra.Command{ + Use: "device", + Short: "Switch or set a device", + Example: ` # set a node + esphomectl device my-device-1 + + # get the active device + esphomectl device +`, + Args: cobra.MaximumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + if len(args) == 0 { + if rootCmd.CONFIG.Active == "" { + fmt.Fprintln(cmd.OutOrStdout(), "No resource found") + return + } + fmt.Fprintf(cmd.ErrOrStderr(), "Active node '%s'\n", rootCmd.CONFIG.Active) + return + } + rootCmd.CONFIG.Active = strings.TrimSpace(args[0]) + client, err := rootCmd.GetActiveClient(nil) + if err != nil { + fmt.Fprintln(cmd.ErrOrStderr(), "Error on login", err) + return + } + if client != nil { + rootCmd.WriteConfigFile() + fmt.Fprintf(cmd.OutOrStdout(), "Switched to '%s'\n", rootCmd.CONFIG.Active) + } + }, +} + +var getDevicesCmd = &cobra.Command{ + Use: "devices", + Short: "Display configured devices", + Example: ` # display configured devices + esphomectl devices +`, + Run: func(cmd *cobra.Command, args []string) { + if len(rootCmd.CONFIG.Devices) == 0 { + fmt.Fprintln(cmd.OutOrStdout(), "No resource found") + return + } + headers := []printer.Header{ + {Title: "address", ValuePath: "address"}, + {Title: "name", ValuePath: "info.name"}, + {Title: "model", ValuePath: "info.model"}, + {Title: "mac address", ValuePath: "info.macAddress"}, + {Title: "version", ValuePath: "info.esphomeVersion"}, + {Title: "compilation time", ValuePath: "info.compilationTime"}, + {Title: "uses password", ValuePath: "info.usesPassword"}, + {Title: "has deep sleep", ValuePath: "info.hasDeepSleep"}, + {Title: "timeout", ValuePath: "timeout", IsWide: true}, + {Title: "status on", ValuePath: "info.statusOn", DisplayStyle: printer.DisplayStyleRelativeTime}, + } + data := make([]interface{}, 0) + for _, device := range rootCmd.CONFIG.Devices { + data = append(data, device) + } + printer.Print(cmd.OutOrStdout(), headers, data, rootCmd.HideHeader, rootCmd.OutputFormat, rootCmd.Pretty) + }, +} diff --git a/cli/command/device/get.go b/cli/command/device/get.go new file mode 100644 index 0000000..40cc4b2 --- /dev/null +++ b/cli/command/device/get.go @@ -0,0 +1,15 @@ +package device + +import ( + rootCmd "github.com/mycontroller-org/esphome_api/cli/command/root" + "github.com/spf13/cobra" +) + +func init() { + rootCmd.AddCommand(getCmd) +} + +var getCmd = &cobra.Command{ + Use: "get", + Short: "List resources", +} diff --git a/cli/command/device/get_entities.go b/cli/command/device/get_entities.go new file mode 100644 index 0000000..5e10377 --- /dev/null +++ b/cli/command/device/get_entities.go @@ -0,0 +1,117 @@ +package device + +import ( + "fmt" + "strings" + "time" + + rootCmd "github.com/mycontroller-org/esphome_api/cli/command/root" + "github.com/mycontroller-org/esphome_api/pkg/api" + "github.com/mycontroller-org/server/v2/pkg/utils/convertor" + filterUtils "github.com/mycontroller-org/server/v2/pkg/utils/filter_sort" + "github.com/mycontroller-org/server/v2/pkg/utils/printer" + "github.com/spf13/cobra" + "google.golang.org/protobuf/proto" +) + +var ( + entitiesTimeout time.Duration +) + +func init() { + getCmd.AddCommand(getEntitiesCmd) + getEntitiesCmd.Flags().DurationVar(&entitiesTimeout, "timeout", 5*time.Second, "Timeout to wait for entities") +} + +var getEntitiesCmd = &cobra.Command{ + Use: "entity", + Short: "Lists available entities", + Aliases: []string{"entities"}, + Example: ` # lists available entities + esphomectl get entities + + # list entities with a timeout + esphomectl get entities --timeout 10s +`, + Run: func(cmd *cobra.Command, args []string) { + + entitiesCollectionDone := false + entities := map[string][]interface{}{} + collectEntities := func(msg proto.Message) { + switch entity := msg.(type) { + case *api.ListEntitiesDoneResponse: + entitiesCollectionDone = true + + default: + _, _deviceClass, err := filterUtils.GetValueByKeyPath(entity, "deviceClass") + if err != nil { + fmt.Fprintln(cmd.ErrOrStderr(), "error:", err) + return + } + deviceClass := convertor.ToString(_deviceClass) + _sensors, found := entities[deviceClass] + if !found { + _sensors = make([]interface{}, 0) + } + _sensors = append(_sensors, entity) + entities[deviceClass] = _sensors + } + } + + client, err := rootCmd.GetActiveClient(collectEntities) + if err != nil { + fmt.Fprintln(cmd.ErrOrStderr(), "error:", err.Error()) + return + } + + err = client.ListEntities() + if err != nil { + fmt.Fprintln(cmd.ErrOrStderr(), "error:", err.Error()) + return + } + + ticker := time.NewTicker(200 * time.Millisecond) + timeoutTime := time.Now().Add(entitiesTimeout) + for range ticker.C { + if entitiesCollectionDone || time.Now().Before(timeoutTime) { + break + } + } + + if len(entities) == 0 { + fmt.Fprintln(cmd.OutOrStdout(), "No resource found") + return + } + + for k, _sensors := range entities { + fmt.Fprintln(cmd.OutOrStdout()) + fmt.Fprintln(cmd.OutOrStdout(), strings.ToUpper(k)) + + switch k { + case "light": + headers := []printer.Header{ + {Title: "name", ValuePath: "name"}, + {Title: "object id", ValuePath: "objectId"}, + {Title: "key", ValuePath: "key"}, + {Title: "unique id", ValuePath: "uniqueId"}, + {Title: "effects", ValuePath: "effects"}, + {Title: "icon", ValuePath: "icon"}, + } + printer.Print(cmd.OutOrStdout(), headers, _sensors, rootCmd.HideHeader, rootCmd.OutputFormat, rootCmd.Pretty) + + default: + headers := []printer.Header{ + {Title: "name", ValuePath: "name"}, + {Title: "object id", ValuePath: "objectId"}, + {Title: "key", ValuePath: "key"}, + {Title: "unique id", ValuePath: "uniqueId"}, + {Title: "device class", ValuePath: "deviceClass"}, + } + printer.Print(cmd.OutOrStdout(), headers, _sensors, rootCmd.HideHeader, rootCmd.OutputFormat, rootCmd.Pretty) + + } + + } + + }, +} diff --git a/cli/command/device/set.go b/cli/command/device/set.go new file mode 100644 index 0000000..abbcf27 --- /dev/null +++ b/cli/command/device/set.go @@ -0,0 +1,15 @@ +package device + +import ( + rootCmd "github.com/mycontroller-org/esphome_api/cli/command/root" + "github.com/spf13/cobra" +) + +func init() { + rootCmd.AddCommand(setCmd) +} + +var setCmd = &cobra.Command{ + Use: "set", + Short: "update resource state", +} diff --git a/cli/command/device/set_entities.go b/cli/command/device/set_entities.go new file mode 100644 index 0000000..dffdfa8 --- /dev/null +++ b/cli/command/device/set_entities.go @@ -0,0 +1,28 @@ +package device + +import ( + "github.com/spf13/cobra" +) + +func init() { + setCmd.AddCommand(setEntitiesCmd) +} + +var setEntitiesCmd = &cobra.Command{ + Use: "entity", + Short: "Updates entity value", + Example: ` # lists available entities + esphomectl set entities unique_id --payload test=on +`, + Args: cobra.MinimumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + // _client, err := rootCmd.GetActiveClient(nil) + // if err != nil { + // fmt.Fprintln(cmd.ErrOrStderr(), "error:", err.Error()) + // return + // } + // + // var request proto.Message + + }, +} diff --git a/cli/command/root/cmd.go b/cli/command/root/cmd.go new file mode 100644 index 0000000..885fa58 --- /dev/null +++ b/cli/command/root/cmd.go @@ -0,0 +1,186 @@ +package root + +import ( + "errors" + "fmt" + "os" + "path/filepath" + + homedir "github.com/mitchellh/go-homedir" + cliTY "github.com/mycontroller-org/esphome_api/cli/types" + "github.com/mycontroller-org/esphome_api/pkg/client" + TY "github.com/mycontroller-org/esphome_api/pkg/types" + clientTY "github.com/mycontroller-org/server/v2/pkg/types/client" + printer "github.com/mycontroller-org/server/v2/pkg/utils/printer" + "gopkg.in/yaml.v3" + + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +const ( + ENV_PREFIX = "ESPHOME" + CONFIG_FILE_NAME = ".esphomectl" + CONFIG_FILE_EXT = "yaml" + CONFIG_FILE_ENV = "ESPHOMECTL_CONFIG" +) + +var ( + cfgFile string + CONFIG *cliTY.Config // keeps device details + ioStreams clientTY.IOStreams // read and write to this stream + + HideHeader bool + Pretty bool + OutputFormat string + + rootCliLong = `esphome cli Client + +This client helps you to control your esphome devices from the command line. +` +) + +func AddCommand(cmds ...*cobra.Command) { + rootCmd.AddCommand(cmds...) +} + +var rootCmd = &cobra.Command{ + Use: "esphomectl", + Short: "esphomectl", + Long: rootCliLong, + PersistentPreRun: func(cmd *cobra.Command, args []string) { + cmd.SetOut(ioStreams.Out) + cmd.SetErr(ioStreams.ErrOut) + }, +} + +func init() { + CONFIG = &cliTY.Config{} + + cobra.OnInitialize(loadConfig) + + rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.esphomectl.yaml)") + rootCmd.PersistentFlags().StringVarP(&OutputFormat, "output", "o", printer.OutputConsole, "output format. options: yaml, json, console, wide") + rootCmd.PersistentFlags().BoolVar(&HideHeader, "hide-header", false, "hides the header on the console output") + rootCmd.PersistentFlags().BoolVar(&Pretty, "pretty", false, "JSON pretty print") +} + +func GetActiveClient(callBackFunc TY.CallBackFunc) (*client.Client, error) { + if CONFIG.Active == "" { + return nil, errors.New("no device configured") + } + cfg := GetDevice(CONFIG.Active) + if cfg == nil { + return nil, fmt.Errorf("device[%s] configuration is not available", CONFIG.Active) + } + return GetClient(cfg, callBackFunc) +} + +func GetClient(cfg *cliTY.DeviceConfig, callBackFunc TY.CallBackFunc) (*client.Client, error) { + _client, err := client.GetClient("mc_esphome_cli", cfg.Address, cfg.EncryptionKey, cfg.Timeout, callBackFunc) + if err != nil { + return nil, err + } + if cfg.GetPassword() != "" { + err = _client.Login(cfg.GetPassword()) + if err != nil { + return nil, err + } + } + + return _client, nil +} + +func Execute(streams clientTY.IOStreams) { + ioStreams = streams + if err := rootCmd.Execute(); err != nil { + fmt.Fprintln(ioStreams.ErrOut, err) + os.Exit(1) + } +} + +func WriteConfigFile() { + if cfgFile == "" { + return + } + if CONFIG == nil { + CONFIG = &cliTY.Config{} + } + + configBytes, err := yaml.Marshal(CONFIG) + if err != nil { + fmt.Fprintf(ioStreams.ErrOut, "error on config file marshal. error:[%s]\n", err.Error()) + } + err = os.WriteFile(cfgFile, configBytes, os.ModePerm) + if err != nil { + fmt.Fprintf(ioStreams.ErrOut, "error on writing config file to disk, filename:%s, error:[%s]\n", cfgFile, err.Error()) + } +} + +func loadConfig() { + if cfgFile != "" { + // Use config file from the flag. + viper.SetConfigFile(cfgFile) + } else if os.Getenv(CONFIG_FILE_ENV) != "" { + cfgFile = os.Getenv(CONFIG_FILE_ENV) + } else { + // Find home directory.initConfig + home, err := homedir.Dir() + cobra.CheckErr(err) + + // Search config in home directory with name ".myc" (without extension). + viper.AddConfigPath(home) + viper.SetConfigName(CONFIG_FILE_NAME) + viper.SetConfigType(CONFIG_FILE_EXT) + + cfgFile = filepath.Join(home, fmt.Sprintf("%s.%s", CONFIG_FILE_NAME, CONFIG_FILE_EXT)) + } + + viper.SetEnvPrefix(ENV_PREFIX) + viper.AutomaticEnv() + + if err := viper.ReadInConfig(); err == nil { + err = viper.Unmarshal(&CONFIG) + if err != nil { + fmt.Fprint(ioStreams.ErrOut, "error on unmarshal of config\n", err) + } + } +} + +func GetDevice(address string) *cliTY.DeviceConfig { + for _, device := range CONFIG.Devices { + if device.Address == address { + _device := device.Clone() + return &_device + } + } + return nil +} + +func RemoveDevice(address string) { + devices := make([]cliTY.DeviceConfig, 0) + for _, device := range CONFIG.Devices { + if device.Address == address { + continue + } + devices = append(devices, device) + } + CONFIG.Devices = devices + CONFIG.Active = "" +} + +func AddDevice(newDevice *cliTY.DeviceConfig) { + newDevice.EncodePassword() + devices := make([]cliTY.DeviceConfig, 0) + for _, device := range CONFIG.Devices { + if device.Address == newDevice.Address { + continue + } + devices = append(devices, device) + } + + // add current device + devices = append(devices, *newDevice) + CONFIG.Devices = devices + CONFIG.Active = newDevice.Address +} diff --git a/cli/command/root/login.go b/cli/command/root/login.go new file mode 100644 index 0000000..c24ecb9 --- /dev/null +++ b/cli/command/root/login.go @@ -0,0 +1,100 @@ +package root + +import ( + "fmt" + "time" + + cliTY "github.com/mycontroller-org/esphome_api/cli/types" + "github.com/spf13/cobra" +) + +var ( + devicePassword string + deviceEncryptionKey string + deviceTimeout time.Duration +) + +func init() { + AddCommand(loginCmd) + loginCmd.Flags().StringVar(&devicePassword, "password", "", "Password to login into esphome device") + loginCmd.Flags().StringVar(&deviceEncryptionKey, "encryption-key", "", "Encryption key to login into esphome device") + loginCmd.Flags().DurationVar(&deviceTimeout, "timeout", 10*time.Second, "esphome device communication timeout") + + AddCommand(logoutCmd) +} + +var loginCmd = &cobra.Command{ + Use: "login", + Short: "Log in to a esphome device", + Example: ` # login to esphome device without password and encryption key + esphomectl login my_esphome.local:6053 + + # login to esphome device with password + esphomectl login my_esphome.local:6053 --password my_secret + + # login to esphome device with encryption key + esphomectl login my_esphome.local:6053 --encryption-key my_encryption_key +`, + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + deviceCfg := &cliTY.DeviceConfig{ + Address: args[0], + Password: devicePassword, + EncryptionKey: deviceEncryptionKey, + Timeout: deviceTimeout, + } + + _client, err := GetClient(deviceCfg, nil) + if err != nil { + fmt.Fprintln(cmd.ErrOrStderr(), "error on login", err) + return + } + if _client != nil { + deviceInfo, err := _client.DeviceInfo() + if err != nil { + fmt.Fprintln(cmd.ErrOrStderr(), "error on getting device information", err) + return + } + // update device info + deviceCfg.Info = cliTY.DeviceInfo{ + Name: deviceInfo.Name, + Model: deviceInfo.Model, + MacAddress: deviceInfo.MacAddress, + EsphomeVersion: deviceInfo.EsphomeVersion, + CompilationTime: deviceInfo.CompilationTime, + UsesPassword: deviceInfo.UsesPassword, + HasDeepSleep: deviceInfo.HasDeepSleep, + StatusOn: time.Now(), + } + AddDevice(deviceCfg) + WriteConfigFile() + + fmt.Fprintln(cmd.OutOrStdout(), "Login successful.") + fmt.Fprintf(cmd.OutOrStdout(), "%+v\n", deviceInfo) + } + }, +} + +var logoutCmd = &cobra.Command{ + Use: "logout", + Short: "Log out from a esphome device", + Example: ` # logout from a esphome device + esphomectl logout + + # logout from esphome devices + esphomectl logout my_device_1:6053 my_device_2:6053`, + Run: func(cmd *cobra.Command, args []string) { + if len(args) == 0 && CONFIG.Active == "" { + fmt.Fprintln(cmd.ErrOrStderr(), "There is no active device information.") + return + } + + // remove given devices + for _, address := range args { + RemoveDevice(address) + } + WriteConfigFile() + + fmt.Fprintln(cmd.OutOrStdout(), "Logout successful.") + }, +} diff --git a/cli/command/root/verson.go b/cli/command/root/verson.go new file mode 100644 index 0000000..1791e60 --- /dev/null +++ b/cli/command/root/verson.go @@ -0,0 +1,20 @@ +package root + +import ( + "fmt" + + "github.com/mycontroller-org/esphome_api/cli/version" + "github.com/spf13/cobra" +) + +func init() { + AddCommand(versionCmd) +} + +var versionCmd = &cobra.Command{ + Use: "version", + Long: "Prints version information", + Run: func(cmd *cobra.Command, args []string) { + fmt.Println(version.Get().String()) + }, +} diff --git a/cli/main.go b/cli/main.go new file mode 100644 index 0000000..91a6bda --- /dev/null +++ b/cli/main.go @@ -0,0 +1,12 @@ +package main + +import ( + _ "github.com/mycontroller-org/esphome_api/cli/command/device" + rootCmd "github.com/mycontroller-org/esphome_api/cli/command/root" + + clientTY "github.com/mycontroller-org/server/v2/pkg/types/client" +) + +func main() { + rootCmd.Execute(clientTY.NewStdStreams()) +} diff --git a/cli/types/config.go b/cli/types/config.go new file mode 100644 index 0000000..7c84255 --- /dev/null +++ b/cli/types/config.go @@ -0,0 +1,80 @@ +package types + +import ( + "encoding/base64" + "fmt" + "log" + "strings" + "time" +) + +const ( + EncodePrefix = "BASE64/" +) + +type Config struct { + Active string `yaml:"active"` + Devices []DeviceConfig `yaml:"devices"` +} + +type DeviceInfo struct { + Name string `yaml:"name"` + Model string `yaml:"model"` + MacAddress string `yaml:"macAddress"` + EsphomeVersion string `yaml:"esphomeVersion"` + CompilationTime string `yaml:"compilationTime"` + UsesPassword bool `yaml:"usesPassword"` + HasDeepSleep bool `yaml:"hasDeepSleep"` + StatusOn time.Time `yaml:"statusOn"` +} + +func (di *DeviceInfo) Clone() DeviceInfo { + return DeviceInfo{ + Name: di.Name, + Model: di.Model, + MacAddress: di.MacAddress, + EsphomeVersion: di.EsphomeVersion, + CompilationTime: di.CompilationTime, + UsesPassword: di.UsesPassword, + HasDeepSleep: di.HasDeepSleep, + } +} + +type DeviceConfig struct { + Address string `yaml:"address"` + Password string `yaml:"password"` // encode as base64 + EncryptionKey string `yaml:"encryptionKey"` + Timeout time.Duration `yaml:"timeout"` + Info DeviceInfo `yaml:"info"` +} + +func (dc *DeviceConfig) Clone() DeviceConfig { + return DeviceConfig{ + Address: dc.Address, + Password: dc.Password, + EncryptionKey: dc.EncryptionKey, + Timeout: dc.Timeout, + Info: dc.Info.Clone(), + } +} + +// GetPassword decodes and returns the password +func (nc *DeviceConfig) GetPassword() string { + if strings.HasPrefix(nc.Password, EncodePrefix) { + password := strings.Replace(nc.Password, EncodePrefix, "", 1) + decodedPassword, err := base64.StdEncoding.DecodeString(password) + if err != nil { + log.Fatal("error on decoding the password", err) + } + return string(decodedPassword) + } + return nc.Password +} + +// EncodePassword encodes and update the password +func (nc *DeviceConfig) EncodePassword() { + if nc.Password != "" && !strings.HasPrefix(nc.Password, EncodePrefix) { + encodedPassword := base64.StdEncoding.EncodeToString([]byte(nc.Password)) + nc.Password = fmt.Sprintf("%s%s", EncodePrefix, encodedPassword) + } +} diff --git a/cli/version/version.go b/cli/version/version.go new file mode 100644 index 0000000..455e789 --- /dev/null +++ b/cli/version/version.go @@ -0,0 +1,42 @@ +package version + +import ( + "fmt" + "runtime" +) + +var ( + gitCommit string + version string + buildDate string +) + +// Version holds version data +type Version struct { + Version string `json:"version"` + GitCommit string `json:"gitCommit"` + BuildDate string `json:"buildDate"` + GoVersion string `json:"goVersion"` + Compiler string `json:"compiler"` + Platform string `json:"platform"` + Arch string `json:"arch"` +} + +// Get returns the Version object +func Get() *Version { + return &Version{ + GitCommit: gitCommit, + Version: version, + BuildDate: buildDate, + GoVersion: runtime.Version(), + Compiler: runtime.Compiler, + Platform: runtime.GOOS, + Arch: runtime.GOARCH, + } +} + +func (v *Version) String() string { + return fmt.Sprintf("{version:%s, gitCommit:%s, buildDate:%s, goVersion:%s, compiler:%s, platform:%s, arch:%s}", + v.Version, v.GitCommit, v.BuildDate, v.GoVersion, v.Compiler, v.Platform, v.Arch, + ) +} diff --git a/go.mod b/go.mod index 0f21275..e6ee827 100644 --- a/go.mod +++ b/go.mod @@ -1,13 +1,40 @@ module github.com/mycontroller-org/esphome_api -go 1.18 +go 1.21 require ( github.com/flynn/noise v1.0.1-0.20220214164934-d803f5c4b0f4 - google.golang.org/protobuf v1.28.0 + github.com/mitchellh/go-homedir v1.1.0 + github.com/mycontroller-org/server/v2 v2.0.1-0.20240615005612-617ab65354e9 + github.com/spf13/cobra v1.8.1 + github.com/spf13/viper v1.19.0 + google.golang.org/protobuf v1.34.2 + gopkg.in/yaml.v3 v3.0.1 ) require ( - golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect - golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mattn/go-runewidth v0.0.9 // indirect + github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c // indirect + github.com/nleeper/goment v1.4.4 // indirect + github.com/olekukonko/tablewriter v0.0.5 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + github.com/tkuchiki/go-timezone v0.2.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/crypto v0.24.0 // indirect + golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect ) diff --git a/go.sum b/go.sum index 1595ca5..1e97f88 100644 --- a/go.sum +++ b/go.sum @@ -1,25 +1,106 @@ +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/flynn/noise v1.0.1-0.20220214164934-d803f5c4b0f4 h1:6pcIWmKkQZdpPjs/pD9OLt0NwftBozNE0Nm5zMCG2C4= github.com/flynn/noise v1.0.1-0.20220214164934-d803f5c4b0f4/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c h1:cqn374mizHuIWj+OSJCajGr/phAmuMug9qIX3l9CflE= +github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mycontroller-org/server/v2 v2.0.1-0.20240615005612-617ab65354e9 h1:vdDJn/Xn0zCG4rDwtdvU9NTGfqqDUvCcTiutObGsTT4= +github.com/mycontroller-org/server/v2 v2.0.1-0.20240615005612-617ab65354e9/go.mod h1:tWt8BkFKFNOVjm6Yak+RYmHknaRtBFaQwlI09N0q84U= +github.com/nleeper/goment v1.4.4 h1:GlMTpxvhueljArSunzYjN9Ri4SOmpn0Vh2hg2z/IIl8= +github.com/nleeper/goment v1.4.4/go.mod h1:zDl5bAyDhqxwQKAvkSXMRLOdCowrdZz53ofRJc4VhTo= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= +github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/tkuchiki/go-timezone v0.2.0 h1:yyZVHtQRVZ+wvlte5HXvSpBkR0dPYnPEIgq9qqAqltk= +github.com/tkuchiki/go-timezone v0.2.0/go.mod h1:b1Ean9v2UXtxSq4TZF0i/TU9NuoWa9hOzOKoGCV2zqY= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc h1:ao2WRsKSzW6KuUY9IWPwWahcHCgR0s52IfwutMfEbdM= +golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/client/client.go b/pkg/client/client.go index 5f8a951..59e46bc 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -23,21 +23,21 @@ type Client struct { waitMapMutex sync.RWMutex waitMap map[uint64]chan proto.Message lastMessageAt time.Time - handlerFunc func(proto.Message) + callBackFunc types.CallBackFunc CommunicationTimeout time.Duration apiConn connection.ApiConnection } // GetClient returns esphome api client -func GetClient(clientID, address, encryptionKey string, timeout time.Duration, handlerFunc func(proto.Message)) (*Client, error) { +func GetClient(clientID, address, encryptionKey string, timeout time.Duration, callBackFunc types.CallBackFunc) (*Client, error) { conn, err := net.DialTimeout("tcp", address, timeout) if err != nil { return nil, err } // add noop func, if handler not defined - if handlerFunc == nil { - handlerFunc = func(msg proto.Message) {} + if callBackFunc == nil { + callBackFunc = func(msg proto.Message) {} } apiConn, err := connection.GetConnection(conn, timeout, encryptionKey) @@ -51,7 +51,7 @@ func GetClient(clientID, address, encryptionKey string, timeout time.Duration, h reader: bufio.NewReader(conn), waitMap: make(map[uint64]chan proto.Message), stopChan: make(chan bool), - handlerFunc: handlerFunc, + callBackFunc: callBackFunc, CommunicationTimeout: timeout, apiConn: apiConn, } @@ -204,8 +204,8 @@ func (c *Client) getMessage() error { if c.handleInternal(message) { return nil } else if c.isExternal(message) { - if c.handlerFunc != nil { - c.handlerFunc(message) + if c.callBackFunc != nil { + c.callBackFunc(message) return nil } } diff --git a/pkg/types/types.go b/pkg/types/types.go index 6a45ae5..2bdd7f6 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -15,6 +15,9 @@ var ( ErrConnRequireEncryption = errors.New("esphome_api: connection requires encryption") ) +// call back function used to report received messages +type CallBackFunc func(proto.Message) + // DeviceInfo struct type DeviceInfo struct { Name string diff --git a/scripts/generate_executables.sh b/scripts/generate_executables.sh new file mode 100755 index 0000000..73e756d --- /dev/null +++ b/scripts/generate_executables.sh @@ -0,0 +1,68 @@ +#!/bin/bash + +# this script used to generate binary files +# should be executed from the root locations of the repository + + +source ./scripts/version.sh + +BUILD_DIR=builds +BINARY_DIR=binary +# clean builds directory +rm ${BUILD_DIR}/* -rf + + +# create directories +mkdir -p ${BUILD_DIR}/${BINARY_DIR} + +# download dependencies +go mod tidy + + +function package { + local PACKAGE_STAGING_DIR=$1 + local BINARY_FILE=$2 + local FILE_EXTENSION=$3 + + mkdir -p ${PACKAGE_STAGING_DIR} + + # echo "Package dir: ${PACKAGE_STAGING_DIR}" + cp ${BUILD_DIR}/${BINARY_DIR}/${BINARY_FILE} ${PACKAGE_STAGING_DIR}/esphomectl${FILE_EXTENSION} + + # copy license + cp LICENSE ${PACKAGE_STAGING_DIR}/LICENSE.txt + + if [[ ${PACKAGE_STAGING_DIR} =~ "windows" ]]; then + ARCHIVE_NAME="${PACKAGE_STAGING_DIR}.zip" + zip -r ${BUILD_DIR}/${ARCHIVE_NAME} ${PACKAGE_STAGING_DIR} + else + ARCHIVE_NAME="${PACKAGE_STAGING_DIR}.tar.gz" + tar -czf ${BUILD_DIR}/${ARCHIVE_NAME} ${PACKAGE_STAGING_DIR} + fi + rm ${PACKAGE_STAGING_DIR} -rf +} + +# platforms to build +PLATFORMS=("linux/arm" "linux/arm64" "linux/386" "linux/amd64" "windows/386" "windows/amd64") + +# compile +for platform in "${PLATFORMS[@]}" +do + platform_raw=(${platform//\// }) + GOOS=${platform_raw[0]} + GOARCH=${platform_raw[1]} + package_name="esphomectl-${GOOS}-${GOARCH}" + + env GOOS=${GOOS} GOARCH=${GOARCH} go build -o ${BUILD_DIR}/${BINARY_DIR}/${package_name} -ldflags "-s -w $LD_FLAGS" cli/main.go + if [ $? -ne 0 ]; then + echo 'an error has occurred. aborting the build process' + exit 1 + fi + + FILE_EXTENSION="" + if [ $GOOS = "windows" ]; then + FILE_EXTENSION='.exe' + fi + + package esphomectl-${VERSION}-${GOOS}-${GOARCH} ${package_name} ${FILE_EXTENSION} +done diff --git a/scripts/version.sh b/scripts/version.sh new file mode 100755 index 0000000..9574e47 --- /dev/null +++ b/scripts/version.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# this script updateds version information + +# version details +export BUILD_DATE=`date -u +'%Y-%m-%dT%H:%M:%S%:z'` +export GIT_BRANCH=`git rev-parse --abbrev-ref HEAD` +export GIT_SHA=`git rev-parse HEAD` +export GIT_SHA_SHORT=`git rev-parse --short HEAD` +export VERSION_PKG="github.com/mycontroller-org/esphome_api/cli/version" + +# update tag, if available +if [ ${GIT_BRANCH} = "HEAD" ]; then + export GIT_BRANCH=`git describe --abbrev=0 --tags` +fi + +# update version number +export VERSION=`echo ${GIT_BRANCH} | awk 'match($0, /([0-9]*\.[0-9]*)$/) { print substr($0, RSTART, RLENGTH) }'` +if [ -z "$VERSION" ]; then + # takes version from versions file and adds devel suffix with that + STATIC_VERSION=`grep esphomectl= versions.txt | awk -F= '{print $2}'` + export VERSION="${STATIC_VERSION}-devel" +fi + +export LD_FLAGS="-X $VERSION_PKG.version=$VERSION -X $VERSION_PKG.buildDate=$BUILD_DATE -X $VERSION_PKG.gitCommit=$GIT_SHA" diff --git a/versions.txt b/versions.txt new file mode 100644 index 0000000..3570c54 --- /dev/null +++ b/versions.txt @@ -0,0 +1,2 @@ +# this version number will be used for development builds +esphomectl=1.4 \ No newline at end of file