meshtastic: support
Some checks failed
Run tests / test (1.25) (push) Failing after 1m1s
Run tests / test (stable) (push) Failing after 1m0s

This commit is contained in:
2026-03-06 09:24:56 +01:00
parent e6bda98b92
commit e2b69d92fd
39 changed files with 26113 additions and 1 deletions

View File

@@ -60,6 +60,10 @@ func (dev *Node) Info() *radio.Info {
return dev.driver.Info()
}
func (dev *Node) Stats() map[string]any {
return dev.driver.Stats()
}
func (dev *Node) Trace(path []byte) (snr []float64, err error) {
if tracer, ok := dev.driver.(nodeTracer); ok {
return tracer.Trace(path)
@@ -74,6 +78,7 @@ type nodeDriver interface {
Setup() error
Packets() <-chan *Packet
Stats() map[string]any
}
type nodeTracer interface {

View File

@@ -47,6 +47,7 @@ type companionDriver struct {
info companionInfo
traceTag uint32
traceAuthCode uint32
stats map[string]any
}
type companionDriverWaiting struct {
@@ -208,6 +209,10 @@ func (drv *companionDriver) Info() *radio.Info {
}
}
func (drv *companionDriver) Stats() map[string]any {
return drv.stats
}
func (drv *companionDriver) Trace(path []byte) (snr []float64, err error) {
var (
args = make([]byte, 4+4+1+len(path))

View File

@@ -23,6 +23,7 @@ type repeaterDriver struct {
lastFrame []byte
lastFrameAt time.Time
info repeaterInfo
stats map[string]any
err error
}
@@ -102,6 +103,7 @@ func newRepeaterDriver(conn io.ReadWriteCloser, hasSNR bool) *repeaterDriver {
conn: conn,
waiting: make(chan *repeaterDriverWaiting, 16),
hasSNR: hasSNR,
stats: make(map[string]any),
}
}
@@ -114,6 +116,7 @@ func (drv *repeaterDriver) Setup() (err error) {
if err = drv.queryDeviceInfo(); err != nil {
return err
}
go drv.pollStats()
return drv.err
}
@@ -176,6 +179,10 @@ func (drv *repeaterDriver) RawPackets() <-chan *protocol.Packet {
return drv.rawPackets
}
func (drv *repeaterDriver) Stats() map[string]any {
return drv.stats
}
func (drv *repeaterDriver) queryDeviceInfo() (err error) {
if drv.info.FirmwareVersion, err = drv.writeCommand("ver"); err != nil {
return
@@ -404,6 +411,73 @@ func (drv *repeaterDriver) poll() {
}
}
func (drv *repeaterDriver) pollStats() {
ticker := time.NewTicker(time.Minute)
defer ticker.Stop()
for {
neighbors, err := drv.getNeighbors()
if err != nil {
Logger.Warnf("meshcore: failed to get neighbors: %v", err)
} else {
drv.stats["neighbors"] = neighbors
}
response, err := drv.writeCommand("stats")
if err != nil {
Logger.Warnf("meshcore: failed to get stats: %v", err)
return
}
stats := make(map[string]any)
for _, line := range strings.Split(response, "\n") {
parts := strings.SplitN(line, "=", 2)
if len(parts) != 2 {
continue
}
key := parts[0]
value := parts[1]
if i, err := strconv.Atoi(value); err == nil {
stats[key] = i
} else if f, err := strconv.ParseFloat(value, 64); err == nil {
stats[key] = f
} else {
stats[key] = value
}
}
drv.stats = stats
<-ticker.C
}
}
func (drv *repeaterDriver) getNeighbors() (neighbors map[string]float64, err error) {
// "neighbors" command returns a list of neighbors, one per line, in the format: "78CCC5A3:470:47\n"
var response string
if response, err = drv.writeCommand("neighbors"); err != nil {
return
}
Logger.Tracef("meshcore: neighbors response: %q", response)
neighbors = make(map[string]float64)
for _, line := range strings.Split(response, "\n") {
parts := strings.SplitN(line, ":", 3)
if len(parts) != 3 {
continue
}
var (
prefix = parts[0]
snr float64
)
if snr, err = strconv.ParseFloat(parts[2], 64); err != nil {
return
}
neighbors[prefix] = snr
}
return
}
func (drv *repeaterDriver) parsePacket(line string) (time.Time, string, error) {
// "11:08:38 - 15/5/2024 U: TX, len=124 (type=4, route=D, payload_len=122)"
if len(line) < 22 {