Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for using teslamate geofences #12

Merged
merged 4 commits into from
Jul 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions cmd/app/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,20 @@ func main() {

// create channels to receive messages
for _, car := range cars {
log.Printf("Subscribing to MQTT geofence, latitude, and longitude topics for car %d", car.ID)
log.Printf("Subscribing to MQTT topics for car %d", car.ID)

topics := make([]string, 0)
if car.GarageDoor.TriggerCloseGeofence.IsGeofenceDefined() && car.GarageDoor.TriggerOpenGeofence.IsGeofenceDefined() {
car.GarageDoor.UseTeslmateGeofence = true
topics = append(topics, "geofence")
} else if car.GarageDoor.Location.IsPointDefined() {
topics = append(topics, "latitude", "longitude")
car.GarageDoor.UseTeslmateGeofence = false
} else {
log.Fatalf("must define a valid location and radii for garage door or open and close geofence triggers")
}

for _, topic := range []string{"geofence", "latitude", "longitude"} {
for _, topic := range topics {
if token := client.Subscribe(
fmt.Sprintf("teslamate/cars/%d/%s", car.ID, topic),
0,
Expand Down Expand Up @@ -170,7 +181,10 @@ func main() {
// if lat or lng received, check geofence
switch m[3] {
case "geofence":
log.Printf("Received geo for car %d: %v", car.ID, string(message.Payload()))
car.PrevGeofence = car.CurGeofence
car.CurGeofence = string(message.Payload())
log.Printf("Received geo for car %d: %v", car.ID, car.CurGeofence)
go geo.CheckGeoFence(util.Config, car)
case "latitude":
if debug {
log.Printf("Received lat for car %d: %v", car.ID, string(message.Payload()))
Expand Down
17 changes: 10 additions & 7 deletions config.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ global:
myq_pass: super_secret_password # password to auth to myq account; can also be passed as env var MYQ_PASS

garage_doors:
- &main_door
- # main garage
location: # defines the latitude and longitude of the garage door location, which will be the center of the geofence radius
lat: 48.858195
lng: 2.294689
Expand All @@ -26,10 +26,13 @@ garage_doors:
cars:
- teslamage_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
- <<: *main_door # will inherit all properties defined in the first garage_door, with overrides listed below
location:
lat: 48.858197
lng: 2.294689
myq_serial: myq_serial_2
- # 3rd car garage
trigger_close_geofence: # this method is not yet fully released, but is available for preview in v0.1.2-rc1; please use location, close_radius, and open_radius for latest full release versions
from: home
to: close_to_home
trigger_open_geofence: # this method is not yet fully released, but is available for preview in v0.1.2-rc1; please use location, close_radius, and open_radius for latest full release versions
from: not_home
to: close_to_home
myq_serial: myq_serial_2 # serial number of garage door opener; see README for more info
cars:
- teslamate_car_id: 3
- teslamage_car_id: 3 # id used for the third vehicle in TeslaMate's MQTT broker
63 changes: 44 additions & 19 deletions internal/geo/geo.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,26 +73,12 @@ func toRadians(degrees float64) float64 {
// check if outside close geo or inside open geo and set garage door state accordingly
func CheckGeoFence(config util.ConfigStruct, car *util.Car) {

if car.CurLat == 0 || car.CurLng == 0 {
return // need valid lat and lng to check fence
}

// Define a carLocation to check
carLocation := util.Point{
Lat: car.CurLat,
Lng: car.CurLng,
}

// update car's current distance, and store the previous distance in a variable
prevDistance := car.CurDistance
car.CurDistance = distance(carLocation, car.GarageDoor.Location)

// check if car has crossed a geofence and set an appropriate action
// get action based on either geo cross events or distance threshold cross events
var action string
if prevDistance <= car.GarageDoor.CloseRadius && car.CurDistance > car.GarageDoor.CloseRadius { // car was within close geofence, but now beyond it (car left geofence)
action = myq.ActionClose
} else if prevDistance >= car.GarageDoor.OpenRadius && car.CurDistance < car.GarageDoor.OpenRadius { // car was outside of open geofence, but is now within it (car entered geofence)
action = myq.ActionOpen
if car.GarageDoor.UseTeslmateGeofence {
action = getGeoChangeEventAction(config, car)
} else {
action = getDistanceChangeAction(config, car)
}

if action == "" || car.GarageDoor.OpLock {
Expand All @@ -119,6 +105,45 @@ func CheckGeoFence(config util.ConfigStruct, car *util.Car) {
car.GarageDoor.OpLock = false // release garage door's operation lock
}

// gets action based on if there was a relevant distance change
func getDistanceChangeAction(config util.ConfigStruct, car *util.Car) string {
if car.CurLat == 0 || car.CurLng == 0 {
return "" // need valid lat and lng to check fence
}

// Define a carLocation to check
carLocation := util.Point{
Lat: car.CurLat,
Lng: car.CurLng,
}

// update car's current distance, and store the previous distance in a variable
prevDistance := car.CurDistance
car.CurDistance = distance(carLocation, car.GarageDoor.Location)

// check if car has crossed a geofence and set an appropriate action
var action string
if prevDistance <= car.GarageDoor.CloseRadius && car.CurDistance > car.GarageDoor.CloseRadius { // car was within close geofence, but now beyond it (car left geofence)
action = myq.ActionClose
} else if prevDistance >= car.GarageDoor.OpenRadius && car.CurDistance < car.GarageDoor.OpenRadius { // car was outside of open geofence, but is now within it (car entered geofence)
action = myq.ActionOpen
}
return action
}

// gets action based on if there was a relevant geofence event change
func getGeoChangeEventAction(config util.ConfigStruct, car *util.Car) string {
var action string
if car.PrevGeofence == car.GarageDoor.TriggerCloseGeofence.From &&
car.CurGeofence == car.GarageDoor.TriggerCloseGeofence.To {
action = "close"
} else if car.PrevGeofence == car.GarageDoor.TriggerOpenGeofence.From &&
car.CurGeofence == car.GarageDoor.TriggerOpenGeofence.To {
action = "open"
}
return action
}

func setGarageDoor(config util.ConfigStruct, deviceSerial string, action string) error {
s := myqExec
s.New()
Expand Down
Loading