From 088a15975c1e64fdfb0ebd7b4cef368ec1817462 Mon Sep 17 00:00:00 2001 From: Michael Lehmann Date: Tue, 18 Feb 2025 21:06:52 +0100 Subject: [PATCH] Inital --- .envrc | 3 ++ .gitignore | 9 ++++ config/config.go | 32 +++++++++++++ config/data.go | 24 ++++++++++ controller/controller.go | 66 ++++++++++++++++++++++++++ controller/data.go | 17 +++++++ devenv.lock | 100 +++++++++++++++++++++++++++++++++++++++ devenv.nix | 47 ++++++++++++++++++ devenv.yaml | 15 ++++++ go.mod | 5 ++ go.sum | 3 ++ main.go | 28 +++++++++++ misc/sample-config.yml | 12 +++++ 13 files changed, 361 insertions(+) create mode 100644 .envrc create mode 100644 config/config.go create mode 100644 config/data.go create mode 100644 controller/controller.go create mode 100644 controller/data.go create mode 100644 devenv.lock create mode 100644 devenv.nix create mode 100644 devenv.yaml create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go create mode 100644 misc/sample-config.yml diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..894571b --- /dev/null +++ b/.envrc @@ -0,0 +1,3 @@ +source_url "https://raw.githubusercontent.com/cachix/devenv/82c0147677e510b247d8b9165c54f73d32dfd899/direnvrc" "sha256-7u4iDd1nZpxL4tCzmPG0dQgC5V+/44Ba+tHkPob1v2k=" + +use devenv diff --git a/.gitignore b/.gitignore index 6f72f89..178b678 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,12 @@ go.work.sum # env file .env +# Devenv +.devenv* +devenv.local.nix + +# direnv +.direnv + +# pre-commit +.pre-commit-config.yaml diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..3ffa32b --- /dev/null +++ b/config/config.go @@ -0,0 +1,32 @@ +package config + +import ( + "fmt" + "os" + + "gopkg.in/yaml.v2" +) + +// LoadConfig liest die YAML-Konfigurationsdatei und parst sie in ein Config-Struct. +func loadConfig(path string) (*Config, error) { + data, err := os.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("fehler beim Lesen der Datei: %w", err) + } + + var cfg Config + if err := yaml.Unmarshal(data, &cfg); err != nil { + return nil, fmt.Errorf("fehler beim Parsen der YAML-Daten: %w", err) + } + return &cfg, nil +} + +func GetConfig() (*Config, error) { + config, err := loadConfig("misc/sample-config.yml") + if err != nil { + return nil, fmt.Errorf("unable to read config: %w", err) + } + + return config, nil + +} diff --git a/config/data.go b/config/data.go new file mode 100644 index 0000000..a541f4d --- /dev/null +++ b/config/data.go @@ -0,0 +1,24 @@ +package config + +// Config bildet die Struktur der YAML-Konfiguration ab. +type Config struct { + Defaults Defaults `yaml:"defaults"` + Groups map[string]GroupDef `yaml:"groups"` +} + +// Defaults enthält Standardwerte. +type Defaults struct { + Temperatur int `yaml:"temperatur"` + Dimming int `yaml:"dimming"` +} + +// GroupDef enthält die Details einer Gruppe, in diesem Fall eine Liste von Bulps. +type GroupDef struct { + Bulps []Bulp `yaml:"bulps"` +} + +// Bulp repräsentiert ein einzelnes Gerät. +type Bulp struct { + IP string `yaml:"ip"` + Port string `yaml:"port"` +} diff --git a/controller/controller.go b/controller/controller.go new file mode 100644 index 0000000..278663f --- /dev/null +++ b/controller/controller.go @@ -0,0 +1,66 @@ +package controller + +import ( + "encoding/json" + "fmt" + "net" + "time" +) + +// TODO Implement wiz functions. +// https://dev.to/santosh/how-to-control-philips-wiz-bulb-using-go-2ad9 + +func TurnOn(ip string, port string, temperature int, dimming int) { + c, err := net.Dial("udp", fmt.Sprintf("%s:%s", ip, port)) + if err != nil { + panic("Unable to connect to light bulp!") + } + + c.Write([]byte(`{"method": "setPilot", "params":{"state": true, "temp": temperature, "dimming": dimming}}`)) +} + +func TurnOff(ip string, port string) { + c, err := net.Dial("udp", fmt.Sprintf("%s:%s", ip, port)) + if err != nil { + panic("Unable to connect to light bulp!") + } + + c.Write([]byte(`{"method": "setPilot", "params":{"state": false}}`)) +} + +func GetStatus(ip string, port string) (*StatusResponse, error) { + // Verbindung per UDP herstellen + conn, err := net.Dial("udp", fmt.Sprintf("%s:%s", ip, port)) + if err != nil { + return nil, fmt.Errorf("unable to connect to light bulb: %w", err) + } + defer conn.Close() + + // Senden der Anfrage + request := []byte(`{"method": "getPilot", "params":{}}`) + _, err = conn.Write(request) + if err != nil { + return nil, fmt.Errorf("failed to send request: %w", err) + } + + // Setzen eines Lese-Timeouts, da UDP keine Garantie für Antwortlieferung gibt. + deadline := time.Now().Add(3 * time.Second) + if err := conn.SetReadDeadline(deadline); err != nil { + return nil, fmt.Errorf("failed to set read deadline: %w", err) + } + + // Buffer, um die Antwort zu lesen + buffer := make([]byte, 2048) + n, err := conn.Read(buffer) + if err != nil { + return nil, fmt.Errorf("failed to read response: %w", err) + } + + // Parsen der JSON-Antwort in das Struct + var status StatusResponse + if err := json.Unmarshal(buffer[:n], &status); err != nil { + return nil, fmt.Errorf("failed to parse response: %w", err) + } + + return &status, nil +} diff --git a/controller/data.go b/controller/data.go new file mode 100644 index 0000000..ad318fc --- /dev/null +++ b/controller/data.go @@ -0,0 +1,17 @@ +package controller + +type StatusResponse struct { + Method string `json:"method"` + Env string `json:"env"` + Result PilotDetails `json:"result"` +} + +type PilotDetails struct { + Mac string `json:"mac"` + RSSI int `json:"rssi"` + Src string `json:"src"` + State bool `json:"state"` + SceneId int `json:"sceneId"` + Temp int `json:"temp"` + Dimming int `json:"dimming"` +} diff --git a/devenv.lock b/devenv.lock new file mode 100644 index 0000000..eec00f3 --- /dev/null +++ b/devenv.lock @@ -0,0 +1,100 @@ +{ + "nodes": { + "devenv": { + "locked": { + "dir": "src/modules", + "lastModified": 1739444039, + "owner": "cachix", + "repo": "devenv", + "rev": "1235cd13f47df6ad19c8a183c6eabc1facb7c399", + "type": "github" + }, + "original": { + "dir": "src/modules", + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1733328505, + "owner": "edolstra", + "repo": "flake-compat", + "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1733477122, + "owner": "cachix", + "repo": "devenv-nixpkgs", + "rev": "7bd9e84d0452f6d2e63b6e6da29fe73fac951857", + "type": "github" + }, + "original": { + "owner": "cachix", + "ref": "rolling", + "repo": "devenv-nixpkgs", + "type": "github" + } + }, + "pre-commit-hooks": { + "inputs": { + "flake-compat": "flake-compat", + "gitignore": "gitignore", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1737465171, + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "9364dc02281ce2d37a1f55b6e51f7c0f65a75f17", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "root": { + "inputs": { + "devenv": "devenv", + "nixpkgs": "nixpkgs", + "pre-commit-hooks": "pre-commit-hooks" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/devenv.nix b/devenv.nix new file mode 100644 index 0000000..cd841a1 --- /dev/null +++ b/devenv.nix @@ -0,0 +1,47 @@ +{ + pkgs, + lib, + config, + inputs, + ... +}: { + # https://devenv.sh/basics/ + env.GREET = "WIZ Controller"; + + # https://devenv.sh/packages/ + packages = [pkgs.nmap]; + + # https://devenv.sh/languages/ + languages.go.enable = true; + + # https://devenv.sh/processes/ + # processes.cargo-watch.exec = "cargo-watch"; + + # https://devenv.sh/services/ + # services.postgres.enable = true; + + # https://devenv.sh/scripts/ + scripts.hello.exec = '' + echo hello from $GREET + ''; + + enterShell = '' + hello + ''; + + # https://devenv.sh/tasks/ + # tasks = { + # "myproj:setup".exec = "mytool build"; + # "devenv:enterShell".after = [ "myproj:setup" ]; + # }; + + # https://devenv.sh/tests/ + enterTest = '' + echo "Running tests" + ''; + + # https://devenv.sh/pre-commit-hooks/ + # pre-commit.hooks.shellcheck.enable = true; + + # See full reference at https://devenv.sh/reference/options/ +} diff --git a/devenv.yaml b/devenv.yaml new file mode 100644 index 0000000..116a2ad --- /dev/null +++ b/devenv.yaml @@ -0,0 +1,15 @@ +# yaml-language-server: $schema=https://devenv.sh/devenv.schema.json +inputs: + nixpkgs: + url: github:cachix/devenv-nixpkgs/rolling + +# If you're using non-OSS software, you can set allowUnfree to true. +# allowUnfree: true + +# If you're willing to use a package that's vulnerable +# permittedInsecurePackages: +# - "openssl-1.1.1w" + +# If you have more than one devenv you can merge them +#imports: +# - ./backend diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..fae20b1 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module kattudden/wiz-controller + +go 1.23.3 + +require gopkg.in/yaml.v2 v2.4.0 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..7534661 --- /dev/null +++ b/go.sum @@ -0,0 +1,3 @@ +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/main.go b/main.go new file mode 100644 index 0000000..d702250 --- /dev/null +++ b/main.go @@ -0,0 +1,28 @@ +package main + +import ( + "fmt" + "kattudden/wiz-controller/config" + "kattudden/wiz-controller/controller" +) + +func main() { + config, err := config.GetConfig() + if err != nil { + fmt.Println(err) + return + } + + for groupName, group := range config.Groups { + fmt.Println("Gruppe:", groupName) + + for _, bulp := range group.Bulps { + fmt.Printf(" Bulp: ip: %s, port: %s\n", bulp.IP, bulp.Port) + status, err := controller.GetStatus(bulp.IP, bulp.Port) + if err != nil { + fmt.Println(err) + } + fmt.Println("status:", status.Result.State) + } + } +} diff --git a/misc/sample-config.yml b/misc/sample-config.yml new file mode 100644 index 0000000..d2469d9 --- /dev/null +++ b/misc/sample-config.yml @@ -0,0 +1,12 @@ +defaults: + temperatur: 2700 + dimming: 100 +groups: + wohnzimmer: + bulps: + - ip: "192.168.0.151" + port: "38899" + - ip: "192.168.0.152" + port: "38899" + - ip: "192.168.0.153" + port: "38899" \ No newline at end of file