Skip to content

Commit

Permalink
fix: Update last seen on network address events (#1004)
Browse files Browse the repository at this point in the history
* Fix whitespace errors

* Test that last seen is updated on network address events

* Fix controller.ts whitespace errors

* Update the last seen timestamp on nwk events
  • Loading branch information
deviantintegral authored Apr 3, 2024
1 parent 1b7b4f7 commit a45aff4
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 17 deletions.
17 changes: 9 additions & 8 deletions src/controller/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ class Controller extends events.EventEmitter {
ieeeAddr = aqaraMatch[1];
key = aqaraMatch[2];
} else {
assert(installCode.length === 95 || installCode.length === 91,
assert(installCode.length === 95 || installCode.length === 91,
`Unsupported install code, got ${installCode.length} chars, expected 95 or 91`);
const keyStart = installCode.length - (installCode.length === 95 ? 36 : 32);
ieeeAddr = installCode.substring(keyStart - 19, keyStart - 3);
Expand Down Expand Up @@ -444,7 +444,8 @@ class Controller extends events.EventEmitter {
return;
}

this.selfAndDeviceEmit(device, Events.Events.lastSeenChanged,
device.updateLastSeen();
this.selfAndDeviceEmit(device, Events.Events.lastSeenChanged,
{device, reason: 'networkAddress'} as Events.LastSeenChangedPayload);

if (device.networkAddress !== payload.networkAddress) {
Expand All @@ -467,7 +468,7 @@ class Controller extends events.EventEmitter {
}

device.updateLastSeen();
this.selfAndDeviceEmit(device, Events.Events.lastSeenChanged,
this.selfAndDeviceEmit(device, Events.Events.lastSeenChanged,
{device, reason: 'deviceAnnounce'} as Events.LastSeenChangedPayload);
device.implicitCheckin();

Expand Down Expand Up @@ -552,7 +553,7 @@ class Controller extends events.EventEmitter {
if (this.options.acceptJoiningDeviceHandler) {
if (!(await this.options.acceptJoiningDeviceHandler(payload.ieeeAddr))) {
logger.debug(`Device '${payload.ieeeAddr}' rejected by handler, removing it`, NS);
await catcho(() => this.adapter.removeDevice(payload.networkAddress, payload.ieeeAddr),
await catcho(() => this.adapter.removeDevice(payload.networkAddress, payload.ieeeAddr),
'Failed to remove rejected device');
return;
} else {
Expand Down Expand Up @@ -585,7 +586,7 @@ class Controller extends events.EventEmitter {
}

device.updateLastSeen();
this.selfAndDeviceEmit(device, Events.Events.lastSeenChanged,
this.selfAndDeviceEmit(device, Events.Events.lastSeenChanged,
{device, reason: 'deviceJoined'} as Events.LastSeenChangedPayload);
device.implicitCheckin();

Expand Down Expand Up @@ -642,7 +643,7 @@ class Controller extends events.EventEmitter {

let device = gpDevice ? gpDevice : (typeof dataPayload.address === 'string' ?
Device.byIeeeAddr(dataPayload.address) : Device.byNetworkAddress(dataPayload.address));

/**
* Handling of re-transmitted Xiaomi messages.
* https://github.com/Koenkk/zigbee2mqtt/issues/1238
Expand Down Expand Up @@ -756,10 +757,10 @@ class Controller extends events.EventEmitter {
};

this.selfAndDeviceEmit(device, Events.Events.message, eventData);
this.selfAndDeviceEmit(device, Events.Events.lastSeenChanged,
this.selfAndDeviceEmit(device, Events.Events.lastSeenChanged,
{device, reason: 'messageEmitted'} as Events.LastSeenChangedPayload);
} else {
this.selfAndDeviceEmit(device, Events.Events.lastSeenChanged,
this.selfAndDeviceEmit(device, Events.Events.lastSeenChanged,
{device, reason: 'messageNonEmitted'} as Events.LastSeenChangedPayload);
}

Expand Down
29 changes: 20 additions & 9 deletions test/controller.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,8 @@ const restoreMocksendZclFrameToEndpoint = () => {
for (const item of frame.Payload) {
payload.push({attrId: item.attrId, status: configureReportStatus, direction: 1})
}


}
// @ts-ignore
return {frame: new ZclFrame(null, payload, frame.Cluster)};
Expand Down Expand Up @@ -501,6 +501,7 @@ const events = {
deviceLeave: [],
message: [],
permitJoinChanged: [],
lastSeenChanged: [],
}

const backupPath = getTempFile('backup');
Expand Down Expand Up @@ -564,6 +565,7 @@ describe('Controller', () => {
controller.on('deviceAnnounce', (device) => events.deviceAnnounce.push(device));
controller.on('deviceLeave', (device) => events.deviceLeave.push(device));
controller.on('message', (message) => events.message.push(message));
controller.on('lastSeenChanged', (device) => events.lastSeenChanged.push(device));
restoreMocksendZclFrameToEndpoint();
});

Expand Down Expand Up @@ -989,6 +991,15 @@ describe('Controller', () => {
await mockAdapterEvents['networkAddress']({networkAddress: 19321, ieeeAddr: '0x19321'});
});

it('Network address event should update the last seen value', async () => {
await controller.start();
await mockAdapterEvents['deviceJoined']({networkAddress: 129, ieeeAddr: '0x129'});
Date.now = jest.fn()
Date.now.mockReturnValue(200);
await mockAdapterEvents['networkAddress']({networkAddress: 129, ieeeAddr: '0x129'});
expect(events.lastSeenChanged[1].device.lastSeen).toBe(200);
});

it('Device leave event and remove from database', async () => {
await controller.start();
await mockAdapterEvents['deviceJoined']({networkAddress: 129, ieeeAddr: '0x129'});
Expand Down Expand Up @@ -3239,7 +3250,7 @@ describe('Controller', () => {
expect(deepClone(call[3])).toStrictEqual({"Header":{"frameControl":{"reservedBits":0,"frameType":0,"direction":1,"disableDefaultResponse":true,"manufacturerSpecific":false},"transactionSequenceNumber":99,"manufacturerCode":null,"commandIdentifier":4},"Payload":[{"attrId":85,"status":1}],"Cluster":{"ID":0,"attributes":{"zclVersion":{"ID":0,"type":32,"name":"zclVersion"},"appVersion":{"ID":1,"type":32,"name":"appVersion"},"schneiderMeterRadioPower": {"ID": 57856,"manufacturerCode": 4190,"name": "schneiderMeterRadioPower","type": 40},"stackVersion":{"ID":2,"type":32,"name":"stackVersion"},"hwVersion":{"ID":3,"type":32,"name":"hwVersion"},"manufacturerName":{"ID":4,"type":66,"name":"manufacturerName"},"modelId":{"ID":5,"type":66,"name":"modelId"},"dateCode":{"ID":6,"type":66,"name":"dateCode"},"powerSource":{"ID":7,"type":48,"name":"powerSource"},"appProfileVersion":{"ID":8,"type":48,"name":"appProfileVersion"},"swBuildId":{"ID":16384,"type":66,"name":"swBuildId"},"locationDesc":{"ID":16,"type":66,"name":"locationDesc"},"physicalEnv":{"ID":17,"type":48,"name":"physicalEnv"},"develcoPrimaryHwVersion":{"ID": 32800,"manufacturerCode": 4117,"name": "develcoPrimaryHwVersion","type": 65,},"develcoPrimarySwVersion":{"ID": 32768,"manufacturerCode": 4117,"name": "develcoPrimarySwVersion","type": 65,},"develcoLedControl":{"ID":33024,"manufacturerCode":4117,"name":"develcoLedControl","type":24,},"deviceEnabled":{"ID":18,"type":16,"name":"deviceEnabled"},"alarmMask":{"ID":19,"type":24,"name":"alarmMask"},"disableLocalConfig":{"ID":20,"type":24,"name":"disableLocalConfig"}},"name":"genBasic","commands":{"resetFactDefault":{"ID":0,"parameters":[],"name":"resetFactDefault"},"tuyaSetup":{"ID":240,"parameters":[],"name":"tuyaSetup"}},"commandsResponse":{}},"Command":{"ID":4,"name":"writeRsp","parameters":[{"name":"status","type":32},{"conditions":[{"type":"statusNotEquals","value":0}],"name":"attrId","type":33}]}});
expect(call[4]).toBe(10000);
});

it('Write response to endpoint with unknown string attribute', async () => {
await controller.start();
await mockAdapterEvents['deviceJoined']({networkAddress: 129, ieeeAddr: '0x129'});
Expand All @@ -3263,7 +3274,7 @@ describe('Controller', () => {
expect(error.message).toStrictEqual(`Use parameter`)
expect(mocksendZclFrameToEndpoint).toBeCalledTimes(0);
});

it('Write response to endpoint with no status attribute specified', async () => {
await controller.start();
await mockAdapterEvents['deviceJoined']({networkAddress: 129, ieeeAddr: '0x129'});
Expand Down Expand Up @@ -3627,7 +3638,7 @@ describe('Controller', () => {
expect(mockLogger.info.mock.calls[2][0]).toBe(`Coordinator Port: ${fakePort}`);
expect(mockLogger.info.mock.calls[3][0]).toBe(`Coordinator Radio: ${fakeRadio}`);
expect(mockLogger.info.mock.calls[4][0]).toBe(`Coordinator Baud: ${fakeBaud}\n`);

});

it('Adapter mdns detection unsupported adapter test', async () => {
Expand Down Expand Up @@ -4338,7 +4349,7 @@ describe('Controller', () => {


const response = mockZclFrame.create(1, 1, true, null, 4, 'response', 33, payload);

expect(mocksendZclFrameToAll).toHaveBeenCalledTimes(1);
expect(mocksendZclFrameToAll.mock.calls[0][0]).toBe(242);
expect(deepClone(mocksendZclFrameToAll.mock.calls[0][1])).toStrictEqual(deepClone(response));
Expand Down Expand Up @@ -4469,7 +4480,7 @@ describe('Controller', () => {
expect(deepClone(mocksendZclFrameToAll.mock.calls[0][1])).toStrictEqual(deepClone(removeFrame));
expect(mocksendZclFrameToAll.mock.calls[0][2]).toBe(242);
expect(mocksendZclFrameToAll).toHaveBeenCalledTimes(1);
expect(controller.getDeviceByIeeeAddr('0x00000000017171f8')).toBeUndefined();
expect(controller.getDeviceByIeeeAddr('0x00000000017171f8')).toBeUndefined();

expect(Device.byIeeeAddr('0x00000000017171f8')).toBeUndefined();
expect(deepClone(Device.byIeeeAddr('0x00000000017171f8', true))).toStrictEqual({"ID":2,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_skipDefaultResponse": false,"_endpoints":[{"ID":242,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"clusters":{},"deviceIeeeAddress":"0x00000000017171f8","deviceNetworkAddress":0x71f8,"inputClusters":[],"meta":{},"outputClusters":[],"pendingRequests": {"ID": 242,"deviceIeeeAddress": "0x00000000017171f8","sendInProgress": false}}],"_ieeeAddr":"0x00000000017171f8","_interviewCompleted":false,"_interviewing":false,"_lastSeen":150,"_linkquality":50,"_manufacturerID":null,"_modelID":"GreenPower_2","_networkAddress":0x71f8,"_type":"GreenPower","_deleted":true,"meta":{}});
Expand Down Expand Up @@ -4574,7 +4585,7 @@ describe('Controller', () => {
expect(mocksendZclFrameToEndpoint).toHaveBeenCalledTimes(2);
});


it('Write to device with pendingRequestTimeout > 0, override default sendPolicy', async () => {
await controller.start();
await mockAdapterEvents['deviceJoined']({networkAddress: 174, ieeeAddr: '0x129'});
Expand Down Expand Up @@ -4617,7 +4628,7 @@ describe('Controller', () => {
return f;
};
endpoint.pendingRequests.add(new Request(async () => {}, [], 100, undefined, undefined, () => {}, () => {}));

mocksendZclFrameToEndpoint.mockClear();
mocksendZclFrameToEndpoint.mockImplementationOnce(async () => {throw new Error('Cats barking too hard');});
mocksendZclFrameToEndpoint.mockImplementationOnce(async () => {throw new Error('Dogs barking too hard');});
Expand Down

0 comments on commit a45aff4

Please sign in to comment.