mirror of
https://github.com/nikdoof/hapz2m.git
synced 2026-01-30 03:28:18 +00:00
Record & persist device last_seen time
This will allow us to mark devices as non-responsive if they haven't been seen in some time.
This commit is contained in:
54
bridge.go
54
bridge.go
@@ -32,6 +32,9 @@ const (
|
|||||||
|
|
||||||
// timeout for UpdateZ2MState
|
// timeout for UpdateZ2MState
|
||||||
Z2M_UPDATE_TIMEOUT = 3 * time.Second
|
Z2M_UPDATE_TIMEOUT = 3 * time.Second
|
||||||
|
|
||||||
|
// timeout for marking devices as non-responsive
|
||||||
|
Z2M_LAST_SEEN_TIMEOUT = 24 * time.Hour
|
||||||
)
|
)
|
||||||
|
|
||||||
const BRIDGE_DEBUG = false
|
const BRIDGE_DEBUG = false
|
||||||
@@ -64,6 +67,7 @@ type BridgeDevice struct {
|
|||||||
Device *Device
|
Device *Device
|
||||||
Accessory *accessory.A
|
Accessory *accessory.A
|
||||||
Mappings map[string]*ExposeMapping
|
Mappings map[string]*ExposeMapping
|
||||||
|
LastSeen time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates and initializes a Bridge.
|
// Creates and initializes a Bridge.
|
||||||
@@ -179,6 +183,25 @@ func (br *Bridge) loadZ2MState(m *sync.Map) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range stateMap {
|
for k, v := range stateMap {
|
||||||
|
// add a last_seen timestamp for saved states without one
|
||||||
|
var devState map[string]any
|
||||||
|
err = json.Unmarshal(v, &devState)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("cannot unmarshal device %s JSON: %v", k, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, hasLastSeen := devState["last_seen"]; !hasLastSeen {
|
||||||
|
devState["last_seen"] = time.Now().Add(-Z2M_LAST_SEEN_TIMEOUT)
|
||||||
|
|
||||||
|
// re-marshal the JSON
|
||||||
|
newJson, err := json.Marshal(devState)
|
||||||
|
if err == nil {
|
||||||
|
v = newJson
|
||||||
|
} else {
|
||||||
|
log.Printf("cannot re-marshal JSON state after adding last_seen for %s: %v", k, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// LoadOrStore retains existing data, only storing if empty
|
// LoadOrStore retains existing data, only storing if empty
|
||||||
if _, exists := m.LoadOrStore(k, v); exists {
|
if _, exists := m.LoadOrStore(k, v); exists {
|
||||||
log.Printf("skipping %s, newer data is available", k)
|
log.Printf("skipping %s, newer data is available", k)
|
||||||
@@ -211,7 +234,10 @@ func (br *Bridge) saveZ2MState() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// serialize into JSON
|
// serialize into JSON
|
||||||
if len(devState) > 0 {
|
lastSeenSince := time.Since(dev.LastSeen)
|
||||||
|
if len(devState) > 0 || lastSeenSince < Z2M_LAST_SEEN_TIMEOUT {
|
||||||
|
devState["last_seen"] = dev.LastSeen.Unix() * 1000 // timestamp in millis
|
||||||
|
|
||||||
jsonState, err := json.Marshal(devState)
|
jsonState, err := json.Marshal(devState)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -366,6 +392,8 @@ func (br *Bridge) AddDevice(dev *Device, acc *accessory.A, mappings []*ExposeMap
|
|||||||
em[prop] = m
|
em[prop] = m
|
||||||
}
|
}
|
||||||
|
|
||||||
|
brdev := &BridgeDevice{dev, acc, em, time.Date(2000, 01, 01, 23, 59, 00, 0, time.Local)}
|
||||||
|
|
||||||
// wire up accessory's remote value update functions
|
// wire up accessory's remote value update functions
|
||||||
for _, m := range mappings {
|
for _, m := range mappings {
|
||||||
m := m
|
m := m
|
||||||
@@ -384,7 +412,7 @@ func (br *Bridge) AddDevice(dev *Device, acc *accessory.A, mappings []*ExposeMap
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
br.devices[name] = &BridgeDevice{dev, acc, em}
|
br.devices[name] = brdev
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -482,6 +510,28 @@ func (br *Bridge) UpdateAccessoryState(devName string, payload []byte) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lastSeen := time.Now()
|
||||||
|
if lastSeenProp, found := newState["last_seen"]; found {
|
||||||
|
switch v := lastSeenProp.(type) {
|
||||||
|
case float64:
|
||||||
|
lastSeen = time.Unix(int64(v/1000), 0)
|
||||||
|
case string:
|
||||||
|
if lastSeenDate, err := time.Parse(time.RFC3339, v); err == nil {
|
||||||
|
lastSeen = lastSeenDate
|
||||||
|
} else {
|
||||||
|
log.Printf("invalid last_seen timestamp %v", v)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
log.Printf("invalid last_seen %T %[1]v", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update LastSeen only if it was valid
|
||||||
|
if lastSeen.After(dev.LastSeen) {
|
||||||
|
//log.Printf("updating last seen for %s to %s", devName, lastSeen)
|
||||||
|
dev.LastSeen = lastSeen
|
||||||
|
}
|
||||||
|
|
||||||
for prop, mapping := range dev.Mappings {
|
for prop, mapping := range dev.Mappings {
|
||||||
newVal, exists := newState[prop]
|
newVal, exists := newState[prop]
|
||||||
if !exists {
|
if !exists {
|
||||||
|
|||||||
Reference in New Issue
Block a user