heater/pkg/device/settings.go

258 lines
6.1 KiB
Go

package device
import (
"time"
"bytes"
"text/template"
"fmt"
)
type DayOfWeek int
func (d DayOfWeek) Next() DayOfWeek {
if d == Sunday {
return Monday
}
return d+1
}
func (d DayOfWeek) Previous() DayOfWeek {
if d == Monday {
return Sunday
}
return d-1
}
func (d DayOfWeek) DaysBetween(n DayOfWeek) int {
var between int
if (n < d) {
between = 7 - int(d-n)
} else {
between = int(n-d)
}
return between
}
const (
Monday DayOfWeek = 0
Thuesday DayOfWeek = 1
Wednesday DayOfWeek = 2
Thursday DayOfWeek = 3
Friday DayOfWeek = 4
Saturday DayOfWeek = 5
Sunday DayOfWeek = 6
)
func WeekDayEnToFr(weekday time.Weekday) DayOfWeek {
// translate weekday to french week, start by Monday
return map[time.Weekday]DayOfWeek {
time.Monday : Monday,
time.Tuesday : Thuesday,
time.Wednesday: Wednesday,
time.Thursday : Thursday,
time.Friday: Friday,
time.Saturday: Saturday,
time.Sunday: Sunday,
}[weekday]
}
func daytime(t time.Time) int {
return t.Hour()*60 + t.Minute()
}
func weekday(t time.Time) DayOfWeek {
return WeekDayEnToFr(t.Weekday())
}
type Message struct {
Payload []byte
Topic string
Retain bool
}
type Setpoint struct {
Start int `json:"start"`
Preset_id int `json:"preset_id"`
}
func (s Setpoint) Value(presets []Preset) (int, error) {
//TODO need test
if len(presets) < s.Preset_id + 1 {
return 0, Error(fmt.Sprintf("preset id %d not found", s.Preset_id))
}
return presets[s.Preset_id].Value, nil
}
type WeekProgram map[DayOfWeek][]Setpoint
func (p WeekProgram) Current() Setpoint {
// TODO: need test
// return current Setpoint
now := timeNow()
weekday := weekday(now)
daytime := daytime(now)
setpoint := Setpoint{}
for _, sp := range p[weekday] {
if daytime < sp.Start {
break
}
setpoint = sp
}
return setpoint
}
func (p WeekProgram) NextTime(t time.Time) (time.Time, error) {
// return next program change
setpoint := Setpoint{}
var next time.Time
weekday := weekday(t)
daytime := daytime(t)
// Recursive func to find setpoint on weekday
get := func (weekday DayOfWeek, daytime int) Setpoint {
setpoint := Setpoint{}
for _, sp := range p[weekday] {
setpoint = sp
if daytime < sp.Start {
return setpoint
}
}
return Setpoint{}
}
startweekday := weekday
for (setpoint == Setpoint{}) {
setpoint = get(weekday, daytime)
if (setpoint != Setpoint{}) {
// setpoint found, compute time
next := time.Date(t.Year(), t.Month(), t.Day() + startweekday.DaysBetween(weekday), 0, 0, 0, 0, time.Local)
next = next.Add(time.Duration( (setpoint.Start) * int(time.Minute) ))
return next, nil
}
weekday = weekday.Next()
daytime = 0
if weekday == startweekday {
return next, fmt.Errorf("Shouldn't happen no setpoint found over the week")
}
}
return next, nil
}
type Programs map[string]WeekProgram
type Preset struct {
Label string `json:"label"`
Value int `json:"value"`
Color string `json:"color"`
}
type TVRSettings struct {
Setpoint_topic string `json:"setpoint_topic"`
Setpoint_payload string `json:"setpoint_payload"`
Setpoint_state_topic string `json:"setpoint_state_topic"`
Setpoint_state_jp string `json:"setpoint_state_jp"`
}
func (s TVRSettings) FormatTopicState(device_name string) (string, error) {
type Variable struct {
Device string
}
variables := Variable{device_name}
t, err := template.New("topic").Parse(s.Setpoint_state_topic)
if err != nil {
return "", err
}
buf := new(bytes.Buffer)
err = t.Execute(buf, variables)
if err != nil {
return "", err
}
return buf.String(), nil
}
func (s TVRSettings) FormatTopic(device_name string) (string, error) {
type Variable struct {
Device string
}
variables := Variable{device_name}
t, err := template.New("topic").Parse(s.Setpoint_topic)
if err != nil {
return "", err
}
buf := new(bytes.Buffer)
err = t.Execute(buf, variables)
if err != nil {
return "", err
}
return buf.String(), nil
}
func (s TVRSettings) FormatPayload(setpoint int) (string, error) {
type Variable struct {
Setpoint int
}
variables := Variable{setpoint}
t, err := template.New("payload").Parse(s.Setpoint_payload)
if err != nil {
return "", err
}
buf := new(bytes.Buffer)
err = t.Execute(buf, variables)
if err != nil {
return "", err
}
return buf.String(), nil
}
type DeviceSettings struct {
Programs Programs `json:"programs"`
Presets []Preset `json:"presets"`
TVR TVRSettings `json:"TVR"`
}
//////////////////////////////////////////////
// Defaults
var DefaultPresets = []Preset{
{Label: "default", Value: 17, Color: "#012a36"},
{Label: "normal", Value: 19, Color: "#b6244f"},
}
var defaultSetpoints = []Setpoint{
{Start: 7*60, Preset_id: 1},
{Start: 8*60, Preset_id: 0},
{Start: 16*60, Preset_id: 1},
{Start: 22*60, Preset_id: 0},
}
var DefaultWeekProgram = WeekProgram{
Monday: defaultSetpoints,
Thuesday: defaultSetpoints,
Wednesday: defaultSetpoints,
Thursday : defaultSetpoints,
Friday: defaultSetpoints,
Saturday: defaultSetpoints,
Sunday: defaultSetpoints,
}
var DefaultPrograms = Programs{
"default": DefaultWeekProgram,
}
var DefaultTVRSettings = TVRSettings {
Setpoint_topic : "zigbee2mqtt/TVR/{{.Device}}/set",
Setpoint_payload : "{\"current_heating_setpoint\": {{.Setpoint}}}",
Setpoint_state_topic : "zigbee2mqtt/TVR/{{.Device}}",
Setpoint_state_jp : "$.current_heating_setpoint",
}
var DefaultDeviceSettings = DeviceSettings{
Programs: DefaultPrograms,
Presets: DefaultPresets,
TVR: DefaultTVRSettings,
}