-
Notifications
You must be signed in to change notification settings - Fork 1
/
doorLogic.go
117 lines (99 loc) · 3.03 KB
/
doorLogic.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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
package main
import (
"log"
"strconv"
"time"
)
type iDoorSensor interface {
IsOpen() bool
}
// DoorLogic object
type DoorLogic struct {
doorSensor iDoorSensor
onOpen func()
onClose func()
onForgot func()
onHeartbeat func()
doorOpenWaitDuration time.Duration
heartbeatSeconds int
isOpen bool
}
// NewDoorLogic creates a new DoorLogic object that you Run() and then it'll fire callbacks on events to the door.
func NewDoorLogic(
doorSensor iDoorSensor,
onOpen func(),
onClose func(),
onForgot func(),
onHeartbeat func(),
doorOpenWaitSeconds int,
heartbeatSeconds int) DoorLogic {
// https://www.calhoun.io/6-tips-for-using-strings-in-go/
// https://www.ardanlabs.com/blog/2013/06/gos-duration-type-unravelled.html
doorOpenWaitDuration, _ := time.ParseDuration(strconv.Itoa(doorOpenWaitSeconds) + "s")
doorLogic := DoorLogic{doorSensor, onOpen, onClose, onForgot, onHeartbeat, doorOpenWaitDuration, heartbeatSeconds, false}
return doorLogic
}
// Run starts the DoorLogic object listening. It'll listen forever.
// Modeled after this example: https://github.com/stianeikeland/go-rpio/blob/master/examples/event/event.go
func (ds DoorLogic) Run() {
log.Print("Listening for door sensor")
var currentForgotTime *time.Time
isForgotten := false
// Set up heartbeat
var nextHeartbeatTime time.Time
if ds.heartbeatSeconds > 0 {
nextHeartbeatTime = ds.getNextHeartbeatTime()
log.Printf("Heartbeat every %v seconds", ds.heartbeatSeconds)
} else {
log.Print("Heartbeat disabled")
}
// Loop forever listing for changes in the sensor state
for true {
// Get new state of sensor
currentlyOpen := ds.doorSensor.IsOpen()
// Send forgot alarm if open for too long
if currentForgotTime != nil && time.Now().After(*currentForgotTime) {
currentForgotTime = nil
isForgotten = true
ds.onForgot()
}
// Check if state has changed
if currentlyOpen != ds.isOpen {
ds.isOpen = currentlyOpen
ds.printState()
if ds.isOpen {
newForgotTime := time.Now().Add(ds.doorOpenWaitDuration)
currentForgotTime = &newForgotTime // Need to get reference
ds.onOpen()
} else {
currentForgotTime = nil
// Only send the door closed message if it was forgotten open
if isForgotten {
isForgotten = false
ds.onClose()
}
}
}
// Send heartbeat
if ds.heartbeatSeconds > 0 && time.Now().After(nextHeartbeatTime) {
ds.onHeartbeat()
nextHeartbeatTime = ds.getNextHeartbeatTime()
}
// Sleep half second before next loop
time.Sleep(time.Second / 2)
}
log.Print("Done listening for door sensor")
}
// getNextHeartbeatTime returns the next time a heartbeat message should be sent
func (ds DoorLogic) getNextHeartbeatTime() time.Time {
heartbeatDuration, _ := time.ParseDuration(strconv.Itoa(ds.heartbeatSeconds) + "s")
return time.Now().Add(heartbeatDuration)
}
// printState prints the current open/closed state of the door
func (ds DoorLogic) printState() {
if ds.isOpen {
log.Print("Door open")
} else {
log.Print("Door closed")
}
}