Skip to content

Commit

Permalink
Implement LED control for Home Assistant Green (#164)
Browse files Browse the repository at this point in the history
* Implement LED control for Home Assistant Green

Allow to control Home Assistant Green LEDs through D-Bus. Control the
state directly through sysfs.

* Fix linter issue
  • Loading branch information
agners authored Sep 5, 2023
1 parent 592c913 commit b92723f
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 1 deletion.
3 changes: 3 additions & 0 deletions boards/boards.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"github.com/godbus/dbus/v5/introspect"
"github.com/godbus/dbus/v5/prop"

"github.com/home-assistant/os-agent/boards/green"
"github.com/home-assistant/os-agent/boards/supervised"
"github.com/home-assistant/os-agent/boards/yellow"
logging "github.com/home-assistant/os-agent/utils/log"
Expand Down Expand Up @@ -70,6 +71,8 @@ func InitializeDBus(conn *dbus.Conn, board string) {
// Initialize the board
if board == "Yellow" {
yellow.InitializeDBus(conn)
} else if board == "Green" {
green.InitializeDBus(conn)
} else if board == "Supervised" {
supervised.InitializeDBus(conn)
} else {
Expand Down
120 changes: 120 additions & 0 deletions boards/green/green.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package green

import (
"github.com/godbus/dbus/v5"
"github.com/godbus/dbus/v5/introspect"
"github.com/godbus/dbus/v5/prop"

"github.com/home-assistant/os-agent/utils/led"
logging "github.com/home-assistant/os-agent/utils/log"
)

const (
objectPath = "/io/hass/os/Boards/Green"
ifaceName = "io.hass.os.Boards.Green"
)

var (
ledPower led.LED = led.LED{Name: "power", DefaultTrigger: "default-on"}
ledActivity led.LED = led.LED{Name: "activity", DefaultTrigger: "activity"}
ledUser led.LED = led.LED{Name: "user", DefaultTrigger: "heartbeat"}
)

type green struct {
conn *dbus.Conn
props *prop.Properties
}

func getTriggerLED(led led.LED) bool {
value, err := led.GetTrigger()
if err != nil {
logging.Error.Print(err)
}
return value != "none"
}

func setTriggerLED(led led.LED, c *prop.Change) *dbus.Error {
logging.Info.Printf("Set Green %s LED to %t", led.Name, c.Value)
err := led.SetTrigger(c.Value.(bool))
if err != nil {
return dbus.MakeFailedError(err)
}
return nil
}

func setPowerLED(c *prop.Change) *dbus.Error {
return setTriggerLED(ledPower, c)
}

func setActivityLED(c *prop.Change) *dbus.Error {
return setTriggerLED(ledActivity, c)
}

func setUserLED(c *prop.Change) *dbus.Error {
return setTriggerLED(ledUser, c)
}

func InitializeDBus(conn *dbus.Conn) {
d := green{
conn: conn,
}

// Init base value
ledPowerValue := getTriggerLED(ledPower)
ledActivityValue := getTriggerLED(ledActivity)
ledUserValue := getTriggerLED(ledUser)

propsSpec := map[string]map[string]*prop.Prop{
ifaceName: {
"PowerLED": {
Value: ledPowerValue,
Writable: true,
Emit: prop.EmitTrue,
Callback: setPowerLED,
},
"ActivityLED": {
Value: ledActivityValue,
Writable: true,
Emit: prop.EmitTrue,
Callback: setActivityLED,
},
"UserLED": {
Value: ledUserValue,
Writable: true,
Emit: prop.EmitTrue,
Callback: setUserLED,
},
},
}

props, err := prop.Export(conn, objectPath, propsSpec)
if err != nil {
logging.Critical.Panic(err)
}
d.props = props

err = conn.Export(d, objectPath, ifaceName)
if err != nil {
logging.Critical.Panic(err)
}

node := &introspect.Node{
Name: objectPath,
Interfaces: []introspect.Interface{
introspect.IntrospectData,
prop.IntrospectData,
{
Name: ifaceName,
Methods: introspect.Methods(d),
Properties: props.Introspection(ifaceName),
},
},
}

err = conn.Export(introspect.NewIntrospectable(node), objectPath, "org.freedesktop.DBus.Introspectable")
if err != nil {
logging.Critical.Panic(err)
}

logging.Info.Printf("Exposing object %s with interface %s ...", objectPath, ifaceName)
}
3 changes: 2 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ const (
)

var (
// version and baord are set at link time via -X flag
version string = "dev"
enableCapture bool = false
board string = "unknown"
enableCapture bool = false
)

func main() {
Expand Down
39 changes: 39 additions & 0 deletions utils/led/led.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package led

import (
"os"
"path/filepath"
"strings"
)

type LED struct {
Name string
DefaultTrigger string
}

func (led LED) GetTrigger() (string, error) {
ledTriggerFilePath := filepath.Join("/sys/class/leds/", led.Name, "trigger")
ledTrigger, err := os.ReadFile(ledTriggerFilePath)
if err != nil {
return "", err
}
return strings.TrimSpace(string(ledTrigger)), err
}

func (led LED) SetTrigger(newState bool) error {
ledTriggerFilePath := filepath.Join("/sys/class/leds/", led.Name, "trigger")
var newTrigger []byte

if newState {
newTrigger = []byte(led.DefaultTrigger)
} else {
newTrigger = []byte("none")
}

err := os.WriteFile(ledTriggerFilePath, newTrigger, 0600)
if err != nil {
return err
}

return nil
}

0 comments on commit b92723f

Please sign in to comment.