Skip to content

Commit

Permalink
rename cars to trackers and make local to garage
Browse files Browse the repository at this point in the history
  • Loading branch information
brchri committed Dec 13, 2023
1 parent ccc1262 commit c11fa2c
Show file tree
Hide file tree
Showing 14 changed files with 230 additions and 220 deletions.
70 changes: 37 additions & 33 deletions cmd/app/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ import (

var (
configFile string
cars []*geo.Car // list of all cars from all garage doors
version string = "v0.0.1" // pass -ldflags="-X main.version=<version>" at build time to set linker flag and bake in binary version
trackers []*geo.Tracker // list of all trackers from all garage doors
version string = "v0.0.1" // pass -ldflags="-X main.version=<version>" at build time to set linker flag and bake in binary version
commitHash string
messageChan chan mqtt.Message // channel to receive mqtt messages
mqttSettings *util.MqttConnectSettings // point to util.Config.Global.MqttSettings.Connection for shorter reference
Expand All @@ -50,13 +50,13 @@ func init() {
geo.ParseGarageDoorConfig()
checkEnvVars()
for _, garageDoor := range geo.GarageDoors {
for _, car := range garageDoor.Cars {
car.GarageDoor = garageDoor
cars = append(cars, car)
car.InsidePolyCloseGeo = true // only relevent for polygon geos but won't be used if that's not the geofence type
car.InsidePolyOpenGeo = true // only relevent for polygon geos but won't be used if that's not the geofence type
// start listening to car update location channels
go processLocationUpdates(car)
for _, tracker := range garageDoor.Trackers {
tracker.GarageDoor = garageDoor
trackers = append(trackers, tracker)
tracker.InsidePolyCloseGeo = true // only relevent for polygon geos but won't be used if that's not the geofence type
tracker.InsidePolyOpenGeo = true // only relevent for polygon geos but won't be used if that's not the geofence type
// start listening to tracker update location channels
go processLocationUpdates(tracker)
}
}
}
Expand Down Expand Up @@ -168,35 +168,35 @@ func main() {
case message := <-messageChan:
m := strings.Split(message.Topic(), "/")

// locate car and car's garage door
var car *geo.Car
for _, c := range cars {
// locate tracker and tracker's garage door
var tracker *geo.Tracker
for _, c := range trackers {
if fmt.Sprintf("%d", c.ID) == m[2] {
car = c
tracker = c
break
}
}

// if lat or lng received, check geofence
switch m[3] {
case "geofence":
car.PrevGeofence = car.CurGeofence
car.CurGeofence = string(message.Payload())
logger.Infof("Received geo for car %d: %v", car.ID, car.CurGeofence)
go geo.CheckGeofence(car)
tracker.PrevGeofence = tracker.CurGeofence
tracker.CurGeofence = string(message.Payload())
logger.Infof("Received geo for tracker %d: %v", tracker.ID, tracker.CurGeofence)
go geo.CheckGeofence(tracker)
case "latitude":
logger.Debugf("Received lat for car %d: %v", car.ID, string(message.Payload()))
logger.Debugf("Received lat for tracker %d: %v", tracker.ID, string(message.Payload()))
lat, _ := strconv.ParseFloat(string(message.Payload()), 64)
go func(lat float64) {
// send as goroutine so it doesn't block other vehicle updates if channel buffer is full
car.LocationUpdate <- geo.Point{Lat: lat, Lng: 0}
tracker.LocationUpdate <- geo.Point{Lat: lat, Lng: 0}
}(lat)
case "longitude":
logger.Debugf("Received long for car %d: %v", car.ID, string(message.Payload()))
logger.Debugf("Received long for tracker %d: %v", tracker.ID, string(message.Payload()))
lng, _ := strconv.ParseFloat(string(message.Payload()), 64)
go func(lng float64) {
// send as goroutine so it doesn't block other vehicle updates if channel buffer is full
car.LocationUpdate <- geo.Point{Lat: 0, Lng: lng}
tracker.LocationUpdate <- geo.Point{Lat: 0, Lng: lng}
}(lng)
}

Expand All @@ -213,30 +213,34 @@ func main() {
}
}

// watches the LocationUpdate channel for a car and queues a CheckGeofence operation
// watches the LocationUpdate channel for a tracker and queues a CheckGeofence operation
// this allows threaded geofence checks for multiple vehicles, while each individual vehicle
// does not have parallel threads executing checks
func processLocationUpdates(car *geo.Car) {
for update := range car.LocationUpdate {
func processLocationUpdates(tracker *geo.Tracker) {
for update := range tracker.LocationUpdate {
if update.Lat != 0 {
car.CurrentLocation.Lat = update.Lat
tracker.CurrentLocation.Lat = update.Lat
}
if update.Lng != 0 {
car.CurrentLocation.Lng = update.Lng
tracker.CurrentLocation.Lng = update.Lng
}
if car.CurrentLocation.IsPointDefined() {
geo.CheckGeofence(car)
if tracker.CurrentLocation.IsPointDefined() {
geo.CheckGeofence(tracker)
}
}
}

// subscribe to topics when MQTT client connects (or reconnects)
func onMqttConnect(client mqtt.Client) {
for _, car := range cars {
logger.Infof("Subscribing to MQTT topics for car %d", car.ID)
for _, tracker := range trackers {
logger.Infof("Subscribing to MQTT topics for tracker %d", tracker.ID)

// define which topics are relevant for each car based on config
topics := car.GarageDoor.Geofence.GetMqttTopics(car.ID)
// define which topics are relevant for each tracker based on config

topics := []string{
tracker.LatTopic,
tracker.LngTopic,
}

// subscribe to topics
for _, topic := range topics {
Expand All @@ -254,7 +258,7 @@ func onMqttConnect(client mqtt.Client) {
logger.Debugf("Topic subscribed successfully: %s", topic)
break
} else {
logger.Infof("Failed to subscribe to topic %s for car %d, will make %d more attempts. Error: %v", topic, car.ID, retryAttempts, token.Error())
logger.Infof("Failed to subscribe to topic %s for tracker %d, will make %d more attempts. Error: %v", topic, tracker.ID, retryAttempts, token.Error())
}
time.Sleep(5 * time.Second)
}
Expand Down
6 changes: 4 additions & 2 deletions config.simple.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,7 @@ garage_doors:
mqtt_settings:
connection: *mqtt_connection_settings
prefix: home/garage/Main
cars:
- teslamate_car_id: 1
trackers:
- id: 1
lat_topic: teslamate/cars/1/latitude
lng_topic: teslamate/cars/1/longitude
10 changes: 7 additions & 3 deletions examples/config.circular.homeassistant.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ garage_doors:
skip_tls_verify: false # optional, if use_tls = true, this option indicates whether the client should skip certificate validation on home assistant
entity_id: cover.main_door # id for the garage door entity in home assistant, can be found by adding '/config/entities' to the base url in home assistant
enable_status_checks: true # set to true if gdo supports garage states (e.g. garage is closed)
cars: # list of cars that use this garage door
- teslamate_car_id: 1 # id used for the first vehicle in TeslaMate's MQTT broker
- teslamate_car_id: 2 # id used for the second vehicle in TeslaMate's MQTT broker
trackers:
- id: 1 # required, some identifier, can be number or string
lat_topic: teslamate/cars/1/latitude # topic to retrieve latitude for tracker
lng_topic: teslamate/cars/1/longitude # topic to retrieve longitude for tracker
- id: 2 # required, some identifier, can be number or string
lat_topic: teslamate/cars/2/latitude # topic to retrieve latitude for tracker
lng_topic: teslamate/cars/2/longitude # topic to retrieve longitude for tracker
10 changes: 7 additions & 3 deletions examples/config.circular.homebridge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ garage_doors:
values: # defines expected values for status and command characteristics
open: 0 # defines value for characteristic to open door
close: 1 # defines value for characteristic to close door
cars: # list of cars that use this garage door
- teslamate_car_id: 1 # id used for the first vehicle in TeslaMate's MQTT broker
- teslamate_car_id: 2 # id used for the second vehicle in TeslaMate's MQTT broker
trackers:
- id: 1 # required, some identifier, can be number or string
lat_topic: teslamate/cars/1/latitude # topic to retrieve latitude for tracker
lng_topic: teslamate/cars/1/longitude # topic to retrieve longitude for tracker
- id: 2 # required, some identifier, can be number or string
lat_topic: teslamate/cars/2/latitude # topic to retrieve latitude for tracker
lng_topic: teslamate/cars/2/longitude # topic to retrieve longitude for tracker
10 changes: 7 additions & 3 deletions examples/config.circular.ratgdo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ garage_doors:
use_tls: false # optional, instructs app to connect to mqtt broker using tls (defaults to false)
skip_tls_verify: false # optional, if use_tls = true, this option indicates whether the client should skip certificate validation on the mqtt broker
topic_prefix: home/garage/Main # required
cars: # list of cars that use this garage door
- teslamate_car_id: 1 # id used for the first vehicle in TeslaMate's MQTT broker
- teslamate_car_id: 2 # id used for the second vehicle in TeslaMate's MQTT broker
trackers:
- id: 1 # required, some identifier, can be number or string
lat_topic: teslamate/cars/1/latitude # topic to retrieve latitude for tracker
lng_topic: teslamate/cars/1/longitude # topic to retrieve longitude for tracker
- id: 2 # required, some identifier, can be number or string
lat_topic: teslamate/cars/2/latitude # topic to retrieve latitude for tracker
lng_topic: teslamate/cars/2/longitude # topic to retrieve longitude for tracker
22 changes: 15 additions & 7 deletions examples/config.multiple.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,13 @@ garage_doors:
client_id: tesla-geogdo-ratgdo1
# WARNING!! client_id's MUST BE UNIQUE for any mqtt client that shares a broker !!
topic_prefix: home/garage/Main
cars:
- teslamate_car_id: 1
- teslamate_car_id: 2
trackers:
- id: 1 # required, some identifier, can be number or string
lat_topic: teslamate/cars/1/latitude # topic to retrieve latitude for tracker
lng_topic: teslamate/cars/1/longitude # topic to retrieve longitude for tracker
- id: 2 # required, some identifier, can be number or string
lat_topic: teslamate/cars/2/latitude # topic to retrieve latitude for tracker
lng_topic: teslamate/cars/2/longitude # topic to retrieve longitude for tracker

- # 3rd car garage example
geofence:
Expand Down Expand Up @@ -68,8 +72,10 @@ garage_doors:
topic_suffix: command/door
required_start_state: open
required_finish_state: closed
cars:
- teslamate_car_id: 3
trackers:
- id: 3 # required, some identifier, can be number or string
lat_topic: teslamate/cars/3/latitude # topic to retrieve latitude for tracker
lng_topic: teslamate/cars/3/longitude # topic to retrieve longitude for tracker

- # 4th car detached garage example
geofence:
Expand Down Expand Up @@ -97,5 +103,7 @@ garage_doors:
body:
required_start_state: open
required_finish_state: closed
cars:
- teslamate_car_id: 4
trackers:
- id: 4 # required, some identifier, can be number or string
lat_topic: teslamate/cars/4/latitude # topic to retrieve latitude for tracker
lng_topic: teslamate/cars/4/longitude # topic to retrieve longitude for tracker
6 changes: 4 additions & 2 deletions examples/config.polygon.http.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,5 +89,7 @@ garage_doors:
headers: # optional, list of headers, each must be surrounded by single quotes
- 'Authorization: Bearer long_api_key' # example header
- 'Content-Type: application/json' # example header
cars:
- teslamate_car_id: 1 # id used for the first vehicle in TeslaMate's MQTT broker
trackers:
- id: 1 # required, some identifier, can be number or string
lat_topic: teslamate/cars/1/latitude # topic to retrieve latitude for tracker
lng_topic: teslamate/cars/1/longitude # topic to retrieve longitude for tracker
9 changes: 7 additions & 2 deletions examples/config.teslamate.mqtt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,10 @@ garage_doors:
topic_suffix: command/door
required_start_state: open
required_finish_state: closed
cars:
- teslamate_car_id: 1 # id used for the first vehicle in TeslaMate's MQTT broker
trackers:
- id: 1 # required, some identifier, can be number or string
lat_topic: teslamate/cars/1/latitude # topic to retrieve latitude for tracker
lng_topic: teslamate/cars/1/longitude # topic to retrieve longitude for tracker
- id: 2 # required, some identifier, can be number or string
lat_topic: teslamate/cars/2/latitude # topic to retrieve latitude for tracker
lng_topic: teslamate/cars/2/longitude # topic to retrieve longitude for tracker
25 changes: 8 additions & 17 deletions internal/geo/circular.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"fmt"
"math"

util "github.com/brchri/tesla-geogdo/internal/util"
"gopkg.in/yaml.v3"
)

Expand All @@ -17,14 +16,6 @@ type (
}
)

func (c *CircularGeofence) GetMqttTopics(carId int) []string {
// doesn't care about carId, just needs to match interface signature
return []string{
util.Config.Global.MqttSettings.LatTopic,
util.Config.Global.MqttSettings.LngTopic,
}
}

func distance(point1 Point, point2 Point) float64 {
// Calculate the distance between two points using the haversine formula
const radius = 6371 // Earth's radius in kilometers
Expand All @@ -43,23 +34,23 @@ func toRadians(degrees float64) float64 {
}

// gets action based on if there was a relevant distance change
func (c *CircularGeofence) getEventChangeAction(car *Car) (action string) {
if !car.CurrentLocation.IsPointDefined() {
func (c *CircularGeofence) getEventChangeAction(tracker *Tracker) (action string) {
if !tracker.CurrentLocation.IsPointDefined() {
return // need valid lat and lng to check fence
}

// update car's current distance, and store the previous distance in a variable
prevDistance := car.CurDistance
car.CurDistance = distance(car.CurrentLocation, c.Center)
// update tracker's current distance, and store the previous distance in a variable
prevDistance := tracker.CurDistance
tracker.CurDistance = distance(tracker.CurrentLocation, c.Center)

// check if car has crossed a geofence and set an appropriate action
// check if tracker has crossed a geofence and set an appropriate action
if c.CloseDistance > 0 && // is valid close distance defined
prevDistance <= c.CloseDistance &&
car.CurDistance > c.CloseDistance { // car was within close geofence, but now beyond it (car left geofence)
tracker.CurDistance > c.CloseDistance { // tracker was within close geofence, but now beyond it (tracker left geofence)
action = ActionClose
} else if c.OpenDistance > 0 && // is valid open distance defined
prevDistance >= c.OpenDistance &&
car.CurDistance < c.OpenDistance { // car was outside of open geofence, but is now within it (car entered geofence)
tracker.CurDistance < c.OpenDistance { // tracker was outside of open geofence, but is now within it (tracker entered geofence)
action = ActionOpen
}
return
Expand Down
Loading

0 comments on commit c11fa2c

Please sign in to comment.