Skip to content

Commit

Permalink
Merge pull request #43 from jagheterfredrik/movetogo
Browse files Browse the repository at this point in the history
move to go version
  • Loading branch information
tronikos authored Dec 23, 2023
2 parents 7f68f13 + 03f45a8 commit 0af1539
Show file tree
Hide file tree
Showing 26 changed files with 923 additions and 687 deletions.
25 changes: 25 additions & 0 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# This workflow will build a golang project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go

name: Go

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

jobs:

build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.20'

- name: Build
run: ./make.sh
166 changes: 9 additions & 157 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,160 +1,12 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# Test binary, built with `go test -c`
*.test

# C extensions
*.so
# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# make.sh output
bridge-armhf
bridge-arm64

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
.pybuilder/
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version

# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock

# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock

# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml

# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

# pytype static type analyzer
.pytype/

# Cython debug symbols
cython_debug/

# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https:/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
# Go workspace file
go.work
42 changes: 0 additions & 42 deletions .pre-commit-config.yaml

This file was deleted.

21 changes: 2 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,27 +21,10 @@ This open-source project connects your Wallbox fully locally to Home Assistant,

1. [Root your Wallbox](https:/jagheterfredrik/wallbox-pwn)
2. Setup an MQTT Broker, if you don't already have one. Here's an example [installing it as a Home Assistant add-on](https://www.youtube.com/watch?v=dqTn-Gk4Qeo)
3. Edit bridge.ini
- Set `host` the IP address of your MQTT broker
- Set `username` and `password` to match your broker setup
4. Copy the files in mqtt-bridge to your Wallbox.

On Windows you can use WinSCP.

On OS X/Linux this can be done using `scp -r /path/to/wallbox-mqtt-bridge/mqtt-bridge root@<wallbox-ip>:~`

You should end up with the following files in your Wallbox:
- `/home/root/mqtt-bridge/bridge.ini`
- `/home/root/mqtt-bridge/bridge.py`
- `/home/root/mqtt-bridge/install.sh`
- `/home/root/mqtt-bridge/mqtt-bridge.service`
- `/home/root/mqtt-bridge/requirements.txt`
5. `ssh` to your Wallbox and run the installer
3. `ssh` to your Wallbox and run

```sh
cd mqtt-bridge
chmod +x install.sh
./install.sh
curl -sSfL https:/jagheterfredrik/wallbox-mqtt-bridge/releases/download/bridge/install.sh > install.sh && bash install.sh
```

## Acknowledgments
Expand Down
127 changes: 127 additions & 0 deletions app/bridge.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package bridge

import (
"encoding/json"
"fmt"
"os"
"os/signal"
"strings"
"syscall"
"time"

"github.com/eclipse/paho.mqtt.golang"
"github.com/jagheterfredrik/wallbox-mqtt-bridge/app/ratelimit"
"github.com/jagheterfredrik/wallbox-mqtt-bridge/app/wallbox"
)

var connectLostHandler mqtt.ConnectionLostHandler = func(client mqtt.Client, err error) {
panic("Connection to MQTT lost")
}

func LaunchBridge(configPath string) {
c := LoadConfig(configPath)
w := wallbox.New()
w.RefreshData()

serialNumber := w.SerialNumber()
entityConfig := getEntities(w)
if c.Settings.DebugSensors {
for k, v := range getDebugEntities(w) {
entityConfig[k] = v
}
}

topicPrefix := "wallbox_" + serialNumber
availabilityTopic := topicPrefix + "/availability"

opts := mqtt.NewClientOptions()
opts.AddBroker(fmt.Sprintf("tcp://%s:%d", c.MQTT.Host, c.MQTT.Port))
opts.SetUsername(c.MQTT.Username)
opts.SetPassword(c.MQTT.Password)
opts.SetWill(availabilityTopic, "offline", 1, true)
opts.OnConnectionLost = connectLostHandler

client := mqtt.NewClient(opts)
if token := client.Connect(); token.Wait() && token.Error() != nil {
panic(token.Error())
}

for key, val := range entityConfig {
component := val.Component
uid := serialNumber + "_" + key
config := map[string]interface{}{
"~": topicPrefix + "/" + key,
"availability_topic": availabilityTopic,
"state_topic": "~/state",
"unique_id": uid,
"device": map[string]string{
"identifiers": serialNumber,
"name": c.Settings.DeviceName,
},
}
if val.Setter != nil {
config["command_topic"] = "~/set"
}
for k, v := range val.Config {
config[k] = v
}
jsonPayload, _ := json.Marshal(config)
token := client.Publish("homeassistant/"+component+"/"+uid+"/config", 1, true, jsonPayload)
token.Wait()
}

token := client.Publish(availabilityTopic, 1, true, "online")
token.Wait()

messageHandler := func(client mqtt.Client, msg mqtt.Message) {
field := strings.Split(msg.Topic(), "/")[1]
payload := string(msg.Payload())
setter := entityConfig[field].Setter
fmt.Println("Setting", field, payload)
setter(payload)
}

topic := topicPrefix + "/+/set"
client.Subscribe(topic, 1, messageHandler)

ticker := time.NewTicker(time.Duration(c.Settings.PollingIntervalSeconds) * time.Second)
defer ticker.Stop()

published := make(map[string]interface{})
rateLimiter := map[string]*ratelimit.DeltaRateLimit{
"charging_power": ratelimit.NewDeltaRateLimit(10, 100),
"added_energy": ratelimit.NewDeltaRateLimit(10, 50),
}

for {
select {
case <-ticker.C:
w.RefreshData()
for key, val := range entityConfig {
payload := val.Getter()
bytePayload := []byte(fmt.Sprint(payload))
if published[key] != payload {
if rate, ok := rateLimiter[key]; ok && !rate.Allow(strToFloat(payload)) {
continue
}
fmt.Println("Publishing: ", key, payload)
token := client.Publish(topicPrefix+"/"+key+"/state", 1, true, bytePayload)
token.Wait()
published[key] = payload
}
}
case <-interrupt():
fmt.Println("Interrupted. Exiting...")
token := client.Publish(availabilityTopic, 1, true, "offline")
token.Wait()
client.Disconnect(250)
os.Exit(0)
}
}
}

func interrupt() <-chan os.Signal {
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM)
return interrupt
}
Loading

0 comments on commit 0af1539

Please sign in to comment.