summaryrefslogtreecommitdiff
path: root/main.go
blob: 710f195a63dfe8a40261e80b4f6dee105216fcc0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package main

import (
	"context"
	"encoding/base64"
	"encoding/binary"
	"fmt"
	"os"
	"os/signal"
	"time"

	"github.com/charmbracelet/log"
	"github.com/meshnet-gophers/meshtastic-go"
	pb "github.com/meshnet-gophers/meshtastic-go/meshtastic"
	flag "github.com/spf13/pflag"
	"golang.org/x/crypto/curve25519"
)

func main() {
	var args struct {
		DbDir      string
		LogsDir    string
		ConfigPath string
		Interface  string
		Relentless bool
	}
	flag.StringVarP(&args.DbDir, "dbdir", "", "./data/", "The path to the database directory")
	flag.StringVarP(&args.LogsDir, "logsdir", "", "./logs/", "The path to the logs directory")
	flag.StringVarP(&args.ConfigPath, "config", "c", "./config.json", "The path to the config.json")
	flag.StringVarP(&args.Interface, "interface", "i", "kava", "Mesh interface (kava, mqtt)")
	flag.BoolVarP(&args.Relentless, "relentless", "r", false, "Relentless reconnect to modem")
	flag.Parse()

	log.SetLevel(log.DebugLevel)
	cfg, err := ReadConfig(args.ConfigPath)
	if err != nil {
		log.Fatal(err)
	}
	pbChannels := make([]*pb.ChannelSettings, len(cfg.Channels))
	for i, channel := range cfg.Channels {
		var pbChannelSettings = pb.ChannelSettings{
			Name: channel.Name,
			Psk:  channel.PSK,
		}
		pbChannels[i] = &pbChannelSettings
	}

	var nodeID meshtastic.NodeID
	if len(cfg.NodeID) == 4 {
		nodeID = meshtastic.NodeID(binary.BigEndian.Uint32(cfg.NodeID))
	} else if len(cfg.NodeID) == 0 {
		nodeID, err = meshtastic.RandomNodeID()
		if err != nil {
			log.Fatal(err)
		}
	}
	log.Print("Config:")
	log.Print("  Database", "directory", args.DbDir)
	log.Print("  Logs", "directory", args.LogsDir)
	log.Print("  Node", "id", nodeID, "short", cfg.ShortName, "name", cfg.Name)
	nodeConfig := NodeConfig{
		DatabaseDir:               args.DbDir,
		LogsDir:                   args.LogsDir,
		NodeID:                    nodeID,
		LongName:                  cfg.Name,
		ShortName:                 cfg.ShortName,
		DefaultHops:               cfg.DefaultHops,
		MaxHops:                   cfg.MaxHops,
		Channels:                  &pb.ChannelSet{Settings: pbChannels},
		BroadcastNodeInfoInterval: time.Duration(cfg.NodeBroadcastInterval * float64(time.Second)),
		BroadcastPositionInterval: time.Duration(cfg.Position.BroadcastInterval * float64(time.Second)),
		// Position paramters
		PositionLatitudeI:  int32(cfg.Position.Latitude * 1.e7),
		PositionLongitudeI: int32(cfg.Position.Longitude * 1.e7),
		PositionAltitude:   int32(cfg.Position.Altitude),
		// Device Metrics
		DeviceMetricsBroadcastInterval: time.Duration(cfg.DeviceMetrics.BroadcastInterval * float64(time.Second)),
		DeviceMetricsUPSAddress:        cfg.DeviceMetrics.UPSAddress,
		TCPListenAddr:                  "0.0.0.0:4403",
	}
	if len(cfg.X25519Key) == 32 {
		nodeConfig.X25519SecretKey = cfg.X25519Key
		nodeConfig.X25519PublicKey, err = curve25519.X25519(nodeConfig.X25519SecretKey, curve25519.Basepoint)
		if err != nil {
			log.Fatal(err)
		}
		log.Print("  X25519PublicKey", "base64", base64.StdEncoding.EncodeToString(nodeConfig.X25519PublicKey), "hex", fmt.Sprintf("%x", nodeConfig.X25519PublicKey))
	} else if len(cfg.X25519Key) != 0 {
		log.Fatal("invalid x25519 key")
	}

	if args.Relentless {
		log.Warn("Starting Node in 'relentless' mode...")
	} else {
		log.Info("Starting Node...")
	}

	n, err := NewNode(nodeConfig)
	if err != nil {
		log.Fatal(err)
	}
	defer n.Close()
	ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt)
	for {
		var iface MeshIf
		switch args.Interface {
		case "kava":
			iface = NewKavaConn(cfg.KavaModem.Address, 10*time.Second, n.cfg.Channels)
		case "mqtt":
			iface = NewMqttIf(cfg.MqttIf.Address, cfg.MqttIf.Username, cfg.MqttIf.Password, cfg.MqttIf.Root, cfg.MqttIf.ClientID, meshtastic.NodeID(nodeID), n.cfg.Channels)
		default:
			log.Fatal("invalid argument", "interface", args.Interface)
		}

		if err := n.Run(ctx, iface); err != nil {
			log.Error("!! Node.Run()", "err", err)
		}
		if err := ctx.Err(); err != nil {
			log.Error("!! Caught interrupt!")
			break
		}
		if !args.Relentless {
			break
		}
		log.Warn("Reconnecting in 30 seconds...")
		ticker := time.NewTicker(30 * time.Second)
		select {
		case <-ctx.Done():
			return
		case <-ticker.C:
		}
	}
}