diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml
index 40b77b3e..8d8669b7 100644
--- a/deploy/docker-compose.yml
+++ b/deploy/docker-compose.yml
@@ -3,7 +3,6 @@
# - run-local: run required services + extras like Grafana and Prometheus
# - demo: run everything, including an instance of garden-app and garden-controller
-version: "3.9"
services:
grafana:
image: "grafana/grafana:latest"
diff --git a/garden-app/pkg/garden.go b/garden-app/pkg/garden.go
index 79e01c7a..bfc65dfc 100644
--- a/garden-app/pkg/garden.go
+++ b/garden-app/pkg/garden.go
@@ -30,6 +30,7 @@ type Garden struct {
EndDate *time.Time `json:"end_date,omitempty" yaml:"end_date,omitempty"`
LightSchedule *LightSchedule `json:"light_schedule,omitempty" yaml:"light_schedule,omitempty"`
TemperatureHumiditySensor *bool `json:"temperature_humidity_sensor,omitempty" yaml:"temperature_humidity_sensor,omitempty"`
+ NotificationClientID *string `json:"notification_client_id,omitempty" yaml:"notification_client_id,omitempty"`
}
func (g *Garden) GetID() string {
@@ -41,6 +42,14 @@ func (g *Garden) String() string {
return fmt.Sprintf("%+v", *g)
}
+func (g *Garden) GetNotificationClientID() string {
+ if g.NotificationClientID == nil {
+ return ""
+ }
+
+ return *g.NotificationClientID
+}
+
// GardenHealth holds information about the Garden controller's health status
type GardenHealth struct {
Status HealthStatus `json:"status,omitempty"`
@@ -117,8 +126,7 @@ func (g *Garden) Patch(newGarden *Garden) *babyapi.ErrResponse {
// If both Duration and StartTime are empty, remove the schedule
if newGarden.LightSchedule.Duration == nil &&
- newGarden.LightSchedule.StartTime == nil &&
- newGarden.LightSchedule.NotificationClientID == nil {
+ newGarden.LightSchedule.StartTime == nil {
g.LightSchedule = nil
}
}
@@ -126,6 +134,10 @@ func (g *Garden) Patch(newGarden *Garden) *babyapi.ErrResponse {
g.TemperatureHumiditySensor = newGarden.TemperatureHumiditySensor
}
+ if newGarden.NotificationClientID != nil {
+ g.NotificationClientID = newGarden.NotificationClientID
+ }
+
return nil
}
diff --git a/garden-app/pkg/light_schedule.go b/garden-app/pkg/light_schedule.go
index b5c316e1..c893e8da 100644
--- a/garden-app/pkg/light_schedule.go
+++ b/garden-app/pkg/light_schedule.go
@@ -66,10 +66,9 @@ func (l *LightState) unmarshal(data []byte) error {
// LightSchedule allows the user to control when the Garden light is turned on and off
// "Time" should be in the format of LightTimeFormat constant ("15:04:05-07:00")
type LightSchedule struct {
- Duration *Duration `json:"duration" yaml:"duration"`
- StartTime *StartTime `json:"start_time" yaml:"start_time"`
- AdhocOnTime *time.Time `json:"adhoc_on_time,omitempty" yaml:"adhoc_on_time,omitempty"`
- NotificationClientID *string `json:"notification_client_id,omitempty" yaml:"notification_client_id,omitempty"`
+ Duration *Duration `json:"duration" yaml:"duration"`
+ StartTime *StartTime `json:"start_time" yaml:"start_time"`
+ AdhocOnTime *time.Time `json:"adhoc_on_time,omitempty" yaml:"adhoc_on_time,omitempty"`
}
// String...
@@ -77,14 +76,6 @@ func (ls *LightSchedule) String() string {
return fmt.Sprintf("%+v", *ls)
}
-func (ls *LightSchedule) GetNotificationClientID() string {
- if ls.NotificationClientID == nil {
- return ""
- }
-
- return *ls.NotificationClientID
-}
-
// Patch allows modifying the struct in-place with values from a different instance
func (ls *LightSchedule) Patch(new *LightSchedule) {
if new.Duration != nil {
@@ -96,7 +87,4 @@ func (ls *LightSchedule) Patch(new *LightSchedule) {
if new.AdhocOnTime == nil {
ls.AdhocOnTime = nil
}
- if new.NotificationClientID != nil {
- ls.NotificationClientID = new.NotificationClientID
- }
}
diff --git a/garden-app/pkg/storage/client.go b/garden-app/pkg/storage/client.go
index 9c7c82bb..48dece1b 100644
--- a/garden-app/pkg/storage/client.go
+++ b/garden-app/pkg/storage/client.go
@@ -9,10 +9,10 @@ import (
"github.com/calvinmclean/babyapi"
"github.com/calvinmclean/babyapi/storage/kv"
+ "github.com/mitchellh/mapstructure"
"github.com/tarmac-project/hord"
"github.com/tarmac-project/hord/drivers/hashmap"
"github.com/tarmac-project/hord/drivers/redis"
- "github.com/mitchellh/mapstructure"
)
// Config is used to identify and configure a storage client
diff --git a/garden-app/server/garden.go b/garden-app/server/garden.go
index 327ffd91..846f1598 100644
--- a/garden-app/server/garden.go
+++ b/garden-app/server/garden.go
@@ -178,15 +178,15 @@ func (api *GardensAPI) onCreateOrUpdate(_ http.ResponseWriter, r *http.Request,
}
}
- if garden.LightSchedule != nil {
- // Validate NotificationClient exists
- if garden.LightSchedule.NotificationClientID != nil {
- apiErr := checkNotificationClientExists(r.Context(), api.storageClient, *garden.LightSchedule.NotificationClientID)
- if apiErr != nil {
- return apiErr
- }
+ // Validate NotificationClient exists
+ if garden.NotificationClientID != nil {
+ apiErr := checkNotificationClientExists(r.Context(), api.storageClient, *garden.NotificationClientID)
+ if apiErr != nil {
+ return apiErr
}
+ }
+ if garden.LightSchedule != nil {
// Update the light schedule for the Garden (if it exists)
logger.Info("updating/resetting LightSchedule for Garden")
if err := api.worker.ResetLightSchedule(garden); err != nil {
diff --git a/garden-app/server/garden_test.go b/garden-app/server/garden_test.go
index cbe68c8c..8199d09d 100644
--- a/garden-app/server/garden_test.go
+++ b/garden-app/server/garden_test.go
@@ -437,7 +437,7 @@ func TestUpdateGarden(t *testing.T) {
"AddNotificationClientIDErrorNotFound",
createExampleGarden(),
nil,
- `{"light_schedule":{"notification_client_id":"NOTIFICATION_CLIENT_ID"}}`,
+ `{"notification_client_id":"NOTIFICATION_CLIENT_ID"}`,
`{"status":"Invalid request.","error":"error getting NotificationClient with ID \\"NOTIFICATION_CLIENT_ID\\": resource not found"}`,
http.StatusBadRequest,
},
@@ -445,8 +445,8 @@ func TestUpdateGarden(t *testing.T) {
"AddNotificationClientIDSuccess",
createExampleGarden(),
nil,
- `{"light_schedule":{"notification_client_id":"c5cvhpcbcv45e8bp16dg"}}`,
- `{"name":"test-garden","topic_prefix":"test-garden","id":"[0-9a-v]{20}","max_zones":2,"created_at":"\d{4}-\d{2}-\d\dT\d\d:\d\d:\d\d\.\d+(-07:00|Z)","light_schedule":{"duration":"15h0m0s","start_time":"22:00:01-07:00","notification_client_id":"c5cvhpcbcv45e8bp16dg"},"next_light_action":{"time":"0000-12-31T17:00:00-07:00","state":"OFF"},"health":{"status":"UP","details":"last contact from Garden was \d+(s|ms) ago","last_contact":"\d{4}-\d{2}-\d\dT\d\d:\d\d:\d\d\.\d+(-07:00|Z)"},"num_zones":1,"links":\[{"rel":"self","href":"/gardens/[0-9a-v]{20}"},{"rel":"zones","href":"/gardens/c5cvhpcbcv45e8bp16dg/zones"},{"rel":"action","href":"/gardens/[0-9a-v]{20}/action"}\]}`,
+ `{"notification_client_id":"c5cvhpcbcv45e8bp16dg"}`,
+ `{"name":"test-garden","topic_prefix":"test-garden","id":"[0-9a-v]{20}","max_zones":2,"created_at":"\d{4}-\d{2}-\d\dT\d\d:\d\d:\d\d\.\d+(-07:00|Z)","light_schedule":{"duration":"15h0m0s","start_time":"22:00:01-07:00"},"notification_client_id":"c5cvhpcbcv45e8bp16dg","next_light_action":{"time":"0000-12-31T17:00:00-07:00","state":"OFF"},"health":{"status":"UP","details":"last contact from Garden was \d+(s|ms) ago","last_contact":"\d{4}-\d{2}-\d\dT\d\d:\d\d:\d\d\.\d+(-07:00|Z)"},"num_zones":1,"links":\[{"rel":"self","href":"/gardens/[0-9a-v]{20}"},{"rel":"zones","href":"/gardens/c5cvhpcbcv45e8bp16dg/zones"},{"rel":"action","href":"/gardens/[0-9a-v]{20}/action"}\]}`,
http.StatusOK,
},
{
diff --git a/garden-app/server/templates/garden_modal.html b/garden-app/server/templates/garden_modal.html
index 755f97c1..cf0c30ce 100644
--- a/garden-app/server/templates/garden_modal.html
+++ b/garden-app/server/templates/garden_modal.html
@@ -43,17 +43,15 @@
{{ if .Name }}{{ .Name }}{{ else }}Create Garden{{ en
-