Skip to content

Instantly share code, notes, and snippets.

@grimpy
Created May 30, 2025 22:39
Show Gist options
  • Save grimpy/9a5a21bb71daf0b3acf225a6b4df2ec3 to your computer and use it in GitHub Desktop.
Save grimpy/9a5a21bb71daf0b3acf225a6b4df2ec3 to your computer and use it in GitHub Desktop.
package main
import (
"bytes"
"encoding/json"
"fmt"
"log"
"os"
"net/http"
"github.com/fsnotify/fsnotify"
"github.com/holoplot/go-evdev"
)
func send_to_ha(url string, token string, key string) {
data := map[string]string{
"key": key,
}
jsonData, err := json.Marshal(data)
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
if err != nil {
log.Printf("Failed to make request")
return
}
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Printf("Failed to post request\n")
return
}
defer resp.Body.Close()
log.Printf("Response status: %d\n", resp.StatusCode)
}
func readEvents(path string, keyEvents chan<- string) {
dev, err := evdev.Open(path)
if err != nil {
return
}
inputID, err := dev.InputID()
if err != nil {
log.Printf("Error loading input %s\n", path)
return
}
if inputID.Vendor != 0x4037 || inputID.Product != 0x2804 {
return
}
err = dev.Grab()
if err != nil {
log.Printf("Couldnt grab device %s\n", path)
return
}
name, err := dev.Name()
if err != nil {
log.Printf("Failed to read device name %s\n", path)
return
}
log.Printf("Reading events for %s\n", name)
for {
e, err := dev.ReadOne()
if err != nil {
log.Printf("Error reading from device: %v\n", err)
return
}
ts := fmt.Sprintf("Event: time %d.%06d", e.Time.Sec, e.Time.Usec)
switch e.Type {
case evdev.EV_KEY:
if e.Value == 1 {
log.Printf("%s, %s %d\n", ts, e.CodeName(), e.Value)
keyEvents <- e.CodeName()
}
}
}
}
func main() {
token := os.Getenv("HA_TOKEN")
url := os.Getenv("HA_URL")
if token == "" || url == "" {
log.Fatal("HA_TOKEN and HA_URL environment variables are required")
}
keyEvents := make(chan string)
devicePaths, err := evdev.ListDevicePaths()
if err != nil {
log.Fatalf("Cannot list device paths: %s\n", err)
return
}
for _, devPath := range devicePaths {
go readEvents(devPath.Path, keyEvents)
}
watcher, err := fsnotify.NewWatcher()
if err != nil {
log.Fatalf("Failed to create watches %s", err)
return
}
defer watcher.Close()
// Add a path.
err = watcher.Add("/dev/input")
if err != nil {
log.Fatal(err)
}
// Start listening for events.
for {
select {
case event, ok := <-watcher.Events:
if !ok {
return
}
if event.Has(fsnotify.Chmod) {
log.Printf("Permissions set for file: %s\n", event.Name)
go readEvents(event.Name, keyEvents)
}
case err, ok := <-watcher.Errors:
if !ok {
return
}
log.Println("error:", err)
case key, ok := <-keyEvents:
if !ok {
return
}
send_to_ha(url, token, key)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment