2024-10-19 01:21:50 +00:00
package meshtastic
import (
2024-10-20 20:00:50 +00:00
"bytes"
"context"
2024-10-19 01:21:50 +00:00
"fmt"
"io"
"net/http"
"net/url"
"time"
"git.janky.solutions/finn/matrix-meshtastic-bridge-go/config"
"git.janky.solutions/finn/matrix-meshtastic-bridge-go/meshtastic/protobufs"
"github.com/sirupsen/logrus"
"google.golang.org/protobuf/proto"
)
2024-10-20 20:00:50 +00:00
const (
pathFromRadio = "/api/v1/fromradio"
pathToRadio = "/api/v1/toradio"
)
2024-10-19 01:21:50 +00:00
2024-10-21 01:38:35 +00:00
func Receive ( ctx context . Context , ch chan * protobufs . FromRadio ) {
t := time . NewTicker ( config . C . Meshtastic . PollingInterval )
for {
select {
case <- t . C :
fromRadio , err := getFromRadio ( ctx )
if err != nil {
logrus . WithError ( err ) . Error ( "error communicating with radio" )
}
if fromRadio != nil && fromRadio . PayloadVariant != nil {
ch <- fromRadio
}
case <- ctx . Done ( ) :
return
2024-10-19 01:21:50 +00:00
}
2024-10-20 20:00:50 +00:00
}
}
func ShutdownReceiver ( ) {
// TODO: wait for receive loop to cleanly exit
}
func SendConfigInit ( ctx context . Context ) error {
return sendToRadio ( ctx , & protobufs . ToRadio {
PayloadVariant : & protobufs . ToRadio_WantConfigId {
WantConfigId : 4 , // chosen by fair dice roll. guaranteed to be random. - actually though, we dont do anything special when startup is over so we dont care about this.
} ,
} )
}
func getFromRadio ( ctx context . Context ) ( * protobufs . FromRadio , error ) {
body , err := request ( ctx , http . MethodGet , pathFromRadio , nil )
if err != nil {
return nil , err
}
2024-10-19 01:21:50 +00:00
2024-10-20 20:00:50 +00:00
fromRadio := protobufs . FromRadio { }
err = proto . Unmarshal ( body , & fromRadio )
if err != nil {
return nil , fmt . Errorf ( "error parsing response from radio: %v" , err )
2024-10-19 01:21:50 +00:00
}
2024-10-20 20:00:50 +00:00
return & fromRadio , nil
}
func sendToRadio ( ctx context . Context , req * protobufs . ToRadio ) error {
requestBody , err := proto . Marshal ( req )
if err != nil {
return fmt . Errorf ( "error marshalling ToRadio payload: %v" , err )
}
resp , err := request ( ctx , http . MethodPut , pathToRadio , requestBody )
if err != nil {
return err
}
logrus . WithField ( "resp_body" , string ( resp ) ) . Debug ( "completed request to radio" )
2024-10-19 01:21:50 +00:00
return nil
}
2024-10-20 20:00:50 +00:00
func request ( ctx context . Context , method , path string , body [ ] byte ) ( [ ] byte , error ) {
2024-10-19 01:21:50 +00:00
u := url . URL {
Scheme : "http" ,
Host : config . C . Meshtastic . Address ,
2024-10-20 20:00:50 +00:00
Path : path ,
2024-10-19 01:21:50 +00:00
}
2024-10-20 20:00:50 +00:00
req , err := http . NewRequest ( method , u . String ( ) , bytes . NewBuffer ( body ) )
2024-10-19 01:21:50 +00:00
if err != nil {
2024-10-20 20:00:50 +00:00
return nil , fmt . Errorf ( "error building request to radio with path %s: %v" , path , err )
2024-10-19 01:21:50 +00:00
}
req . Header . Add ( "Accept" , "application/x-protobuf" )
2024-10-20 20:00:50 +00:00
ctx , cancel := context . WithTimeout ( ctx , config . C . Meshtastic . RequestTimeout )
defer cancel ( )
resp , err := http . DefaultClient . Do ( req . WithContext ( ctx ) )
2024-10-19 01:21:50 +00:00
if err != nil {
2024-10-20 20:00:50 +00:00
return nil , fmt . Errorf ( "error making request to radio with path %s: %v" , path , err )
2024-10-19 01:21:50 +00:00
}
defer resp . Body . Close ( )
if resp . StatusCode != http . StatusOK {
2024-10-20 20:00:50 +00:00
return nil , fmt . Errorf ( "unexpected %s from radio to path %s" , resp . Status , path )
2024-10-19 01:21:50 +00:00
}
2024-10-20 20:00:50 +00:00
responseBody , err := io . ReadAll ( resp . Body )
2024-10-19 01:21:50 +00:00
if err != nil {
2024-10-20 20:00:50 +00:00
return nil , fmt . Errorf ( "error reading response from radio path %s: %v" , path , err )
2024-10-19 01:21:50 +00:00
}
2024-10-20 20:00:50 +00:00
return responseBody , nil
2024-10-19 01:21:50 +00:00
}