Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend Persistence Extensions with Riemann Sums #401

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 14 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -496,9 +496,9 @@ See [openhab-js : ItemConfig](https://openhab.github.io/openhab-js/global.html#I
Calling `Item.persistence` returns an `ItemPersistence` object with the following functions:

- ItemPersistence :`object`
- .averageSince(timestamp, serviceId) ⇒ `PersistedState | null`
- .averageUntil(timestamp, serviceId) ⇒ `PersistedState | null`
- .averageBetween(begin, end, serviceId) ⇒ `PersistedState | null`
- .averageSince(timestamp, riemannType, serviceId) ⇒ `PersistedState | null`
- .averageUntil(timestamp, riemannType, serviceId) ⇒ `PersistedState | null`
- .averageBetween(begin, end, riemannType, serviceId) ⇒ `PersistedState | null`
- .changedSince(timestamp, serviceId) ⇒ `boolean`
- .changedUntil(timestamp, serviceId) ⇒ `boolean`
- .changedBetween(begin, end, serviceId) ⇒ `boolean`
Expand All @@ -511,12 +511,12 @@ Calling `Item.persistence` returns an `ItemPersistence` object with the followin
- .deltaSince(timestamp, serviceId) ⇒ `PersistedState | null`
- .deltaUntil(timestamp, serviceId) ⇒ `PersistedState | null`
- .deltaBetween(begin, end, serviceId) ⇒ `PersistedState | null`
- .deviationSince(timestamp, serviceId) ⇒ `PersistedState | null`
- .deviationUntil(timestamp, serviceId) ⇒ `PersistedState | null`
- .deviationBetween(begin, end, serviceId) ⇒ `PersistedState | null`
- .evolutionRateSince(timestamp, serviceId) ⇒ `number | null`
- .evolutionRateUntil(timestamp, serviceId) ⇒ `number | null`
- .evolutionRateBetween(begin, end, serviceId) ⇒ `number | null`
- .deviationSince(timestamp, riemannType, serviceId) ⇒ `PersistedState | null`
- .deviationUntil(timestamp, riemannType, serviceId) ⇒ `PersistedState | null`
- .deviationBetween(begin, end, riemannType, serviceId) ⇒ `PersistedState | null`
- .evolutionRateSince(timestamp, riemannType, serviceId) ⇒ `number | null`
- .evolutionRateUntil(timestamp, riemannType, serviceId) ⇒ `number | null`
- .evolutionRateBetween(begin, end, riemannType, serviceId) ⇒ `number | null`
- .getAllStatesSince(timestamp, serviceId) ⇒ `Array[PersistedItem]`
- .getAllStatesUntil(timestamp, serviceId) ⇒ `Array[PersistedItem]`
- .getAllStatesBetween(begin, end, serviceId) ⇒ `Array[PersistedItem]`
Expand All @@ -540,6 +540,9 @@ Calling `Item.persistence` returns an `ItemPersistence` object with the followin
- .persistedState(timestamp, serviceId) ⇒ `PersistedItem | null`
- .previousState(skipEqual, serviceId) ⇒ `PersistedItem | null`
- .nextState(skipEqual, serviceId) ⇒ `PersistedItem | null`
- .riemannSumSince(timestamp, riemannType, serviceId) ⇒ `PersistedState | null`: Time is considered in seconds.
- .riemannSumUntil(timestamp, riemannType, serviceId) ⇒ `PersistedState | null`: Time is considered in seconds.
- .riemannSumBetween(begin, end, riemannType, serviceId) ⇒ `PersistedState | null`: Time is considered in seconds.
- .sumSince(timestamp, serviceId) ⇒ `PersistedState | null`
- .sumUntil(timestamp, serviceId) ⇒ `PersistedState | null`
- .sumBetween(begin, end, serviceId) ⇒ `PersistedState | null`
Expand All @@ -550,6 +553,8 @@ Calling `Item.persistence` returns an `ItemPersistence` object with the followin
- .varianceUntil(timestamp, serviceId) ⇒ `PersistedState | null`
- .varianceBetween(begin, end, serviceId) ⇒ `PersistedState | null`

`riemannType` is an optional argument for methods that require calculating an approximation of the integral value. The approximation is calculated using a Riemann sum, with left, right, trapezoidal or midpoint value approximations. The argument is a Java RiemannType enum with possible values: `RiemannType.LEFT`, `RiemannType.RIGHT`, `RiemannType.TRAPEZOIDAL` or `RiemannType.MIDPOINT`. If ommitted, `RiemannType.LEFT` is used.

Note: `serviceId` is optional, if omitted, the default persistence service will be used.

```javascript
Expand Down
96 changes: 87 additions & 9 deletions src/items/item-persistence.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const { _toOpenhabPrimitiveType, _isTimeSeries } = require('../helpers');
const PersistenceExtensions = Java.type('org.openhab.core.persistence.extensions.PersistenceExtensions');
const TimeSeries = Java.type('org.openhab.core.types.TimeSeries');
const TypeParser = Java.type('org.openhab.core.types.TypeParser');
const RiemannType = Java.type('org.openhab..core.persistence.extensions.PersistenceExtensions.RiemannType');

/**
* @typedef {import('@js-joda/core').ZonedDateTime} time.ZonedDateTime
Expand Down Expand Up @@ -166,6 +167,8 @@ class ItemPersistence {
this.rawItem = rawItem;
}

static RiemannType = RiemannType;

/**
* Persists a state of a given Item.
*
Expand Down Expand Up @@ -509,25 +512,31 @@ class ItemPersistence {
* Gets the variance of the state of the given Item since a certain point in time.
*
* @param {(time.ZonedDateTime | Date)} timestamp the point in time from which to compute the variance
* @param {RiemannType} [riemannType] optional Riemann approximation type to calculate the integral approximation
* (<code>LEFT</code>, <code>RIGHT</code>, <code>TRAPEZOIDAL</code>, <code>MIDPOINT</code>),
* default <code>LEFT</code>
* @param {string} [serviceId] optional persistence service ID, if omitted, the default persistence service will be used
* @returns {(PersistedState | null)} the variance between then and now as {@link items.PersistedState}, or <code>null</code> if
* <code>timestamp</code> is in the future, or if there is no persisted state for the given
* Item at the given <code>timestamp</code>
*/
varianceSince (timestamp, serviceId) {
varianceSince (timestamp, riemannType, serviceId) {
return _persistedStateOrNull(PersistenceExtensions.varianceSince(this.rawItem, ...arguments));
}

/**
* Gets the variance of the state of the given Item until a certain point in time.
*
* @param {(time.ZonedDateTime | Date)} timestamp the point in time to which to compute the variance
* @param {RiemannType} [riemannType] optional Riemann approximation type to calculate the integral approximation
* (<code>LEFT</code>, <code>RIGHT</code>, <code>TRAPEZOIDAL</code>, <code>MIDPOINT</code>),
* default <code>LEFT</code>
* @param {string} [serviceId] optional persistence service ID, if omitted, the default persistence service will be used
* @returns {(PersistedState | null)} the variance between now and then as {@link items.PersistedState}, or <code>null</code> if
* <code>timestamp</code> is in the past, or if there is no persisted state for the given
* Item at the given <code>timestamp</code>
*/
varianceUntil (timestamp, serviceId) {
varianceUntil (timestamp, riemannType, serviceId) {
return _persistedStateOrNull(PersistenceExtensions.varianceUntil(this.rawItem, ...arguments));
}

Expand All @@ -536,38 +545,47 @@ class ItemPersistence {
*
* @param {(time.ZonedDateTime | Date)} begin the point in time from which to compute the variance
* @param {(time.ZonedDateTime | Date)} end the end time for the computation
* @param {RiemannType} [riemannType] optional Riemann approximation type to calculate the integral approximation
* (<code>LEFT</code>, <code>RIGHT</code>, <code>TRAPEZOIDAL</code>, <code>MIDPOINT</code>),
* default <code>LEFT</code>
* @param {string} [serviceId] optional persistence service ID, if omitted, the default persistence service will be used
* @returns {(PersistedState | null)} the variance between both points of time as {@link items.PersistedState}, or <code>null</code> if
* <code>begin</code> is after <code>end</code>, or if there is no persisted state for the given
* Item between <code>begin</code> and <code>end</code>
*/
varianceBetween (begin, end, serviceId) {
varianceBetween (begin, end, riemannType, serviceId) {
return _persistedStateOrNull(PersistenceExtensions.varianceBetween(this.rawItem, ...arguments));
}

/**
* Gets the standard deviation of the state of the given Item since a certain point in time.
*
* @param {(time.ZonedDateTime | Date)} timestamp the point in time from which to compute the standard deviation
* @param {RiemannType} [riemannType] optional Riemann approximation type to calculate the integral approximation
* (<code>LEFT</code>, <code>RIGHT</code>, <code>TRAPEZOIDAL</code>, <code>MIDPOINT</code>),
* default <code>LEFT</code>
* @param {string} [serviceId] optional persistence service ID, if omitted, the default persistence service will be used
* @returns {(PersistedState | null)} the standard deviation between then and now as {@link items.PersistedState}, or <code>null</code>
* if <code>timestamp</code> is in the future, or if there is no persisted state for the given Item
* at the given <code>timestamp</code>
*/
deviationSince (timestamp, serviceId) {
deviationSince (timestamp, riemannType, serviceId) {
return _persistedStateOrNull(PersistenceExtensions.deviationSince(this.rawItem, ...arguments));
}

/**
* Gets the standard deviation of the state of the given Item until a certain point in time.
*
* @param {(time.ZonedDateTime | Date)} timestamp the point in time to which to compute the standard deviation
* @param {RiemannType} [riemannType] optional Riemann approximation type to calculate the integral approximation
* (<code>LEFT</code>, <code>RIGHT</code>, <code>TRAPEZOIDAL</code>, <code>MIDPOINT</code>),
* default <code>LEFT</code>
* @param {string} [serviceId] optional persistence service ID, if omitted, the default persistence service will be used
* @returns {(PersistedState | null)} the standard deviation between now and then as {@link items.PersistedState}, or <code>null</code>
* if <code>timestamp</code> is in the past, or if there is no persisted state for the given Item
* at the given <code>timestamp</code>
*/
deviationUntil (timestamp, serviceId) {
deviationUntil (timestamp, riemannType, serviceId) {
return _persistedStateOrNull(PersistenceExtensions.deviationUntil(this.rawItem, ...arguments));
}

Expand All @@ -576,12 +594,15 @@ class ItemPersistence {
*
* @param {(time.ZonedDateTime | Date)} begin the point in time from which to compute
* @param {(time.ZonedDateTime | Date)} end the end time for the computation
* @param {RiemannType} [riemannType] optional Riemann approximation type to calculate the integral approximation
* (<code>LEFT</code>, <code>RIGHT</code>, <code>TRAPEZOIDAL</code>, <code>MIDPOINT</code>),
* default <code>LEFT</code>
* @param {string} [serviceId] optional persistence service ID, if omitted, the default persistence service will be used
* @returns {(PersistedState | null)} the standard deviation between both points of time as {@link items.PersistedState}, or <code>null</code>
* if <code>begin</code> is after <code>end</code>, or if there is no persisted state for the given Item
* between <code>begin</code> and <code>end</code>
*/
deviationBetween (begin, end, serviceId) {
deviationBetween (begin, end, riemannType, serviceId) {
return _persistedStateOrNull(PersistenceExtensions.deviationBetween(this.rawItem, ...arguments));
}

Expand All @@ -594,21 +615,27 @@ class ItemPersistence {
* console.log('KitchenDimmer average since yesterday', item.persistence.averageSince(yesterday));
*
* @param {(time.ZonedDateTime | Date)} timestamp the point in time from which to search for the average value
* @param {RiemannType} [riemannType] optional Riemann approximation type to calculate the integral approximation
* (<code>LEFT</code>, <code>RIGHT</code>, <code>TRAPEZOIDAL</code>, <code>MIDPOINT</code>),
* default <code>LEFT</code>
* @param {string} [serviceId] optional persistence service ID, if omitted, the default persistence service will be used
* @returns {(PersistedState | null)} the average value since <code>timestamp</code> as {@link items.PersistedState} or <code>null</code> if no previous states could be found
*/
averageSince (timestamp, serviceId) {
averageSince (timestamp, riemannType, serviceId) {
return _persistedStateOrNull(PersistenceExtensions.averageSince(this.rawItem, ...arguments));
}

/**
* Gets the average value of the state of a given Item until a certain point in time.
*
* @param {(time.ZonedDateTime | Date)} timestamp the point in time to which to search for the average value
* @param {RiemannType} [riemannType] optional Riemann approximation type to calculate the integral approximation
* (<code>LEFT</code>, <code>RIGHT</code>, <code>TRAPEZOIDAL</code>, <code>MIDPOINT</code>),
* default <code>LEFT</code>
* @param {string} [serviceId] optional persistence service ID, if omitted, the default persistence service will be used
* @returns {(PersistedState | null)} the average value until <code>timestamp</code> as {@link items.PersistedState} or <code>null</code> if no future states could be found
*/
averageUntil (timestamp, serviceId) {
averageUntil (timestamp, riemannType, serviceId) {
return _persistedStateOrNull(PersistenceExtensions.averageUntil(this.rawItem, ...arguments));
}

Expand All @@ -617,13 +644,64 @@ class ItemPersistence {
*
* @param {(time.ZonedDateTime | Date)} begin the point in time from which to start the average
* @param {(time.ZonedDateTime | Date)} end the point in time to which to start the average
* @param {RiemannType} [riemannType] optional Riemann approximation type to calculate the integral approximation
* (<code>LEFT</code>, <code>RIGHT</code>, <code>TRAPEZOIDAL</code>, <code>MIDPOINT</code>),
* default <code>LEFT</code>
* @param {string} [serviceId] optional persistence service ID, if omitted, the default persistence service will be used
* @returns {(PersistedState | null)} the average value between <code>begin</code> and <code>end</code> as {@link items.PersistedState} or <code>null</code> if no states could be found
*/
averageBetween (begin, end, serviceId) {
averageBetween (begin, end, riemannType, serviceId) {
return _persistedStateOrNull(PersistenceExtensions.averageBetween(this.rawItem, ...arguments));
}

/**
* Gets the RiemannSum of the states of a given Item since a certain point in time, time is calculated in seconds.
*
* @example
* var yesterday = time.toZDT().minusDays(1);
* var item = items.getItem('KitchenDimmer');
* console.log('KitchenDimmer Riemann sum since yesterday', item.persistence.riemannSumSince(yesterday));
*
* @param {(time.ZonedDateTime | Date)} timestamp the point in time from which to search for the Riemann sum
* @param {RiemannType} [riemannType] optional Riemann approximation type to calculate the integral approximation
* (<code>LEFT</code>, <code>RIGHT</code>, <code>TRAPEZOIDAL</code>, <code>MIDPOINT</code>),
* default <code>LEFT</code>
* @param {string} [serviceId] optional persistence service ID, if omitted, the default persistence service will be used
* @returns {(PersistedState | null)} the Riemann sum since <code>timestamp</code> as {@link items.PersistedState} or <code>null</code> if no previous states could be found, time is calculated in seconds
*/
riemannSumSince (timestamp, riemannType, serviceId) {
return _persistedStateOrNull(PersistenceExtensions.riemannSumSince(this.rawItem, ...arguments));
}

/**
* Gets the RiemannSum of the states of a given Item until a certain point in time, time is calculated in seconds.
*
* @param {(time.ZonedDateTime | Date)} timestamp the point in time to which to search for the Riemann sum
* @param {RiemannType} [riemannType] optional Riemann approximation type to calculate the integral approximation
* (<code>LEFT</code>, <code>RIGHT</code>, <code>TRAPEZOIDAL</code>, <code>MIDPOINT</code>),
* default <code>LEFT</code>
* @param {string} [serviceId] optional persistence service ID, if omitted, the default persistence service will be used
* @returns {(PersistedState | null)} the Riemann sum until <code>timestamp</code> as {@link items.PersistedState} or <code>null</code> if no future states could be found, time is calculated in seconds
*/
riemannSumUntil (timestamp, riemannType, serviceId) {
return _persistedStateOrNull(PersistenceExtensions.riemannSumUntil(this.rawItem, ...arguments));
}

/**
* Gets the RiemannSum of the states of a given Item between two certain points in time, time is calculated in seconds.
*
* @param {(time.ZonedDateTime | Date)} begin the point in time from which to start the Riemann sum
* @param {(time.ZonedDateTime | Date)} end the point in time to which to start the Riemann sum
* @param {RiemannType} [riemannType] optional Riemann approximation type to calculate the integral approximation
* (<code>LEFT</code>, <code>RIGHT</code>, <code>TRAPEZOIDAL</code>, <code>MIDPOINT</code>),
* default <code>LEFT</code>
* @param {string} [serviceId] optional persistence service ID, if omitted, the default persistence service will be used
* @returns {(PersistedState | null)} the Riemann sum between <code>begin</code> and <code>end</code> as {@link items.PersistedState} or <code>null</code> if no states could be found, time is calculated in seconds
*/
riemannSumBetween (begin, end, riemannType, serviceId) {
return _persistedStateOrNull(PersistenceExtensions.riemannSumBetween(this.rawItem, ...arguments));
}

/**
* Gets the median value of the state of a given Item since a certain point in time.
*
Expand Down
Loading