Documentation
¶
Index ¶
- Variables
- func TimeLocationFromOffset(offsetMinutes string) (*time.Location, error)
- func ValidateScaleControl(sc *weather.ScaleControl) error
- func ValidateWeatherControl(wc *weather.Control) error
- type ActivePeriod
- type ControllerConfig
- type ControllerConfigMessage
- type Duration
- func (d *Duration) MarshalJSON() ([]byte, error)
- func (d *Duration) MarshalYAML() (any, error)
- func (d *Duration) SchedulerFunc(s *gocron.Scheduler) *gocron.Scheduler
- func (d *Duration) UnmarshalJSON(data []byte) error
- func (d *Duration) UnmarshalText(data []byte) error
- func (d *Duration) UnmarshalYAML(value *yaml.Node) error
- type EndDateable
- type Garden
- func (g *Garden) Bind(r *http.Request) error
- func (g *Garden) EndDated() bool
- func (g *Garden) GetID() string
- func (g *Garden) GetNotificationClientID() string
- func (g *Garden) GetNotificationSettings() NotificationSettings
- func (g *Garden) GetVersion() uint
- func (g *Garden) HasTemperatureHumiditySensor() bool
- func (g *Garden) ParentID() string
- func (g *Garden) Patch(newGarden *Garden) *babyapi.ErrResponse
- func (g *Garden) Render(_ http.ResponseWriter, _ *http.Request) error
- func (g *Garden) SetEndDate(now time.Time)
- func (g *Garden) SetVersion(v uint)
- func (g *Garden) String() string
- type GardenHealth
- type HealthStatus
- type LightSchedule
- type LightState
- type NextWaterDetails
- type NotificationSettings
- type RainData
- type StartTime
- type TemperatureData
- type WaterHistory
- type WaterHistoryProgress
- type WaterRoutine
- type WaterRoutineStep
- type WaterSchedule
- func (ws *WaterSchedule) Bind(r *http.Request) error
- func (ws *WaterSchedule) EndDated() bool
- func (ws *WaterSchedule) GetID() string
- func (ws *WaterSchedule) GetNotificationClientID() string
- func (ws *WaterSchedule) GetVersion() uint
- func (ws *WaterSchedule) HasRainControl() bool
- func (ws *WaterSchedule) HasTemperatureControl() bool
- func (ws *WaterSchedule) HasWeatherControl() bool
- func (ws *WaterSchedule) IsActive(now time.Time) bool
- func (ws *WaterSchedule) ParentID() string
- func (ws *WaterSchedule) Patch(newWaterSchedule *WaterSchedule) *babyapi.ErrResponse
- func (ws *WaterSchedule) Render(_ http.ResponseWriter, _ *http.Request) error
- func (ws *WaterSchedule) SetEndDate(now time.Time)
- func (ws *WaterSchedule) SetVersion(v uint)
- func (ws *WaterSchedule) String() string
- type WaterStatus
- type WeatherData
- type Zone
- func (z *Zone) Bind(r *http.Request) error
- func (z *Zone) EndDated() bool
- func (z *Zone) GetID() string
- func (z *Zone) GetVersion() uint
- func (z Zone) ParentID() string
- func (z *Zone) Patch(newZone *Zone) *babyapi.ErrResponse
- func (z *Zone) Render(_ http.ResponseWriter, _ *http.Request) error
- func (z *Zone) SetEndDate(now time.Time)
- func (z *Zone) SetVersion(v uint)
- func (z *Zone) String() string
- type ZoneAndGarden
- type ZoneDetails
Constants ¶
This section is empty.
Variables ¶
var ( // ErrElapsedExceedsDuration occurs when watering Started but is still not reported as Complete after // the duration has been exceeded. This likely means that the controller was interrupted during watering ErrElapsedExceedsDuration = errors.New("elapsed time exceeds expected watering duration") // ErrSentButNotStarted occurs if the latest watering is Sent, but not Started and the previous watering // is Completed or Started with ErrElapsedExceedsDuration ErrSentButNotStarted = errors.New("watering was sent but never started") )
Functions ¶
func TimeLocationFromOffset ¶
TimeLocationFromOffset uses an offset minutes from JS `new Date().getTimezoneOffset()` and parses it into Go's time.Location. JS offsets are positive if they are behind UTC
func ValidateScaleControl ¶
func ValidateScaleControl(sc *weather.ScaleControl) error
ValidateScaleControl validates input for ScaleControl
func ValidateWeatherControl ¶
ValidateWeatherControl validates input for the WeatherControl of a WaterSchedule
Types ¶
type ActivePeriod ¶
type ActivePeriod struct {
StartMonth string `json:"start_month" yaml:"start_month"`
EndMonth string `json:"end_month" yaml:"end_month"`
// contains filtered or unexported fields
}
ActivePeriod contains the start and end months for when a WaterSchedule should be considered active. Both of these constraints are inclusive
func (*ActivePeriod) Patch ¶
func (ap *ActivePeriod) Patch(newActivePeriod *ActivePeriod)
Patch allows for easily updating/editing an ActivePeriod
func (*ActivePeriod) Validate ¶
func (ap *ActivePeriod) Validate() error
Validate parses the Month strings to make sure they are valid
type ControllerConfig ¶
type ControllerConfig struct {
ValvePins []uint `json:"valve_pins,omitempty"`
PumpPins []uint `json:"pump_pins,omitempty"`
LightPin *uint `json:"light_pin,omitempty"`
TemperatureHumidityPin *uint `json:"temperature_humidity_pin,omitempty"`
TemperatureHumidityInterval *Duration `json:"temperature_humidity_interval,omitempty"`
}
ControllerConfig is the configuration used for an
func (*ControllerConfig) Patch ¶
func (c *ControllerConfig) Patch(newVal *ControllerConfig) *babyapi.ErrResponse
func (*ControllerConfig) PumpPin ¶
func (c *ControllerConfig) PumpPin(i uint) string
PumpPin gets the config's pump pin at a specific index, if it exists, as a string representation for templating
func (*ControllerConfig) ToMessage ¶
func (c *ControllerConfig) ToMessage() ControllerConfigMessage
ToMessage converts ControllerConfig to a struct compatible with the controller
func (*ControllerConfig) ValvePin ¶
func (c *ControllerConfig) ValvePin(i uint) string
ValvePin gets the config's valve pin at a specific index, if it exists, as a string representation for templating
type ControllerConfigMessage ¶
type ControllerConfigMessage struct {
NumZones uint `json:"num_zones"`
ValvePins []uint `json:"valve_pins"`
PumpPins []uint `json:"pump_pins"`
LightEnabled bool `json:"light"`
LightPin uint `json:"light_pin"`
TemperatureHumidityEnabled bool `json:"temp_humidity"`
TemperatureHumidityPin uint `json:"temp_humidity_pin"`
TemperatureHumidityInterval uint `json:"temp_humidity_interval"`
}
ControllerConfigMessage is similar to ControllerConfig, but is the actual value published to the controller. This allows the actual user-facing config to be simplified. This is defined here instead of where it's used because this makes it easier to keep consistent with the ControllerConfig type
type Duration ¶
Duration is a wrapper around the time.Duration that allows it to be used as interger or string representation. It also supports inputting a cron string as an interval instead if using the prefix "cron:"
func (*Duration) MarshalJSON ¶
MarshalJSON will convert Duration into the string representation
func (*Duration) MarshalYAML ¶
MarshalYAML will convert Duration into the string representation
func (*Duration) SchedulerFunc ¶
SchedulerFunc is a wrapper around gocron's fluent style to easily choose the cron or duration-based scheduling
func (*Duration) UnmarshalJSON ¶
UnmarshalJSON with allow reading a Duration as a string or integer into time.Duration
func (*Duration) UnmarshalText ¶
UnmarshalText is used for HTML form decoding. It doesn't know if it's an integer or string representation so it will try both. It parses integer input as milliseconds
type EndDateable ¶
type Garden ¶
type Garden struct {
Name string `json:"name" yaml:"name,omitempty"`
TopicPrefix string `json:"topic_prefix,omitempty" yaml:"topic_prefix,omitempty"`
ID babyapi.ID `json:"id" yaml:"id,omitempty"`
MaxZones *uint `json:"max_zones" yaml:"max_zones"`
CreatedAt *time.Time `json:"created_at" yaml:"created_at,omitempty"`
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"`
NotificationSettings *NotificationSettings `json:"notification_settings,omitempty" yaml:"notification_settings,omitempty"`
ControllerConfig *ControllerConfig `json:"controller_config,omitempty" yaml:"controller_config,omitempty"`
Version uint `json:"version,omitempty" yaml:"version"`
}
Garden is the representation of a single garden-controller device
func (*Garden) GetNotificationClientID ¶
func (*Garden) GetNotificationSettings ¶
func (g *Garden) GetNotificationSettings() NotificationSettings
func (*Garden) GetVersion ¶
func (*Garden) HasTemperatureHumiditySensor ¶
HasTemperatureHumiditySensor determines if the Garden has a sensor configured
func (*Garden) Patch ¶
func (g *Garden) Patch(newGarden *Garden) *babyapi.ErrResponse
Patch allows for easily updating individual fields of a Garden by passing in a new Garden containing the desired values
func (*Garden) SetEndDate ¶
func (*Garden) SetVersion ¶
type GardenHealth ¶
type GardenHealth struct {
Status HealthStatus `json:"status,omitempty"`
Details string `json:"details,omitempty"`
LastContact *time.Time `json:"last_contact,omitempty"`
}
GardenHealth holds information about the Garden controller's health status
type HealthStatus ¶
type HealthStatus string
const ( HealthStatusDown HealthStatus = "DOWN" HealthStatusUp HealthStatus = "UP" HealthStatusUnknown HealthStatus = "N/A" )
type LightSchedule ¶
type LightSchedule struct {
Duration *Duration `json:"duration" yaml:"duration"`
StartTime *StartTime `json:"start_time" yaml:"start_time"`
}
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")
func (LightSchedule) ExpectedStateAtTime ¶
func (ls LightSchedule) ExpectedStateAtTime(now time.Time) LightState
ExpectedStateAtTime returns the expected state for a LightSchedule at a specific time
func (LightSchedule) NextChange ¶
func (ls LightSchedule) NextChange(now time.Time) (time.Time, LightState)
NextChange determines what the next LightState change will be and at what time. For example, consider a LightSchedule that turns on at 8PM for 12 hours. At 7PM, this will return (8PM, ON). At 9PM, it returns (8AM, OFF).
func (*LightSchedule) Patch ¶
func (ls *LightSchedule) Patch(newLightSchedule *LightSchedule)
Patch allows modifying the struct in-place with values from a different instance
type LightState ¶
type LightState int
LightState is an enum representing the state of a Light (ON or OFF)
const ( // LightStateOff is the value used to turn off a light LightStateOff LightState = iota // LightStateOn is the value used to turn on a light LightStateOn // LightStateToggle is the empty value that results in toggling LightStateToggle )
func (LightState) MarshalJSON ¶
func (l LightState) MarshalJSON() ([]byte, error)
MarshalJSON will convert LightState into it's JSON string representation
func (LightState) String ¶
func (l LightState) String() string
Return the string representation of this LightState
func (*LightState) UnmarshalJSON ¶
func (l *LightState) UnmarshalJSON(data []byte) error
UnmarshalJSON with convert LightState's JSON string representation, ignoring case, into a LightState
func (*LightState) UnmarshalText ¶
func (l *LightState) UnmarshalText(data []byte) error
type NextWaterDetails ¶
type NextWaterDetails struct {
Time *time.Time `json:"time,omitempty"`
Duration string `json:"duration,omitempty"`
WaterScheduleID *xid.ID `json:"water_schedule_id,omitempty"`
Message string `json:"message,omitempty"`
}
NextWaterDetails has information about the next time this WaterSchedule will be used
type NotificationSettings ¶
type NotificationSettings struct {
ControllerStartup bool `json:"controller_startup" yaml:"controller_startup"`
LightSchedule bool `json:"light_schedule" yaml:"light_schedule"`
Downtime *Duration `json:"downtime" yaml:"downtime"`
WateringStarted bool `json:"watering_started" yaml:"watering_started"`
WateringComplete bool `json:"watering_complete" yaml:"watering_complete"`
}
type RainData ¶
RainData shows the total rain in the last watering interval and the scaling factor it would result in
type StartTime ¶
StartTime allows for special handling of Time without the date and also allows several formats for decoding so it is more easily compatible with HTML forms
func NewStartTime ¶
func StartTimeFromString ¶
func (*StartTime) MarshalJSON ¶
func (StartTime) OnDate ¶
OnDate takes the StartTime hour/minute/second and applies to the date on the input
func (*StartTime) UnmarshalJSON ¶
type TemperatureData ¶
type TemperatureData struct {
Celsius float32 `json:"celsius"`
ScaleFactor float32 `json:"scale_factor"`
}
TemperatureData shows the average high temperatures in the last watering interval and the scaling factor it would result in
type WaterHistory ¶
type WaterHistory struct {
Duration Duration `json:"duration" mapstructure:"duration"`
EventID string `json:"event_id" mapstructure:"event_id"`
Status WaterStatus `json:"status" mapstructure:"status"`
Source string `json:"source" mapstructure:"source"`
SentAt time.Time `json:"sent_at" mapstructure:"sent_at"`
StartedAt time.Time `json:"started_at,omitzero" mapstructure:"started_at"`
CompletedAt time.Time `json:"completed_at,omitzero" mapstructure:"completed_at"`
}
WaterHistory holds information about a WaterEvent that occurred in the past
type WaterHistoryProgress ¶
type WaterHistoryProgress struct {
Duration Duration
Elapsed Duration
Progress float32
Queue uint
Error error
}
WaterHistoryProgress is used to show watering progress or errors in the UI
func CalculateWaterProgress ¶
func CalculateWaterProgress(history []WaterHistory) WaterHistoryProgress
CalculateWaterProgress parses the WaterHistory to create WaterHistoryProgress based on the recent entries. If the most recent event is Started, this calculates how far along it is. If the most recent event is Completed, it will show for 1 hour and then is considered irrelevant. If most recent events are Sent, this counts the "Queue" until the most recent Started or Completed event.
There are a few scenarios that will be presented as an error:
- ErrElapsedExceedsDuration: Status is Started, but the elapsed time since then exceeds the specified duration. This means the controller was likely interrupted before completing
- ErrSentButNotStarted: Status is Sent and the previous event was Completed > 1s ago, meaning the controller is likely offline or was interrupted before starting watering
func (WaterHistoryProgress) OneSecondProgress ¶
func (p WaterHistoryProgress) OneSecondProgress() float32
OneSecondProgress is the amount that Progress will increase every second. This is used for incrementing a dynamic progress bar in the UI
func (WaterHistoryProgress) Percent ¶
func (p WaterHistoryProgress) Percent() string
type WaterRoutine ¶
type WaterRoutine struct {
ID babyapi.ID `json:"id" yaml:"id"`
Name string `json:"name" yaml:"name"`
Steps []WaterRoutineStep `json:"steps" yaml:"steps"`
}
WaterRoutine allows watering multiple Zones sequentially with one request
func (WaterRoutine) GetID ¶
func (wr WaterRoutine) GetID() string
func (WaterRoutine) ParentID ¶
func (wr WaterRoutine) ParentID() string
func (*WaterRoutine) Render ¶
func (wr *WaterRoutine) Render(_ http.ResponseWriter, _ *http.Request) error
type WaterRoutineStep ¶
type WaterRoutineStep struct {
ZoneID babyapi.ID `json:"zone_id" yaml:"zone_id"`
Duration *Duration `json:"duration" yaml:"duration"`
}
WaterRoutineStep specifies a Zone and Duration to water
type WaterSchedule ¶
type WaterSchedule struct {
ID babyapi.ID `json:"id" yaml:"id"`
Duration *Duration `json:"duration" yaml:"duration"`
Interval *Duration `json:"interval" yaml:"interval"`
StartDate *time.Time `json:"start_date" yaml:"start_date"`
StartTime *StartTime `json:"start_time" yaml:"start_time"`
EndDate *time.Time `json:"end_date,omitempty" yaml:"end_date,omitempty"`
WeatherControl *weather.Control `json:"weather_control,omitempty" yaml:"weather_control,omitempty"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
ActivePeriod *ActivePeriod `json:"active_period,omitempty" yaml:"active_period,omitempty"`
NotificationClientID *string `json:"notification_client_id,omitempty" yaml:"notification_client_id,omitempty"`
Version uint `json:"version,omitempty" yaml:"version"`
}
WaterSchedule allows the user to have more control over how the Zone is watered using an Interval. StartTime specifies when the watering interval should originate from. It can be used to increase/decrease delays in watering.
func (*WaterSchedule) EndDated ¶
func (ws *WaterSchedule) EndDated() bool
EndDated returns true if the WaterSchedule is end-dated
func (*WaterSchedule) GetID ¶
func (ws *WaterSchedule) GetID() string
func (*WaterSchedule) GetNotificationClientID ¶
func (ws *WaterSchedule) GetNotificationClientID() string
func (*WaterSchedule) GetVersion ¶
func (ws *WaterSchedule) GetVersion() uint
func (*WaterSchedule) HasRainControl ¶
func (ws *WaterSchedule) HasRainControl() bool
HasRainControl is used to determine if rain conditions should be checked before watering the Zone
func (*WaterSchedule) HasTemperatureControl ¶
func (ws *WaterSchedule) HasTemperatureControl() bool
HasTemperatureControl is used to determine if configuration is available for environmental scaling
func (*WaterSchedule) HasWeatherControl ¶
func (ws *WaterSchedule) HasWeatherControl() bool
HasWeatherControl is used to determine if weather conditions should be checked before watering the Zone This checks that WeatherControl is defined and has at least one type of control configured
func (*WaterSchedule) IsActive ¶
func (ws *WaterSchedule) IsActive(now time.Time) bool
IsActive determines if the WaterSchedule is currently in it's ActivePeriod. Always true if no ActivePeriod is configured
func (*WaterSchedule) ParentID ¶
func (ws *WaterSchedule) ParentID() string
func (*WaterSchedule) Patch ¶
func (ws *WaterSchedule) Patch(newWaterSchedule *WaterSchedule) *babyapi.ErrResponse
Patch allows modifying the struct in-place with values from a different instance
func (*WaterSchedule) Render ¶
func (ws *WaterSchedule) Render(_ http.ResponseWriter, _ *http.Request) error
func (*WaterSchedule) SetEndDate ¶
func (ws *WaterSchedule) SetEndDate(now time.Time)
func (*WaterSchedule) SetVersion ¶
func (ws *WaterSchedule) SetVersion(v uint)
type WaterStatus ¶
type WaterStatus string
const ( WaterStatusSent WaterStatus = "sent" WaterStatusStarted WaterStatus = "start" WaterStatusCompleted WaterStatus = "complete" )
type WeatherData ¶
type WeatherData struct {
Rain *RainData `json:"rain,omitempty"`
Temperature *TemperatureData `json:"average_temperature,omitempty"`
}
WeatherData is used to represent the data used for WeatherControl to a user
type Zone ¶
type Zone struct {
Name string `json:"name" yaml:"name,omitempty"`
Details *ZoneDetails `json:"details,omitempty" yaml:"details,omitempty"`
ID babyapi.ID `json:"id" yaml:"id,omitempty"`
GardenID xid.ID `json:"garden_id" yaml:"garden_id,omitempty"`
Position *uint `json:"position" yaml:"position"`
CreatedAt *time.Time `json:"created_at" yaml:"created_at,omitempty"`
EndDate *time.Time `json:"end_date,omitempty" yaml:"end_date,omitempty"`
WaterScheduleIDs []xid.ID `json:"water_schedule_ids" yaml:"water_schedule_ids"`
SkipCount *uint `json:"skip_count" yaml:"skip_count"`
Version uint `json:"version,omitempty" yaml:"version"`
}
Zone represents a "waterable resource" that is owned by a Garden.. This allows for more complex Garden setups where a large irrigation system will be watering entire groups of Zones rather than watering individually. This contains the important information for managing WaterSchedules and some additional details describing the Zone. The Position is an integer that tells the controller which part of hardware needs to be switched on to start watering
func (*Zone) GetVersion ¶
func (*Zone) Patch ¶
func (z *Zone) Patch(newZone *Zone) *babyapi.ErrResponse
Patch allows for easily updating individual fields of a Zone by passing in a new Zone containing the desired values
func (*Zone) SetEndDate ¶
func (*Zone) SetVersion ¶
type ZoneAndGarden ¶
ZoneAndGarden allows grouping the Zone and Garden it belongs too and is useful in some cases where both are needed in a return value
type ZoneDetails ¶
type ZoneDetails struct {
Description string `json:"description,omitempty" yaml:"description,omitempty"`
Notes string `json:"notes,omitempty" yaml:"notes,omitempty"`
}
ZoneDetails is a struct holding some additional details about a Zone that are primarily for user convenience and are generally not used by the application
func (*ZoneDetails) Patch ¶
func (zd *ZoneDetails) Patch(newZoneDetails *ZoneDetails)
Patch allows modifying the struct in-place with values from a different instance