Switch Garmin messaging from IPC Inbound API to MapShare API

This commit is contained in:
Ian Gulliver
2026-03-26 20:31:26 -07:00
parent f880ab46f3
commit 57111e44f4
2 changed files with 63 additions and 67 deletions

View File

@@ -1,68 +1,61 @@
package main package main
import ( import (
"bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"log" "log"
"net/http" "net/http"
"time" "net/url"
"strings"
) )
type garminClient struct { type garminClient struct {
c *http.Client c *http.Client
apiKey string mapshareID string
} }
type garminMessageRequest struct { type garminResponse struct {
Messages []garminMessage `json:"messages"` Success bool `json:"success"`
Error *struct {
Code int `json:"code"`
Msg string `json:"msg"`
} `json:"error"`
} }
type garminMessage struct { func newGarminClient(mapshareID string) *garminClient {
Recipients []string `json:"recipients"`
Sender string `json:"sender"`
Timestamp string `json:"timestamp"`
Message string `json:"message"`
}
type garminMessageResponse struct {
Count int `json:"count"`
}
func newGarminClient(apiKey string) *garminClient {
return &garminClient{ return &garminClient{
c: &http.Client{}, c: &http.Client{},
apiKey: apiKey, mapshareID: mapshareID,
} }
} }
func (gc *garminClient) sendMessage(imeis []string, sender, msg string) error { func (gc *garminClient) sendMessage(deviceIDs []string, sender, msg string) error {
buf := &bytes.Buffer{} for _, deviceID := range deviceIDs {
err := json.NewEncoder(buf).Encode(garminMessageRequest{ err := gc.sendToDevice(deviceID, sender, msg)
Messages: []garminMessage{ if err != nil {
{ return fmt.Errorf("device %s: %w", deviceID, err)
Recipients: imeis, }
Sender: sender, }
Timestamp: time.Now().UTC().Format("2006-01-02T15:04:05Z"), return nil
Message: msg, }
},
},
})
func (gc *garminClient) sendToDevice(deviceID, sender, msg string) error {
data := url.Values{}
data.Set("deviceIds", deviceID)
data.Set("messageText", msg)
data.Set("fromAddr", sender)
endpoint := fmt.Sprintf("https://share.garmin.com/%s/Map/SendMessageToDevices", gc.mapshareID)
log.Printf("[->garmin] %s %s", endpoint, data.Encode())
req, err := http.NewRequest("POST", endpoint, strings.NewReader(data.Encode()))
if err != nil { if err != nil {
return err return err
} }
log.Printf("[->garmin] %s", buf.String()) req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req, err := http.NewRequest("POST", "https://ipcinbound.inreachapp.com/IPC/IPCInboundApi/api/Messaging/Message", buf)
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("x-api-key", gc.apiKey)
resp, err := gc.c.Do(req) resp, err := gc.c.Do(req)
if err != nil { if err != nil {
@@ -72,20 +65,23 @@ func (gc *garminClient) sendMessage(imeis []string, sender, msg string) error {
defer resp.Body.Close() defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body) body, _ := io.ReadAll(resp.Body)
log.Printf("[<-garmin] %d %s", resp.StatusCode, string(body))
if resp.StatusCode != 200 { if resp.StatusCode != 200 {
return fmt.Errorf("%s", string(body)) return fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(body))
} }
grResp := garminMessageResponse{} var grResp garminResponse
err = json.Unmarshal(body, &grResp) err = json.Unmarshal(body, &grResp)
if err != nil { if err != nil {
return err return fmt.Errorf("parse response: %w", err)
} }
log.Printf("[<-garmin] %s", string(body)) if !grResp.Success {
if grResp.Error != nil {
if grResp.Count != len(imeis) { return fmt.Errorf("garmin error %d: %s", grResp.Error.Code, grResp.Error.Msg)
return fmt.Errorf("expected %d messages, got %d", len(imeis), grResp.Count) }
return fmt.Errorf("garmin returned success=false")
} }
return nil return nil

38
main.go
View File

@@ -13,12 +13,12 @@ type PHandler struct {
tmpl *template.Template tmpl *template.Template
pd *pdClient pd *pdClient
gc *garminClient gc *garminClient
garminIMEIs []string garminDeviceIDs []string
garminSender string garminSender string
mux *http.ServeMux mux *http.ServeMux
} }
func NewPHandler(pdRoutingKey, garminAPIKey string, garminIMEIs []string, garminSender string) (*PHandler, error) { func NewPHandler(pdRoutingKey, garminMapshareID string, garminDeviceIDs []string, garminSender string) (*PHandler, error) {
tmpl := template.New("index.html") tmpl := template.New("index.html")
tmpl.Funcs(template.FuncMap{ tmpl.Funcs(template.FuncMap{
@@ -33,13 +33,13 @@ func NewPHandler(pdRoutingKey, garminAPIKey string, garminIMEIs []string, garmin
ph := &PHandler{ ph := &PHandler{
tmpl: tmpl, tmpl: tmpl,
pd: newPDClient(pdRoutingKey), pd: newPDClient(pdRoutingKey),
garminIMEIs: garminIMEIs, garminDeviceIDs: garminDeviceIDs,
garminSender: garminSender, garminSender: garminSender,
mux: http.NewServeMux(), mux: http.NewServeMux(),
} }
if garminAPIKey != "" { if garminMapshareID != "" {
ph.gc = newGarminClient(garminAPIKey) ph.gc = newGarminClient(garminMapshareID)
} }
ph.mux.HandleFunc("/{$}", ph.serveRoot) ph.mux.HandleFunc("/{$}", ph.serveRoot)
@@ -86,7 +86,7 @@ func (ph *PHandler) sendAlert(m string) error {
if ph.gc != nil { if ph.gc != nil {
go func() { go func() {
err := ph.gc.sendMessage(ph.garminIMEIs, ph.garminSender, m) err := ph.gc.sendMessage(ph.garminDeviceIDs, ph.garminSender, m)
if err != nil { if err != nil {
res <- fmt.Errorf("Error sending to Garmin: %w", err) res <- fmt.Errorf("Error sending to Garmin: %w", err)
} else { } else {
@@ -150,28 +150,28 @@ func main() {
log.Fatalf("please set PD_ROUTING_KEY") log.Fatalf("please set PD_ROUTING_KEY")
} }
garminAPIKey := os.Getenv("GARMIN_API_KEY") garminMapshareID := os.Getenv("GARMIN_MAPSHARE_ID")
garminIMEIStr := os.Getenv("GARMIN_IMEI") garminDeviceIDStr := os.Getenv("GARMIN_DEVICE_IDS")
if garminAPIKey != "" && garminIMEIStr == "" { if garminMapshareID != "" && garminDeviceIDStr == "" {
log.Fatalf("please set GARMIN_IMEI") log.Fatalf("please set GARMIN_DEVICE_IDS")
} }
var garminIMEIs []string var garminDeviceIDs []string
if garminIMEIStr != "" { if garminDeviceIDStr != "" {
for _, imei := range strings.Split(garminIMEIStr, ",") { for _, id := range strings.Split(garminDeviceIDStr, ",") {
imei = strings.TrimSpace(imei) id = strings.TrimSpace(id)
if imei != "" { if id != "" {
garminIMEIs = append(garminIMEIs, imei) garminDeviceIDs = append(garminDeviceIDs, id)
} }
} }
} }
garminSender := os.Getenv("GARMIN_SENDER") garminSender := os.Getenv("GARMIN_SENDER")
if garminAPIKey != "" && garminSender == "" { if garminMapshareID != "" && garminSender == "" {
log.Fatalf("please set GARMIN_SENDER") log.Fatalf("please set GARMIN_SENDER")
} }
ph, err := NewPHandler(pdRoutingKey, garminAPIKey, garminIMEIs, garminSender) ph, err := NewPHandler(pdRoutingKey, garminMapshareID, garminDeviceIDs, garminSender)
if err != nil { if err != nil {
log.Fatalf("NewPHandler: %s", err) log.Fatalf("NewPHandler: %s", err)
} }