Skip to content

Commit 3d128bd

Browse files
authored
RFC - connection aging (#292)
Show when Zigbee device was last seen
1 parent d0301ce commit 3d128bd

File tree

3 files changed

+70
-7
lines changed

3 files changed

+70
-7
lines changed

manifest.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"scanChannels": 8190,
2121
"allowFTDISerial": false,
2222
"allowAMASerial": false,
23-
"showBattery": false,
23+
"showAging": false,
2424
"debug": ""
2525
},
2626
"schema": {
@@ -39,15 +39,15 @@
3939
"type": "boolean",
4040
"default": false
4141
},
42-
"showBattery": {
43-
"type": "boolean",
44-
"title": "Show Battery Percentage",
45-
"description": "experimental: Show the 'batteryPercentageRemaining' attribute as a property. Note that this attribute is reported from a power constrained device with possibly low quality consumer-grade batteries. Therefore, it may not represent what you expect. Particularly, it may drop in large increments and may suddenly go from, say, 50% to 1%. This attribute is reported very infrequently, maybe as little as once a day, so may take a while to appear after being enabled."
46-
},
4742
"debug": {
4843
"type": "string",
4944
"default": ""
5045
},
46+
"showAging": {
47+
"type": "boolean",
48+
"title": "Show Aging",
49+
"description": "experimental - Creates an additional 'Last seen' property to show when each device was last active on the Zigbee network"
50+
},
5151
"zigbee2mqtt": {
5252
"title": "Zigbee2Mqtt",
5353
"type": "object",

src/zb-adapter.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,9 @@ class ZigbeeAdapter extends Adapter {
472472
// the ZHA protocol.
473473
const node = this.nodes[frame.remote64];
474474
if (node) {
475+
if (typeof node.updateLastSeen === 'function') {
476+
node.updateLastSeen();
477+
}
475478
for (const property of node.properties.values()) {
476479
if (DEBUG_flow) {
477480
console.error(node.addr64, 'bind failed - setting fireAndForget to true');
@@ -506,6 +509,9 @@ class ZigbeeAdapter extends Adapter {
506509
}
507510
const node = this.findNodeFromRxFrame(frame);
508511
if (node) {
512+
if (typeof node.updateLastSeen === 'function') {
513+
node.updateLastSeen();
514+
}
509515
node.handleZhaResponse(frame);
510516
} else {
511517
console.log('Node:', frame.remote64, frame.remote16, 'not found');
@@ -605,6 +611,11 @@ class ZigbeeAdapter extends Adapter {
605611
if (!node) {
606612
return;
607613
}
614+
615+
if (typeof node.updateLastSeen === 'function') {
616+
node.updateLastSeen();
617+
}
618+
608619
if (this.scanning) {
609620
if (DEBUG_flow) {
610621
console.log('Ignoring Match Descriptor Request - scanning in progress');
@@ -731,6 +742,9 @@ class ZigbeeAdapter extends Adapter {
731742
this.activeEndpointResponseCount++;
732743
const node = this.nodes[frame.remote64];
733744
if (node) {
745+
if (typeof node.updateLastSeen === 'function') {
746+
node.updateLastSeen();
747+
}
734748
for (const endpointNum of frame.activeEndpoints) {
735749
if (!(endpointNum in node.activeEndpoints)) {
736750
node.activeEndpoints[endpointNum] = {};
@@ -792,6 +806,10 @@ class ZigbeeAdapter extends Adapter {
792806
}
793807
const node = this.createNodeIfRequired(frame.remote64, frame.remote16);
794808

809+
if (node && typeof node.updateLastSeen === 'function') {
810+
node.updateLastSeen();
811+
}
812+
795813
for (let i = 0; i < frame.numEntriesThisResponse; i++) {
796814
const neighborIndex = frame.startIndex + i;
797815
const neighbor = frame.neighbors[i];
@@ -943,6 +961,11 @@ class ZigbeeAdapter extends Adapter {
943961
if (!node) {
944962
return;
945963
}
964+
965+
if (typeof node.updateLastSeen === 'function') {
966+
node.updateLastSeen();
967+
}
968+
946969
const endpoint = node.activeEndpoints[frame.endpoint];
947970
if (endpoint) {
948971
endpoint.queryingSimpleDescriptor = false;
@@ -1029,6 +1052,10 @@ class ZigbeeAdapter extends Adapter {
10291052
return;
10301053
}
10311054

1055+
if (typeof node.updateLastSeen === 'function') {
1056+
node.updateLastSeen();
1057+
}
1058+
10321059
if (DEBUG_flow) {
10331060
console.log('handleManagementLeaveResponse: Removing node:', node.addr64);
10341061
}

src/zb-classifier.js

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1196,7 +1196,6 @@ class ZigbeeClassifier {
11961196
maximum: 100,
11971197
multipleOf: 0.5,
11981198
readOnly: true,
1199-
visible: !!node.driver.config.showBattery,
12001199
},
12011200
PROFILE_ID.ZHA, // profileId
12021201
genPowerCfgEndpoint, // endpoint
@@ -1808,6 +1807,41 @@ class ZigbeeClassifier {
18081807
}
18091808
}
18101809

1810+
addLastSeenProperty(node) {
1811+
if (node.driver.config.showAging) {
1812+
const lastSeen = new ZigbeeProperty(
1813+
node,
1814+
'lastSeen', // name
1815+
{
1816+
type: 'string',
1817+
readOnly: true,
1818+
label: 'Last seen',
1819+
},
1820+
'', // profileId
1821+
'', // endPoint
1822+
'', // clusterId
1823+
'', // attr
1824+
null, // setAttrFromValue
1825+
null // parseValueFromAttr
1826+
);
1827+
node.properties.set('lastSeen', lastSeen);
1828+
lastSeen.value = '';
1829+
lastSeen.fireAndForget = true;
1830+
1831+
node.updateLastSeen = () => {
1832+
const d = new Date();
1833+
// there's no easy way to construct an ISO date with local timezone, so do it the hard way
1834+
/* eslint-disable max-len */
1835+
// prettier-ignore
1836+
const s =
1837+
`${d.getFullYear()}-${(`0${d.getMonth() + 1}`).slice(-2)}-${(`0${d.getDate()}`).slice(-2)}
1838+
${(`0${d.getHours()}`).slice(-2)}:${(`0${d.getMinutes()}`).slice(-2)}:${(`0${d.getSeconds()}`).slice(-2)}`;
1839+
/* eslint-enable max-len */
1840+
lastSeen.setCachedValueAndNotify(s);
1841+
};
1842+
}
1843+
}
1844+
18111845
addProperty(
18121846
node,
18131847
name,
@@ -2083,6 +2117,8 @@ class ZigbeeClassifier {
20832117
if (genPowerCfgEndpoint) {
20842118
this.addPowerCfgVoltageProperty(node, genPowerCfgEndpoint);
20852119
}
2120+
2121+
this.addLastSeenProperty(node);
20862122
}
20872123

20882124
classify(node) {

0 commit comments

Comments
 (0)