From d5b92cf02916ad4a72f6cc47114f9f5fbc19ea6f Mon Sep 17 00:00:00 2001 From: Heiko Rothe Date: Sun, 6 Dec 2020 14:11:28 +0100 Subject: [PATCH] feat(entities): add debounce edge options Closes #329 --- config/test.yml | 6 ++++++ docs/guide/entities.md | 14 ++++++++------ src/entities/debounce.proxy.ts | 6 +++++- src/entities/entities.config.ts | 2 ++ src/entities/entities.service.spec.ts | 26 ++++++++++++++++++++++++++ 5 files changed, 47 insertions(+), 7 deletions(-) diff --git a/config/test.yml b/config/test.yml index 93fcc9f..0d4e0fa 100644 --- a/config/test.yml +++ b/config/test.yml @@ -8,6 +8,12 @@ entities: debounce: wait: 0.75 maxWait: 2 + leading_debounced_entity: + debounce: + wait: 0.75 + maxWait: 2 + leading: true + trailing: false rolling_average_entity: rollingAverage: window: 60 diff --git a/docs/guide/entities.md b/docs/guide/entities.md index 7cd136b..14d49ef 100644 --- a/docs/guide/entities.md +++ b/docs/guide/entities.md @@ -21,12 +21,14 @@ Behaviors may be set per entity ID, with the ID being the key and an object with #### Debounce -Debouncing is especially helpful for sensors that jump states quickly in an incorrect manner. This could for example be the case for some GPIO sensors or thermopiles. - -| Name | Type | Default | Description | -| --------- | ------ | ------- | ------------------------------------------------------------ | -| `wait` | Number | | Number of seconds to wait after the last time the state was updated before publishing it to integrations. | -| `maxWait` | Number | | Maximum number of seconds that a state update may be delayed. If left undefined there will be no limit. | +Debouncing is especially helpful for sensors that jump states quickly in an incorrect manner. This could for example be the case for some GPIO sensors or thermopiles. This behavior is based on the [Lodash debounce implementation](https://lodash.com/docs/#debounce) and the options will therefore behave the same. + +| Name | Type | Default | Description | +| ---------- | ------- | ------- | ------------------------------------------------------------ | +| `wait` | Number | | Number of seconds to wait after the last time the state was updated before publishing it to integrations. | +| `maxWait` | Number | | Maximum number of seconds that a state update may be delayed. If left undefined there will be no limit. | +| `leading` | Boolean | `false` | Invoke the update on the leading edge of the timeout. | +| `trailing` | Boolean | `true` | Invoke the update on the trailing edge of the timeout. | ::: details Example Config diff --git a/src/entities/debounce.proxy.ts b/src/entities/debounce.proxy.ts index 4978a4b..817d1fe 100644 --- a/src/entities/debounce.proxy.ts +++ b/src/entities/debounce.proxy.ts @@ -11,7 +11,11 @@ export class DebounceProxyHandler implements ProxyHandler { target.state = value; }, config.wait * 1000, - { maxWait: config.maxWait * 1000 } + { + maxWait: config.maxWait * 1000, + leading: config.leading ?? false, + trailing: config.trailing ?? true, + } ); } diff --git a/src/entities/entities.config.ts b/src/entities/entities.config.ts index 888d844..27d7c08 100644 --- a/src/entities/entities.config.ts +++ b/src/entities/entities.config.ts @@ -10,6 +10,8 @@ export class EntityBehavior { export class DebounceOptions { wait?: number; maxWait?: number; + leading: boolean; + trailing?: boolean; } export class RollingAverageOptions { diff --git a/src/entities/entities.service.spec.ts b/src/entities/entities.service.spec.ts index 593e1a4..0e458ad 100644 --- a/src/entities/entities.service.spec.ts +++ b/src/entities/entities.service.spec.ts @@ -159,6 +159,32 @@ describe('EntitiesService', () => { ); }); + it('should debounce state updates on leading edge if configured', () => { + jest.useFakeTimers('modern'); + const spy = jest.spyOn(emitter, 'emit'); + + const entityProxy = service.add( + new Sensor('leading_debounced_entity', 'Debounce Test') + ); + spy.mockClear(); + + entityProxy.state = 42; + entityProxy.state = 1337; + + expect(entityProxy.state).toBe(42); + + jest.runAllTimers(); + + expect(entityProxy.state).toBe(42); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'stateUpdate', + 'leading_debounced_entity', + 42, + false + ); + }); + it('should calculate rolling average for non-number states if configured', () => { jest.useFakeTimers('modern'); const spy = jest.spyOn(emitter, 'emit');