package device import ( "bytes" "fmt" "text/template" "time" ) 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 weekday := weekday(t) daytime := daytime(t) // Recursive func to find setpoint on weekday get := func(weekday DayOfWeek, daytime int) (Setpoint, bool) { for _, sp := range p[weekday] { if daytime <= sp.Start { return sp, true } } return Setpoint{}, false } startweekday := weekday for { if setpoint, ok := get(weekday, daytime); ok { 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 time.Time{}, Error("shouldn't happen no setpoint found over the week") } } } 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, }