Skip to content

Commit

Permalink
feat(agent): arbitrary dbus commands via MQTT (thanks @jaynis!)
Browse files Browse the repository at this point in the history
  • Loading branch information
jaynis authored and joshuar committed Feb 26, 2024
1 parent 8a4acd5 commit 7204181
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 1 deletion.
23 changes: 23 additions & 0 deletions docs/mqtt.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,29 @@ There is a significant discrepancy in permissions between the device running Go

Go Hass Agent runs under a user account on a device. So the above controls will only work where that user has permissions to run the underlying actions on that device. Home Assistant does not currently offer any fine-grained access control for controls like the above. So any Home Assistant user will be able to run any of the controls. This means that a Home Assistant user not associated with the device user running the agent can use the exposed controls to issue potentially disruptive actions on a device that another user is accessing.

## Arbitrary D-BUS commands

The agent subscribes to the MQTT topic `gohassagent/dbus` on the configured MQTT broker and listens for
JSON messages of the below format, which will be accordingly dispatched to the systems
[d-bus](https://www.freedesktop.org/wiki/Software/dbus/).

```json
{
"bus": "session",
"path": "/org/cinnamon/ScreenSaver",
"method": "org.cinnamon.ScreenSaver.Lock",
"destination": "org.cinnamon.ScreenSaver",
"args": [
""
]
}
```

This can be used to trigger arbitrary d-bus commands on the system where the agent runs on,
by using any MQTT client such as Home Assistants
[`mqtt.publish`](https://www.home-assistant.io/integrations/mqtt/#service-mqttpublish) service.


## Implementation Details

### Linux
Expand Down
64 changes: 64 additions & 0 deletions internal/agent/mqtt_dbus.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright (c) 2024 Joshua Rich <[email protected]>
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT

package agent

import (
"context"
"encoding/json"

"github.com/godbus/dbus/v5"
"github.com/rs/zerolog/log"

"github.com/joshuar/go-hass-agent/pkg/linux/dbusx"

mqttapi "github.com/joshuar/go-hass-anything/v5/pkg/mqtt"

MQTT "github.com/eclipse/paho.mqtt.golang"
)

const (
dbus_mqtt_topic = "gohassagent/dbus"
)

type DbusMessage struct {
Bus string `json:"bus"`
Destination string `json:"destination"`
Path dbus.ObjectPath `json:"path"`
UseSessionPath bool `json:"useSessionPath"`
Method string `json:"method"`
Args []any `json:"args"`
}

func newDbusSubscription(ctx context.Context) *mqttapi.Subscription {
return &mqttapi.Subscription{
Callback: func(c MQTT.Client, m MQTT.Message) {
var dbusMsg DbusMessage

if err := json.Unmarshal(m.Payload(), &dbusMsg); err != nil {
log.Warn().Err(err).Msg("could not unmarshal dbus MQTT message")
return
}

if dbusMsg.UseSessionPath {
dbusMsg.Path = dbusx.GetSessionPath(ctx)
}

dbusType, ok := dbusx.DbusTypeMap[dbusMsg.Bus]
if !ok {
log.Warn().Msg("unsupported dbus type")
return
}

log.Info().Str("bus", dbusMsg.Bus).Str("destination", dbusMsg.Destination).Str("path", string(dbusMsg.Path)).Str("method", dbusMsg.Method).Msg("dispatching dbus MQTT message")

dbusx.NewBusRequest(ctx, dbusType).
Path(dbus.ObjectPath(dbusMsg.Path)).
Destination(dbusMsg.Destination).
Call(dbusMsg.Method, dbusMsg.Args...)
},
Topic: dbus_mqtt_topic,
}
}
7 changes: 6 additions & 1 deletion internal/agent/runners.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,13 @@ func runMQTTWorker(ctx context.Context) {
}
}
if err := mqtthass.Subscribe(o, c); err != nil {
log.Error().Err(err).Msg("Could not activate subscriptions.")
log.Error().Err(err).Msg("Could not activate entity subscriptions.")
}

if err := c.Subscribe(newDbusSubscription(ctx)); err != nil {
log.Error().Err(err).Msg("Could not activate dbus subscription.")
}

log.Debug().Msg("Listening for events on MQTT.")

<-ctx.Done()
Expand Down
5 changes: 5 additions & 0 deletions pkg/linux/dbusx/dbus.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ const (

var ErrNoBus = errors.New("no D-Bus connection")

var DbusTypeMap = map[string]dbusType{
"session": 0,
"system": 1,
}

type dbusType int

type Bus struct {
Expand Down

0 comments on commit 7204181

Please sign in to comment.