Skip to content

Commit

Permalink
Fix throttling functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
gracianodias3 committed Jan 13, 2025
1 parent be17e69 commit 453979c
Show file tree
Hide file tree
Showing 6 changed files with 27 additions and 23 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,9 @@ Zigbee2MQTT is made up of three modules, each developed in its own Github projec
### Developing

Zigbee2MQTT uses TypeScript (partially for now). Therefore after making changes to files in the `lib/` directory you need to recompile Zigbee2MQTT. This can be done by executing `pnpm run build`. For faster development instead of running `pnpm run build` you can run `pnpm run build-watch` in another terminal session, this will recompile as you change files.
In first time before building you need to run `pnpm install --include=dev`
Before submitting changes run `pnpm run test-with-coverage`, `pnpm run pretty:check` and `pnpm run eslint`

Before running any of the commands, you'll first need to run `pnpm install --include=dev`.
Before submitting changes run `pnpm run test:coverage`, `pnpm run pretty:check` and `pnpm run eslint`

## Supported devices

Expand Down
2 changes: 1 addition & 1 deletion lib/extension/receive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import assert from 'node:assert';
import bind from 'bind-decorator';
import debounce from 'debounce';
import stringify from 'json-stable-stringify-without-jsonify';
import throttle from 'throttleit';

import * as zhc from 'zigbee-herdsman-converters';

import logger from '../util/logger';
import * as settings from '../util/settings';
import throttle from '../util/throttler';
import utils from '../util/utils';
import Extension from './extension';

Expand Down
14 changes: 14 additions & 0 deletions lib/util/throttler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export default function throttle<Args extends unknown[], Return>(fn: (...args: Args) => Return, wait: number): (...args: Args) => Return | undefined {
let lastCallTime = 0;

return (...args: Args) => {
const now = Date.now();
const timeSinceLastCall = now - lastCallTime;
const delayForNextCall = wait - timeSinceLastCall;

if (delayForNextCall <= 0) {
lastCallTime = now;
return fn(...args);
}
};
}
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@
"rimraf": "^6.0.1",
"semver": "^7.6.3",
"source-map-support": "^0.5.21",
"throttleit": "^2.1.0",
"winston": "^3.17.0",
"winston-syslog": "^2.7.1",
"winston-transport": "^4.9.0",
Expand Down
9 changes: 0 additions & 9 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 9 additions & 10 deletions test/extensions/receive.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,8 @@ describe('Extension: Receive', () => {
settings.set(['device_options', 'throttle'], throttle_for_testing);
settings.set(['device_options', 'retain'], true);
settings.set(['devices', device.ieeeAddr, 'friendly_name'], 'spammer1');

// Send 3 requests, expect the throttled values calls to be discarded
const data1 = {measuredValue: 1};
const payload1 = {
data: data1,
Expand Down Expand Up @@ -394,16 +396,13 @@ describe('Extension: Receive', () => {
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({temperature: 0.01});
expect(mockMQTTPublishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: true});

// Now we try after elapsed time to see if it publishes next message
// Now we try after elapsed time validate that the old calls haven't made it through
const timeshift = throttle_for_testing * 2000;
vi.advanceTimersByTime(timeshift);
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2);
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
await flushPromises();

expect(mockMQTTPublishAsync.mock.calls[1][0]).toStrictEqual('zigbee2mqtt/spammer1');
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[1][1])).toStrictEqual({temperature: 0.03});
expect(mockMQTTPublishAsync.mock.calls[1][2]).toStrictEqual({qos: 0, retain: true});

// Make a new call _after_ the throttled time, and expect it to make it through
const data4 = {measuredValue: 4};
const payload4 = {
data: data4,
Expand All @@ -416,10 +415,10 @@ describe('Extension: Receive', () => {
await mockZHEvents.message(payload4);
await flushPromises();

expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(3);
expect(mockMQTTPublishAsync.mock.calls[2][0]).toStrictEqual('zigbee2mqtt/spammer1');
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[2][1])).toStrictEqual({temperature: 0.04});
expect(mockMQTTPublishAsync.mock.calls[2][2]).toStrictEqual({qos: 0, retain: true});
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2);
expect(mockMQTTPublishAsync.mock.calls[1][0]).toStrictEqual('zigbee2mqtt/spammer1');
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[1][1])).toStrictEqual({temperature: 0.04});
expect(mockMQTTPublishAsync.mock.calls[1][2]).toStrictEqual({qos: 0, retain: true});
});

it('Shouldnt republish old state', async () => {
Expand Down

0 comments on commit 453979c

Please sign in to comment.