Switch calls to use dbus library and add helpers for private connections
This commit is contained in:
parent
75327286ef
commit
0b5d777077
209
calls.go
209
calls.go
@ -1,176 +1,99 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"github.com/godbus/dbus/v5"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"os/exec"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"go.arsenm.dev/infinitime"
|
"go.arsenm.dev/infinitime"
|
||||||
)
|
)
|
||||||
|
|
||||||
func initCallNotifs(dev *infinitime.Device) error {
|
func initCallNotifs(dev *infinitime.Device) error {
|
||||||
// Define rule to filter dbus messages
|
// Define rule to filter dbus messages
|
||||||
rule := "type='signal',sender='org.freedesktop.ModemManager1',interface='org.freedesktop.ModemManager1.Modem.Voice',member='CallAdded'"
|
//rule := "type='signal',sender='org.freedesktop.ModemManager1',interface='org.freedesktop.ModemManager1.Modem.Voice',member='CallAdded'"
|
||||||
|
|
||||||
// Use dbus-monitor command with profiling output as a workaround
|
// Connect to dbus session monitorConn
|
||||||
// because go-bluetooth seems to monopolize the system bus connection
|
monitorConn, err := newSystemBusConn()
|
||||||
// which makes monitoring show only bluez-related messages.
|
|
||||||
cmd := exec.Command("dbus-monitor", "--system", "--profile", rule)
|
|
||||||
// Get command output pipe
|
|
||||||
stdout, err := cmd.StdoutPipe()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Run command asynchronously
|
|
||||||
err = cmd.Start()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new scanner for command output
|
conn, err := newSystemBusConn()
|
||||||
scanner := bufio.NewScanner(stdout)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = monitorConn.AddMatchSignal(
|
||||||
|
dbus.WithMatchSender("org.freedesktop.ModemManager1"),
|
||||||
|
dbus.WithMatchInterface("org.freedesktop.ModemManager1.Modem.Voice"),
|
||||||
|
dbus.WithMatchMember("CallAdded"),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c := make(chan *dbus.Message, 10)
|
||||||
|
monitorConn.Eavesdrop(c)
|
||||||
go func() {
|
go func() {
|
||||||
// For each line in output
|
for x := range c {
|
||||||
for scanner.Scan() {
|
callPath := x.Body[0].(dbus.ObjectPath)
|
||||||
// Get line as string
|
callObj := conn.Object("org.freedesktop.ModemManager1", callPath)
|
||||||
text := scanner.Text()
|
|
||||||
|
|
||||||
// If line starts with "#", it is part of
|
phoneNum, err := getPhoneNum(conn, callObj)
|
||||||
// the field format, skip it.
|
if err != nil {
|
||||||
if strings.HasPrefix(text, "#") {
|
log.Fatal().Err(err).Send()
|
||||||
|
}
|
||||||
|
|
||||||
|
resCh, err := dev.NotifyCall(phoneNum)
|
||||||
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Split line into fields. The order is as follows:
|
go func() {
|
||||||
// type timestamp serial sender destination path interface member
|
// Wait for PineTime response
|
||||||
fields := strings.Fields(text)
|
res := <-resCh
|
||||||
// Field 7 is Member. Make sure it is "CallAdded".
|
switch res {
|
||||||
if fields[7] == "CallAdded" {
|
case infinitime.CallStatusAccepted:
|
||||||
// Get Modem ID from modem path
|
// Attempt to accept call
|
||||||
modemID := parseModemID(fields[5])
|
err = acceptCall(conn, callObj)
|
||||||
// Get call ID of current call
|
if err != nil {
|
||||||
callID, err := getCurrentCallID(modemID)
|
log.Warn().Err(err).Msg("Error accepting call")
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Get phone number of current call
|
|
||||||
phoneNum, err := getPhoneNum(callID)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Send call notification to PineTime
|
|
||||||
resCh, err := dev.NotifyCall(phoneNum)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
go func() {
|
|
||||||
// Wait for PineTime response
|
|
||||||
res := <-resCh
|
|
||||||
switch res {
|
|
||||||
case infinitime.CallStatusAccepted:
|
|
||||||
// Attempt to accept call
|
|
||||||
err = acceptCall(callID)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn().Err(err).Msg("Error accepting call")
|
|
||||||
}
|
|
||||||
case infinitime.CallStatusDeclined:
|
|
||||||
// Attempt to decline call
|
|
||||||
err = declineCall(callID)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn().Err(err).Msg("Error declining call")
|
|
||||||
}
|
|
||||||
case infinitime.CallStatusMuted:
|
|
||||||
// Warn about unimplemented muting
|
|
||||||
log.Warn().Msg("Muting calls is not implemented")
|
|
||||||
}
|
}
|
||||||
}()
|
case infinitime.CallStatusDeclined:
|
||||||
}
|
// Attempt to decline call
|
||||||
|
err = declineCall(conn, callObj)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn().Err(err).Msg("Error declining call")
|
||||||
|
}
|
||||||
|
case infinitime.CallStatusMuted:
|
||||||
|
// Warn about unimplemented muting
|
||||||
|
log.Warn().Msg("Muting calls is not implemented")
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseModemID(modemPath string) int {
|
func getPhoneNum(conn *dbus.Conn, callObj dbus.BusObject) (string, error) {
|
||||||
// Split path by "/"
|
var out string
|
||||||
splitPath := strings.Split(modemPath, "/")
|
err := callObj.StoreProperty("org.freedesktop.ModemManager1.Call.Number", &out)
|
||||||
// Get last element and convert to integer
|
|
||||||
id, _ := strconv.Atoi(splitPath[len(splitPath)-1])
|
|
||||||
return id
|
|
||||||
}
|
|
||||||
|
|
||||||
func getCurrentCallID(modemID int) (int, error) {
|
|
||||||
// Create mmcli command
|
|
||||||
cmd := exec.Command("mmcli", "--voice-list-calls", "-m", fmt.Sprint(modemID), "-J")
|
|
||||||
// Run command and get output
|
|
||||||
data, err := cmd.Output()
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
var calls map[string][]string
|
|
||||||
// Decode JSON from command output
|
|
||||||
err = json.Unmarshal(data, &calls)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
// Get first call in output
|
|
||||||
firstCall := calls["modem.voice.call"][0]
|
|
||||||
// Split path by "/"
|
|
||||||
splitCall := strings.Split(firstCall, "/")
|
|
||||||
// Return last element converted to integer
|
|
||||||
return strconv.Atoi(splitCall[len(splitCall)-1])
|
|
||||||
}
|
|
||||||
|
|
||||||
func getPhoneNum(callID int) (string, error) {
|
|
||||||
// Create dbus-send command
|
|
||||||
cmd := exec.Command("dbus-send",
|
|
||||||
"--dest=org.freedesktop.ModemManager1",
|
|
||||||
"--system",
|
|
||||||
"--print-reply=literal",
|
|
||||||
"--type=method_call",
|
|
||||||
fmt.Sprintf("/org/freedesktop/ModemManager1/Call/%d", callID),
|
|
||||||
"org.freedesktop.DBus.Properties.Get",
|
|
||||||
"string:org.freedesktop.ModemManager1.Call",
|
|
||||||
"string:Number",
|
|
||||||
)
|
|
||||||
// Run command and get output
|
|
||||||
numData, err := cmd.Output()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
// Split output into fields
|
return out, nil
|
||||||
num := strings.Fields(string(numData))
|
|
||||||
// Return last field
|
|
||||||
return num[len(num)-1], nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func acceptCall(callID int) error {
|
func acceptCall(conn *dbus.Conn, callObj dbus.BusObject) error {
|
||||||
// Create dbus-send command
|
call := callObj.Call("org.freedesktop.ModemManager1.Call.Accept", 0)
|
||||||
cmd := exec.Command("dbus-send",
|
if call.Err != nil {
|
||||||
"--dest=org.freedesktop.ModemManager1",
|
return call.Err
|
||||||
"--print-reply",
|
}
|
||||||
"--system",
|
return nil
|
||||||
"--type=method_call",
|
|
||||||
fmt.Sprintf("/org/freedesktop/ModemManager1/Call/%d", callID),
|
|
||||||
"org.freedesktop.ModemManager1.Call.Accept",
|
|
||||||
)
|
|
||||||
// Run command and return errpr
|
|
||||||
return cmd.Run()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func declineCall(callID int) error {
|
func declineCall(conn *dbus.Conn, callObj dbus.BusObject) error {
|
||||||
// Create dbus-send command
|
call := callObj.Call("org.freedesktop.ModemManager1.Call.Hangup", 0)
|
||||||
cmd := exec.Command("dbus-send",
|
if call.Err != nil {
|
||||||
"--dest=org.freedesktop.ModemManager1",
|
return call.Err
|
||||||
"--print-reply",
|
}
|
||||||
"--system",
|
return nil
|
||||||
"--type=method_call",
|
|
||||||
fmt.Sprintf("/org/freedesktop/ModemManager1/Call/%d", callID),
|
|
||||||
"org.freedesktop.ModemManager1.Call.Hangup",
|
|
||||||
)
|
|
||||||
// Run command and return errpr
|
|
||||||
return cmd.Run()
|
|
||||||
}
|
}
|
37
dbus.go
Normal file
37
dbus.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "github.com/godbus/dbus/v5"
|
||||||
|
|
||||||
|
func newSystemBusConn() (*dbus.Conn, error) {
|
||||||
|
// Connect to dbus session bus
|
||||||
|
conn, err := dbus.SystemBusPrivate()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = conn.Auth(nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = conn.Hello()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSessionBusConn() (*dbus.Conn, error) {
|
||||||
|
// Connect to dbus session bus
|
||||||
|
conn, err := dbus.SessionBusPrivate()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = conn.Auth(nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = conn.Hello()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return conn, nil
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user