-
Notifications
You must be signed in to change notification settings - Fork 4
/
zone_actions.go
82 lines (70 loc) · 2.56 KB
/
zone_actions.go
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
package worker
import (
"context"
"encoding/json"
"fmt"
"time"
"github.com/calvinmclean/automated-garden/garden-app/pkg"
"github.com/calvinmclean/automated-garden/garden-app/pkg/action"
"github.com/calvinmclean/automated-garden/garden-app/pkg/influxdb"
)
// ExecuteZoneAction will execute a ZoneAction
func (w *Worker) ExecuteZoneAction(g *pkg.Garden, z *pkg.Zone, input *action.ZoneAction) error {
if input.Water != nil {
err := w.ExecuteWaterAction(g, z, input.Water)
if err != nil {
return fmt.Errorf("unable to execute WaterAction: %v", err)
}
}
return nil
}
// ExecuteWaterAction sends the message over MQTT to the embedded garden controller. Before doing this, it
// will first check if watering is set to skip and if the moisture value is below the threshold
// if configured
func (w *Worker) ExecuteWaterAction(g *pkg.Garden, z *pkg.Zone, input *action.WaterAction) error {
if z.WaterSchedule.MinimumMoisture > 0 && !input.IgnoreMoisture {
ctx, cancel := context.WithTimeout(context.Background(), influxdb.QueryTimeout)
defer cancel()
defer w.influxdbClient.Close()
moisture, err := w.influxdbClient.GetMoisture(ctx, *z.Position, g.TopicPrefix)
if err != nil {
return fmt.Errorf("error getting Zone's moisture data: %v", err)
}
if moisture > float64(z.WaterSchedule.MinimumMoisture) {
return fmt.Errorf("moisture value %.2f%% is above threshold %d%%", moisture, z.WaterSchedule.MinimumMoisture)
}
}
if w.weatherClient != nil && z.HasWeatherControl() && !input.IgnoreWeather {
// Ignore weather errors and proceed with watering
shouldWater, _ := w.shouldWaterZone(z)
// TODO: Refactor to be able to return warnings so they can be logged without returning an error
if !shouldWater {
return fmt.Errorf("rain control determined that watering should be skipped")
}
}
msg, err := json.Marshal(action.WaterMessage{
Duration: input.Duration,
ZoneID: z.ID,
Position: *z.Position,
})
if err != nil {
return fmt.Errorf("unable to marshal WaterMessage to JSON: %v", err)
}
topic, err := w.mqttClient.WaterTopic(g.TopicPrefix)
if err != nil {
return fmt.Errorf("unable to fill MQTT topic template: %v", err)
}
return w.mqttClient.Publish(topic, msg)
}
func (w *Worker) shouldWaterZone(z *pkg.Zone) (bool, error) {
intervalDuration, err := time.ParseDuration(z.WaterSchedule.Interval)
if err != nil {
return false, err
}
totalRain, err := w.weatherClient.GetTotalRain(intervalDuration)
if err != nil {
return false, err
}
// if rain < threshold, still water
return totalRain < z.WaterSchedule.WeatherControl.Rain.Threshold, nil
}