Skip to content

Commit

Permalink
feat: implemented in go to reduce memory amount
Browse files Browse the repository at this point in the history
  • Loading branch information
philipparndt committed Oct 4, 2024
1 parent 5ba74ed commit 376f5c7
Show file tree
Hide file tree
Showing 8 changed files with 333 additions and 0 deletions.
12 changes: 12 additions & 0 deletions .run/run.run.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="run" type="GoApplicationRunConfiguration" factoryName="Go Application">
<module name="miele-to-mqtt-gw" />
<working_directory value="$PROJECT_DIR$/v2" />
<parameters value="../production/config/config.json" />
<kind value="PACKAGE" />
<package value="github.com/mqtt-home/eltako-to-mqtt-gw" />
<directory value="$PROJECT_DIR$" />
<filePath value="$PROJECT_DIR$/v2/main.go" />
<method v="2" />
</configuration>
</component>
55 changes: 55 additions & 0 deletions v2/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package config

import (
"encoding/json"
"github.com/philipparndt/go-logger"
"github.com/philipparndt/mqtt-gateway/config"
"os"
)

type Config struct {
MQTT config.MQTTConfig `json:"mqtt"`
Eltako Eltako `json:"eltako"`
LogLevel string `json:"loglevel,omitempty"`
}

type Device struct {
Ip string `json:"ip"`
Username string `json:"username"`
Password string `json:"password"`
Name string `json:"name"`
BlindsConfig struct {
HalfOpenPercentage float64 `json:"halfOpenPercentage"`
} `json:"blindsConfig"`
}

type Eltako struct {
Devices []Device `json:"devices"`
PollingInterval int `json:"polling-interval"`
}

func LoadConfig(file string) (Config, error) {
data, err := os.ReadFile(file)
if err != nil {
logger.Error("Error reading config file", err)
return Config{}, err
}

data = config.ReplaceEnvVariables(data)

// Create a Config object
var cfg Config

// Unmarshal the JSON data into the Config object
err = json.Unmarshal(data, &cfg)
if err != nil {
logger.Error("Unmarshalling JSON:", err)
return Config{}, err
}

if cfg.LogLevel == "" {
cfg.LogLevel = "info"
}

return cfg, nil
}
64 changes: 64 additions & 0 deletions v2/eltako/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package eltako

import (
"crypto/tls"
"io"
"net/http"
)

type HTTPClient struct {
BaseURL string
Client *http.Client
AuthToken string
}

func NewHTTPClient(baseURL string) *HTTPClient {
// Create a http.Client that ignores the certificate
// errors. This is necessary because the Eltako devices
// use self-signed certificates.
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}

return &HTTPClient{
BaseURL: baseURL,
Client: &http.Client{
Transport: tr,
},
}
}

func (c *HTTPClient) SetAuthToken(token string) {
c.AuthToken = token
}

func (c *HTTPClient) GetAuthToken() string {
return c.AuthToken
}

func (c *HTTPClient) Get(url string) (*http.Response, error) {
return c.NewRequest("GET", url, nil)
}

func (c *HTTPClient) Post(url string, body io.Reader) (*http.Response, error) {
req, err := http.NewRequest("POST", c.BaseURL+url, body)
if err != nil {
return nil, err
}

return c.Client.Do(req)
}

func (c *HTTPClient) NewRequest(method, url string, body io.Reader) (*http.Response, error) {
req, err := http.NewRequest(method, c.BaseURL+url, body)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")

if c.AuthToken != "" {
req.Header.Set("Authorization", c.AuthToken)
}

return c.Client.Do(req)
}
16 changes: 16 additions & 0 deletions v2/eltako/device.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package eltako

type Data struct {
Type string `json:"type"`
Identifier string `json:"identifier"`
Value interface{} `json:"value"`
}

type Device struct {
DeviceGuid string `json:"deviceGuid"`
ProductGuid string `json:"productGuid"`
DisplayName string `json:"displayName"`
Infos []Data `json:"infos"`
Settings []Data `json:"settings"`
Functions []Data `json:"functions"`
}
97 changes: 97 additions & 0 deletions v2/eltako/eltako.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package eltako

import (
"bytes"
"encoding/json"
"fmt"
"github.com/mqtt-home/eltako-to-mqtt-gw/config"
"io"
"net/http"
)

type ShadingActor struct {
device config.Device
client *HTTPClient
Devices []Device
}

func NewShadingActor(device config.Device) *ShadingActor {
client := NewHTTPClient("https://" + device.Ip + ":443/api/v0")
actor := &ShadingActor{
device: device,
client: client,
}
err := actor.init()
if err != nil {
panic(err)
}
return actor
}

func (s *ShadingActor) init() error {
err := s.UpdateToken()
if err != nil {
return err
}
devices, err := s.getDevices()
s.Devices = devices
return nil
}

