Gisle Aune
3 years ago
18 changed files with 515 additions and 78 deletions
-
25app/api/devices.go
-
12app/client/handler.go
-
7app/services/events.go
-
100cmd/lucy/command.go
-
65cmd/lucy/handlercmd.go
-
18cmd/lucy/help.go
-
14cmd/lucy/main.go
-
3cmd/lucy/tables.go
-
7internal/drivers/hue/bridge.go
-
38internal/drivers/hue/state.go
-
16internal/drivers/nanoleaf/bridge.go
-
12internal/drivers/nanoleaf/data.go
-
19internal/mysql/eventhandlerrepo.go
-
43models/eventhandler.go
-
21models/shared.go
-
138models/timeofday.go
-
42models/timeofday_test.go
-
13scripts/20211002131800_eventhandler_from_to.sql
@ -0,0 +1,138 @@ |
|||||
|
package models |
||||
|
|
||||
|
import ( |
||||
|
"bytes" |
||||
|
"encoding/json" |
||||
|
"fmt" |
||||
|
"strconv" |
||||
|
"strings" |
||||
|
"time" |
||||
|
) |
||||
|
|
||||
|
type TimeOfDay int |
||||
|
|
||||
|
func (t *TimeOfDay) UnmarshalJSON(v []byte) error { |
||||
|
if bytes.Equal(v, []byte("null")) { |
||||
|
*t = -1 |
||||
|
} |
||||
|
|
||||
|
var str string |
||||
|
err := json.Unmarshal(v, &str) |
||||
|
if err != nil { |
||||
|
var n int |
||||
|
err = json.Unmarshal(v, &n) |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
if n < -1 && n >= 86400 { |
||||
|
return fmt.Errorf("value outside range 0..86400: %d", n) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
t2, err := ParseTimeOfDay(str) |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
|
||||
|
*t = t2 |
||||
|
|
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
func (t TimeOfDay) MarshalJSON() ([]byte, error) { |
||||
|
if t.IsNever() { |
||||
|
return []byte("null"), nil |
||||
|
} |
||||
|
|
||||
|
return json.Marshal(t.String()) |
||||
|
} |
||||
|
|
||||
|
const Never = TimeOfDay(-1) |
||||
|
|
||||
|
func (t *TimeOfDay) String() string { |
||||
|
if *t < 0 { |
||||
|
return "n/a" |
||||
|
} |
||||
|
|
||||
|
return fmt.Sprintf("%02d:%02d:%02d", *t/3600, (*t/60)%60, *t%60) |
||||
|
} |
||||
|
|
||||
|
func (t *TimeOfDay) StringPtr() *string { |
||||
|
if *t < 0 { |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
s := t.String() |
||||
|
return &s |
||||
|
} |
||||
|
|
||||
|
func (t TimeOfDay) IsNever() bool { |
||||
|
return t == Never |
||||
|
} |
||||
|
|
||||
|
func (t TimeOfDay) IsBetween(from TimeOfDay, to TimeOfDay) bool { |
||||
|
if from == to { |
||||
|
return t == from |
||||
|
} else if from > to { |
||||
|
return t >= from || t <= to |
||||
|
} else { |
||||
|
return t >= from && t <= to |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func CurrentTimeOfDay() TimeOfDay { |
||||
|
return TimeOfDayFromDate(time.Now().Local()) |
||||
|
} |
||||
|
|
||||
|
func TimeOfDayFromDate(date time.Time) TimeOfDay { |
||||
|
return NewTimeOfDay(date.Hour(), date.Minute(), date.Second()) |
||||
|
} |
||||
|
|
||||
|
func NewTimeOfDay(hours, minutes, seconds int) TimeOfDay { |
||||
|
return TimeOfDay(((hours % 24) * 3600) + ((minutes % 60) * 60) + seconds) |
||||
|
} |
||||
|
|
||||
|
func ParseTimeOfDayPtr(str *string) (TimeOfDay, error) { |
||||
|
if str == nil { |
||||
|
return Never, nil |
||||
|
} |
||||
|
|
||||
|
return ParseTimeOfDay(*str) |
||||
|
} |
||||
|
|
||||
|
func ParseTimeOfDay(str string) (TimeOfDay, error) { |
||||
|
if str == "" || str == "n/a" || str == "N/A" { |
||||
|
return -1, nil |
||||
|
} |
||||
|
|
||||
|
split := strings.SplitN(str, ":", 4) |
||||
|
res := 0 |
||||
|
|
||||
|
n, err := strconv.Atoi(split[0]) |
||||
|
if err != nil { |
||||
|
return Never, err |
||||
|
} |
||||
|
res += n * 3600 |
||||
|
|
||||
|
if len(split) >= 2 { |
||||
|
n, err := strconv.Atoi(split[1]) |
||||
|
if err != nil { |
||||
|
return Never, err |
||||
|
} |
||||
|
res += n * 60 |
||||
|
} |
||||
|
if len(split) >= 3 { |
||||
|
n, err := strconv.Atoi(split[2]) |
||||
|
if err != nil { |
||||
|
return Never, err |
||||
|
} |
||||
|
res += n |
||||
|
} |
||||
|
|
||||
|
if res >= 86400 || res < 0 { |
||||
|
return Never, fmt.Errorf("invalid time of day string %s (=%d)", str, res) |
||||
|
} |
||||
|
|
||||
|
return TimeOfDay(res), nil |
||||
|
} |
@ -0,0 +1,42 @@ |
|||||
|
package models |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
"testing" |
||||
|
) |
||||
|
|
||||
|
func tod(s string) TimeOfDay { |
||||
|
tod, err := ParseTimeOfDay(s) |
||||
|
if err != nil { |
||||
|
panic(err) |
||||
|
} |
||||
|
|
||||
|
return tod |
||||
|
} |
||||
|
|
||||
|
func TestTimeOfDay_IsBetween(t *testing.T) { |
||||
|
table := []struct{ |
||||
|
Value TimeOfDay |
||||
|
From TimeOfDay |
||||
|
To TimeOfDay |
||||
|
Expected bool |
||||
|
}{ |
||||
|
{ tod("16:13:11"), tod("07"), tod("21:30"), true }, |
||||
|
{ tod("16:13:31"), tod("21:30"), tod("07"), false }, |
||||
|
{ tod("23:15:32"), tod("21:30"), tod("07"), true }, |
||||
|
{ tod("04:13:57"), tod("21:30"), tod("07"), true }, |
||||
|
{ tod("16:14:43"), tod("15:30"), tod("16:00"), false }, |
||||
|
{ tod("16:14:43"), tod("15:30"), tod("16:15"), true }, |
||||
|
{ tod("08:45:43"), tod("07:00:00"), tod("21:30:00"), true }, |
||||
|
{ tod("07:00:00"), tod("07:00:00"), tod("21:30:00"), true }, |
||||
|
} |
||||
|
|
||||
|
for i, row := range table { |
||||
|
t.Run(fmt.Sprintf("row_%d", i), func (t *testing.T) { |
||||
|
if row.Value.IsBetween(row.From, row.To) != row.Expected { |
||||
|
t.Log(row.Value, row.From, row.To) |
||||
|
t.Fail() |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
} |
@ -0,0 +1,13 @@ |
|||||
|
-- +goose Up |
||||
|
-- +goose StatementBegin |
||||
|
ALTER TABLE event_handler |
||||
|
ADD COLUMN from_tod VARCHAR(255), |
||||
|
ADD COLUMN to_tod VARCHAR(255); |
||||
|
-- +goose StatementEnd |
||||
|
|
||||
|
-- +goose Down |
||||
|
-- +goose StatementBegin |
||||
|
ALTER TABLE event_handler |
||||
|
DROP COLUMN IF EXISTS from_tod, |
||||
|
DROP COLUMN IF EXISTS to_tod; |
||||
|
-- +goose StatementEnd |
Write
Preview
Loading…
Cancel
Save
Reference in new issue