func (s *ShadingActor) UpdateToken() error {
usernamePassword := map[string]string{
"user": s.device.Username,
"password": s.device.Password,
}

body, err := json.Marshal(usernamePassword)
if err != nil {
return err
}

resp, err := s.client.Post("/login", bytes.NewReader(body))
if err != nil {
return err
}
defer func(Body io.ReadCloser) {
_ = Body.Close()
}(resp.Body)

if resp.StatusCode != http.StatusOK {
return fmt.Errorf("failed to update token, status code: %d", resp.StatusCode)
}

var result map[string]string
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return err
}

token, ok := result["apiKey"]
if !ok {
return fmt.Errorf("apiKey not found in response")
}

s.client.SetAuthToken(token)
return nil
}

func (s *ShadingActor) getDevices() ([]Device, error) {
resp, err := s.client.Get("/devices")
if err != nil {
return nil, err
}
defer func(Body io.ReadCloser) {
_ = Body.Close()
}(resp.Body)

if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to get devices, status code: %d", resp.StatusCode)
}

var devices []Device
if err := json.NewDecoder(resp.Body).Decode(&devices); err != nil {
return nil, err
}

return devices, nil
}
15 changes: 15 additions & 0 deletions v2/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module github.com/mqtt-home/eltako-to-mqtt-gw

go 1.22.2

require (
github.com/philipparndt/go-logger v1.0.0
github.com/philipparndt/mqtt-gateway v1.3.0
)

require (
github.com/eclipse/paho.mqtt.golang v1.4.3 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
golang.org/x/net v0.8.0 // indirect
golang.org/x/sync v0.1.0 // indirect
)
12 changes: 12 additions & 0 deletions v2/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
github.com/eclipse/paho.mqtt.golang v1.4.3 h1:2kwcUGn8seMUfWndX0hGbvH8r7crgcJguQNCyp70xik=
github.com/eclipse/paho.mqtt.golang v1.4.3/go.mod h1:CSYvoAlsMkhYOXh/oKyxa8EcBci6dVkLCbo5tTC1RIE=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/philipparndt/go-logger v1.0.0 h1:5khKl1q8q6FH74S1wF5KPl6DvdZnRdPKqKbBPQDr9P4=
github.com/philipparndt/go-logger v1.0.0/go.mod h1:4mWf8eNH82GhKl5byAFwXcux9WPNuaePaPaAFbgjHXg=
github.com/philipparndt/mqtt-gateway v1.3.0 h1:QeJ0XIEnlzH4Qm7vqAQN3WTrI45UcPxlcQFcTrCXvn8=
github.com/philipparndt/mqtt-gateway v1.3.0/go.mod h1:N6LC57xFs/u41Xc5csPHTACm/00GyOyMgIC+oOqp32Y=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
62 changes: 62 additions & 0 deletions v2/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package main

import (
"github.com/mqtt-home/eltako-to-mqtt-gw/config"
"github.com/mqtt-home/eltako-to-mqtt-gw/eltako"
"github.com/philipparndt/go-logger"
"github.com/philipparndt/mqtt-gateway/mqtt"
"os"
"os/signal"
"syscall"
)

func main() {
if len(os.Args) < 2 {
logger.Error("No config file specified")
os.Exit(1)
}

configFile := os.Args[1]
logger.Info("Config file", configFile)
err := error(nil)

cfg, err := config.LoadConfig(configFile)
if err != nil {
logger.Error("Failed loading config", err)
return
}

logger.Info("Config file", cfg)

for _, device := range cfg.Eltako.Devices {
logger.Info("Device", device)
actor := eltako.NewShadingActor(device)
//actor.Start()
err := actor.UpdateToken()
if err != nil {
logger.Error("Failed updating token", err)
return
}

for _, device := range actor.Devices {
logger.Info("Device", device)
}
}

//api = nuki.New(cfg.Nuki.Target.Web.SmartlockId, cfg.Nuki.Target.Web.Bearer)

logger.SetLevel(cfg.LogLevel)
mqtt.Start(cfg.MQTT, "eltako_mqtt")

//mqtt.Subscribe(cfg.Nuki.Source, OnMessage)
//mqtt.Subscribe(cfg.Nuki.Fingerprint, OnFingerprintButton)
//mqtt.Subscribe(cfg.MQTT.Topic+"/fingerprint_state", OnFingerprintState)

logger.Info("Application is now ready. Press Ctrl+C to quit.")

quitChannel := make(chan os.Signal, 1)
signal.Notify(quitChannel, syscall.SIGINT, syscall.SIGTERM)
<-quitChannel

logger.Info("Received quit signal")
}

0 comments on commit 376f5c7

Please sign in to comment.