diff --git a/bundles/org.openhab.ui/web/src/assets/definitions/blockly/blocks-persistence.js b/bundles/org.openhab.ui/web/src/assets/definitions/blockly/blocks-persistence.js
index 2d800898aa..a99fc7b155 100644
--- a/bundles/org.openhab.ui/web/src/assets/definitions/blockly/blocks-persistence.js
+++ b/bundles/org.openhab.ui/web/src/assets/definitions/blockly/blocks-persistence.js
@@ -14,25 +14,28 @@ export default function defineOHBlocks_Persistence (f7, isGraalJs, persistenceSe
Blockly.Blocks['oh_get_persistvalue'] = {
init: function () {
this.appendDummyInput()
- .appendField('get the')
+ .appendField('get')
.appendField(new Blockly.FieldDropdown([
['persisted state', 'persistedState'],
- ['historic state average', 'averageSince'], ['future state average', 'averageUntil'],
- ['historic state delta', 'deltaSince'], ['future state delta', 'deltaUntil'],
- ['historic state deviation', 'deviationSince'], ['future state deviation', 'deviationUntil'],
- ['historic state variance', 'varianceSince'], ['future state variance', 'varianceUntil'],
- ['historic evolution rate', 'evolutionRateSince'], ['future evolution rate', 'evolutionRateUntil'],
- ['historic state minimum', 'minimumSince'], ['future state minimum', 'minimumUntil'],
- ['historic state maximum', 'maximumSince'], ['future state maximum', 'maximumUntil'],
- ['historic state sum', 'sumSince'], ['future state sum', 'sumUntil'],
+ ['historic state average', 'averageSince'], ['future state average', 'averageUntil'], ['state average between', 'averageBetween'],
+ ['historic state delta', 'deltaSince'], ['future state delta', 'deltaUntil'], ['state delta between', 'deltaBetween'],
+ ['historic state deviation', 'deviationSince'], ['future state deviation', 'deviationUntil'], ['state deviation between', 'deviationBetween'],
+ ['historic state variance', 'varianceSince'], ['future state variance', 'varianceUntil'], ['state variance between', 'varianceBetween'],
+ ['historic evolution rate', 'evolutionRateSince'], ['future evolution rate', 'evolutionRateUntil'], ['state evolution rate between', 'evolutionRateBetween'],
+ ['historic state minimum', 'minimumSince'], ['future state minimum', 'minimumUntil'], ['state minimum between', 'minimumBetween'],
+ ['historic state maximum', 'maximumSince'], ['future state maximum', 'maximumUntil'], ['state maximum between', 'maximumBetween'],
+ ['historic state sum', 'sumSince'], ['future state sum', 'sumUntil'], ['state sum between', 'sumBetween'],
+ ['historic state updates count', 'countSince'], ['future state updates count', 'countUntil'], ['state updates count between', 'countBetween'],
+ ['historic state changes count', 'countStateChangesSince'], ['future state changes count', 'countStateChangesUntil'], ['state changes count between', 'countStateChangesBetween'],
['previous state value', 'previousState'], ['next state value', 'nextState'],
+ ['all states since', 'getAllStatesSince'], ['all states until', 'getAllStatesUntil'], ['all states between', 'getAllStatesBetween'],
['previous state numeric value', 'previousNumericState'], ['next state numeric value', 'nextNumericState'],
['previous state value time', 'previousStateTime'], ['next state value time', 'nextStateTime']
], this.handleTypeSelection.bind(this)
), 'methodName')
this.methodName = this.getFieldValue('methodName')
this.appendValueInput('itemName')
- .appendField('of item ')
+ .appendField('of item')
.setAlign(Blockly.ALIGN_RIGHT)
.setCheck(['String', 'oh_item', 'oh_itemtype'])
this.appendValueInput('persistenceName')
@@ -46,29 +49,46 @@ export default function defineOHBlocks_Persistence (f7, isGraalJs, persistenceSe
this.setTooltip(() => {
let methodName = this.getFieldValue('methodName')
let TIP = {
- 'averageSince': 'Gets the average value of the State of a persisted Item since a certain point in time. This method uses a time-weighted average calculation',
- 'averageUntil': 'Gets the average value of the State of a persisted Item until a certain point in time. This method uses a time-weighted average calculation',
- 'deltaSince': 'Gets the difference in value of the State of a given Item since a certain point in time',
- 'deltaUntil': 'Gets the difference in value of the State of a given Item until a certain point in time',
- 'deviationSince': 'Gets the standard deviation of the state of the given Item since a certain point in time',
- 'deviationUntil': 'Gets the standard deviation of the state of the given Item until a certain point in time',
- 'varianceSince': 'Gets the variance of the state of the given Item since a certain point in time',
- 'varianceUntil': 'Gets the variance of the state of the given Item until a certain point in time',
- 'evolutionRateSince': 'Gets the evolution rate of the state of a given Item since a certain point in time',
- 'evolutionRateUntil': 'Gets the evolution rate of the state of a given Item until a certain point in time',
- 'minimumSince': 'Gets the minimum value of the State of a persisted Item since a certain point in time',
- 'minimumUntil': 'Gets the minimum value of the State of a persisted Item until a certain point in time',
- 'maximumSince': 'Gets the maximum value of the State of a persisted Item since a certain point in time',
- 'maximumUntil': 'Gets the maximum value of the State of a persisted Item until a certain point in time',
- 'sumSince': 'Gets the sum of the previous States of a persisted Item since a certain point in time',
- 'sumUntil': 'Gets the sum of the previous States of a persisted Item until a certain point in time',
- 'previousState': 'Gets the previous state with option to skip to different value as current',
- 'nextState': 'Gets the next state with option to skip to different value as current',
- 'previousNumericState': 'Gets the previous state without the unit with option to skip to different value as current',
- 'nextNumericState': 'Gets the next state without the unit with option to skip to different value as current',
- 'previousStateTime': 'Gets the time when previous state last occurred with option to skip to different value as current',
- 'nextStateTime': 'Gets the time when next state will occur with option to skip to different value as current',
- 'persisted': 'Gets the persisted state at a certain point in time'
+ 'averageSince': 'Gets the average value of the State of the Item since a certain point in time. This method uses a time-weighted average calculation',
+ 'averageUntil': 'Gets the average value of the State of the Item until a certain point in time. This method uses a time-weighted average calculation',
+ 'averageBetween': 'Gets the average value of the State of the Item between two points in time. This method uses a time-weighted average calculation',
+ 'deltaSince': 'Gets the difference in value of the State of the Item since a certain point in time',
+ 'deltaUntil': 'Gets the difference in value of the State of the Item until a certain point in time',
+ 'deltaBetween': 'Gets the difference in value of the State of the Item between two points in time',
+ 'deviationSince': 'Gets the standard deviation of the State of the Item since a certain point in time',
+ 'deviationUntil': 'Gets the standard deviation of the State of the Item until a certain point in time',
+ 'deviationBetween': 'Gets the standard deviation of the State of the Item between two points in time',
+ 'varianceSince': 'Gets the variance of the State of the Item since a certain point in time',
+ 'varianceUntil': 'Gets the variance of the State of the Item until a certain point in time',
+ 'varianceBetween': 'Gets the variance of the State of the Item between two points in time',
+ 'evolutionRateSince': 'Gets the evolution rate of the State of the Item since a certain point in time',
+ 'evolutionRateUntil': 'Gets the evolution rate of the State of the Item until a certain point in time',
+ 'evolutionRateBetween': 'Gets the evolution rate of the State of the Item between two points in time',
+ 'minimumSince': 'Gets the minimum value of the State of the Item since a certain point in time',
+ 'minimumUntil': 'Gets the minimum value of the State of the Item until a certain point in time',
+ 'minimumBetween': 'Gets the minimum value of the State of the Item between two points in time',
+ 'maximumSince': 'Gets the maximum value of the State of the Item since a certain point in time',
+ 'maximumUntil': 'Gets the maximum value of the State of the Item until a certain point in time',
+ 'maximumBetween': 'Gets the maximum value of the State of the Item between two points in time',
+ 'sumSince': 'Gets the sum of the previous States of the Item since a certain point in time',
+ 'sumUntil': 'Gets the sum of the future States of the Item until a certain point in time',
+ 'sumBetween': 'Gets the sum of the States of the Item between two points in time',
+ 'previousState': 'Gets the previous State of the Item, with option to skip to different value as current',
+ 'nextState': 'Gets the next State of the Item, with option to skip to different value as current',
+ 'getAllStatesSince': 'Gets Array of timestamp and state pairs of persisted items since a certain point in time',
+ 'getAllStatesUntil': 'Gets Array of timestamp and state pairs of persisted items until a certain point in time',
+ 'getAllStatesBetween': 'Gets Array of timestamp and state pairs of persisted items between two points in time',
+ 'previousNumericState': 'Gets the previous State of the Item without the unit, with option to skip to different value as current',
+ 'nextNumericState': 'Gets the next State of the Item without the unit, with option to skip to different value as current',
+ 'previousStateTime': 'Gets the time when previous State of the Item last occurred, with option to skip to different value as current',
+ 'nextStateTime': 'Gets the time when next State of the Item will occur, with option to skip to different value as current',
+ 'countSince': 'Gets the number of stored State updates of the Item since a certain point in time',
+ 'countUntil': 'Gets the number of stored State updates of the Item until a certain point in time',
+ 'countBetween': 'Gets the number of stored State updates of the Item between two points in time',
+ 'countStateChangesSince': 'Gets the number of State changes of the Item since a certain point in time',
+ 'countStateChangesUntil': 'Gets the number of State changes of the Item until a certain point in time',
+ 'countStateChangesBetween': 'Gets the number of State changes of the Item between two points in time',
+ 'persistedState': 'Gets the State of the Item at a certain point in time'
}
return TIP[methodName]
})
@@ -85,10 +105,35 @@ export default function defineOHBlocks_Persistence (f7, isGraalJs, persistenceSe
if (!persistenceNameInput.getShadowDom()) {
persistenceNameInput.setShadowDom(Blockly.utils.xml.textToDom(''))
}
+
+ // Always remove when switching, so the sequence of the selection list gets updated
+ if (this.getInput('returnTypeInput')) {
+ this.removeInput('returnTypeInput')
+ }
+ if (isGraalJs && ![
+ 'evolutionRateSince', 'evolutionRateUntil', 'evolutionRateBetween',
+ 'countSince', 'countUntil', 'countBetween',
+ 'countStateChangesSince', 'countStateChangesUntil', 'countStateChangesBetween',
+ 'previousNumericState', 'nextNumericState', 'previousStateTime', 'nextStateTime'
+ ].includes(this.methodName)) {
+ this.appendDummyInput('returnTypeInput')
+ .appendField('as')
+ .appendField(new Blockly.FieldDropdown(this.returnTypeNames()), 'returnTypeName')
+ .setAlign(Blockly.ALIGN_RIGHT)
+ this.moveInputBefore('returnTypeInput', 'itemName')
+ }
+
+ let hasSinceField = this.methodName.endsWith('Since') || this.methodName.endsWith('Between') || (this.methodName === 'persistedState')
+ let hasUntilField = this.methodName.endsWith('Until') || this.methodName.endsWith('Between')
+
+ if (this.getInput('dayInfoSince') && !hasSinceField) {
+ this.removeInput('dayInfoSince')
+ }
+ if (this.getInput('dayInfoUntil') && !hasUntilField) {
+ this.removeInput('dayInfoUntil')
+ }
+
if (['previousState', 'nextState', 'previousNumericState', 'nextNumericState', 'previousStateTime', 'nextStateTime'].includes(this.methodName)) {
- if (this.getInput('dayInfo')) {
- this.removeInput('dayInfo')
- }
if (!this.getInput('skipPrevious')) {
this.appendValueInput('skipPrevious')
.appendField('skip same ')
@@ -100,20 +145,113 @@ export default function defineOHBlocks_Persistence (f7, isGraalJs, persistenceSe
this.removeInput('skipPrevious')
}
- const preposition = (this.methodName === 'persistedState') ? 'at' : (this.methodName.endsWith('Until') ? 'until' : 'since')
+ const prepositionSince = (this.methodName === 'persistedState') ? 'at' : (this.methodName.endsWith('Since') ? 'since' : 'between')
+ const prepositionUntil = this.methodName.endsWith('Until') ? 'until' : 'and'
+
+ if (hasSinceField) {
+ if (!this.getInput('dayInfoSince')) {
+ this.appendValueInput('dayInfoSince')
+ .appendField(prepositionSince, 'prepositionSince')
+ .setCheck(['ZonedDateTime'])
+ this.getInput('dayInfoSince').setShadowDom(
+ Blockly.utils.xml.textToDom(`
+
+
+ 1
+
+
+ Hours
+ minus
+ `))
+ if (this.getInput('dayInfoUntil')) {
+ this.moveInputBefore('dayInfoSince', 'dayInfoUntil')
+ } else {
+ this.moveInputBefore('dayInfoSince', 'persistenceName')
+ }
+ } else {
+ const prepositionField = this.getField('prepositionSince')
+ if (prepositionField.getText() !== prepositionSince) {
+ prepositionField.setValue(prepositionSince)
+ }
+ }
+ }
- if (!this.getInput('dayInfo')) {
- this.appendValueInput('dayInfo')
- .appendField(preposition, 'preposition')
- .setCheck(['ZonedDateTime'])
- this.moveInputBefore('dayInfo', 'persistenceName')
- } else {
- const prepositionField = this.getField('preposition')
- if (prepositionField.getText() !== preposition) {
- prepositionField.setValue(preposition)
+ if (hasUntilField) {
+ if (!this.getInput('dayInfoUntil')) {
+ this.appendValueInput('dayInfoUntil')
+ .appendField(prepositionUntil, 'prepositionUntil')
+ .setCheck(['ZonedDateTime'])
+ this.getInput('dayInfoUntil').setShadowDom(
+ Blockly.utils.xml.textToDom(`
+
+
+ 1
+
+
+ Hours
+ plus
+ `))
+ this.moveInputBefore('dayInfoUntil', 'persistenceName')
+ } else {
+ const prepositionField = this.getField('prepositionUntil')
+ if (prepositionField.getText() !== prepositionUntil) {
+ prepositionField.setValue(prepositionUntil)
+ }
}
}
}
+ },
+ returnTypeNames: function () {
+ // use different list of return types and sequence to make sure first entry is old behaviour for backward compatibility
+ let returnTypes = []
+ switch (this.methodName) {
+ case 'persistedState':
+ case 'previousState':
+ case 'nextState':
+ case 'maximumSince':
+ case 'maximumUntil':
+ case 'maximumBetween':
+ case 'minimumSince':
+ case 'minimumUntil':
+ case 'minimumBetween':
+ returnTypes = [['String', 'state'],
+ ['Quantity', 'quantityState'],
+ ['Number', 'numericState'],
+ ['Timestamp', 'timestamp']]
+ break
+
+ case 'averageSince':
+ case 'averageUntil':
+ case 'averageBetween':
+ case 'deltaSince':
+ case 'deltaUntil':
+ case 'deltaBetween':
+ case 'deviationSince':
+ case 'deviationUntil':
+ case 'deviationBetween':
+ case 'sumSince':
+ case 'sumUntil':
+ case 'sumBetween':
+ case 'varianceSince':
+ case 'varianceUntil':
+ case 'varianceBetween':
+ returnTypes = [['Number', 'numericState'],
+ ['Quantity', 'quantityState'],
+ ['String', 'state']]
+ break
+
+ case 'getAllStatesSince':
+ case 'getAllStatesUntil':
+ case 'getAllStatesBetween':
+ returnTypes = [['String', 'state'],
+ ['Quantity', 'quantityState'],
+ ['Number', 'numericState']]
+ break
+
+ default:
+ break
+ }
+ return returnTypes
}
}
@@ -126,64 +264,78 @@ export default function defineOHBlocks_Persistence (f7, isGraalJs, persistenceSe
const inputType = blockGetCheckedInputType(block, 'itemName')
const methodName = block.getFieldValue('methodName')
+ const returnTypeName = block.getFieldValue('returnTypeName')
const persistenceName = javascriptGenerator.valueToCode(block, 'persistenceName', javascriptGenerator.ORDER_NONE)
const persistence = (isGraalJs) ? null : addPersistence()
const itemCode = generateItemCode(itemName, inputType)
let code = ''
- let dayInfo = ''
+ const dayInfoSince = javascriptGenerator.valueToCode(block, 'dayInfoSince', javascriptGenerator.ORDER_NONE)
+ const dayInfoUntil = javascriptGenerator.valueToCode(block, 'dayInfoUntil', javascriptGenerator.ORDER_NONE)
+ const dayInfo = dayInfoSince + ((dayInfoSince && dayInfoUntil) ? ' ,' : '') + dayInfoUntil
let skipPrevious = javascriptGenerator.valueToCode(block, 'skipPrevious', javascriptGenerator.ORDER_NONE)
skipPrevious = ((skipPrevious === 'undefined') ? false : skipPrevious)
const persistenceExtension = (persistenceName === '\'default\'') ? '' : `, ${persistenceName}`
switch (methodName) {
- // Returning JS PersistedItem (GraalJS) or org.openhab.core.persistence.HistoricItem
+ // Returning JS PersistedItem mapped to return type (GraalJS) or org.openhab.core.persistence.HistoricItem
+ case 'persistedState':
case 'maximumSince':
case 'maximumUntil':
+ case 'maximumBetween':
case 'minimumSince':
case 'minimumUntil':
- case 'persistedState':
- dayInfo = javascriptGenerator.valueToCode(block, 'dayInfo', javascriptGenerator.ORDER_NONE)
- code = (isGraalJs) ? `${itemCode}.persistence.${methodName}(${dayInfo}${persistenceExtension})?.state` : `${persistence}.${methodName}(${itemCode}, ${dayInfo}${persistenceExtension}).getState()`
+ case 'minimumBetween':
+ code = (isGraalJs) ? `${itemCode}.persistence.${methodName}(${dayInfo}${persistenceExtension})?.${returnTypeName}` : `${persistence}.${methodName}(${itemCode}, ${dayInfo}${persistenceExtension}).getState()`
break
case 'previousState':
case 'nextState':
- code = (isGraalJs) ? `${itemCode}.persistence.${methodName}(${skipPrevious}${persistenceExtension})?.state` : `${persistence}.${methodName}(${itemCode},${skipPrevious}${persistenceExtension}).getState()`
+ code = (isGraalJs) ? `${itemCode}.persistence.${methodName}(${skipPrevious}${persistenceExtension})?.${returnTypeName}` : `${persistence}.${methodName}(${itemCode}, ${skipPrevious}${persistenceExtension}).getState()`
break
case 'previousNumericState':
- code = (isGraalJs) ? `${itemCode}.persistence.previousState(${skipPrevious}${persistenceExtension})?.numericState` : `${persistence}.previousState(${itemCode},${skipPrevious}${persistenceExtension}).getNumericState()`
+ code = (isGraalJs) ? `${itemCode}.persistence.previousState(${skipPrevious}${persistenceExtension})?.numericState` : `${persistence}.previousState(${itemCode}, ${skipPrevious}${persistenceExtension}).getNumericState()`
break
case 'nextNumericState':
- code = (isGraalJs) ? `${itemCode}.persistence.nextState(${skipPrevious}${persistenceExtension})?.numericState` : `${persistence}.nextState(${itemCode},${skipPrevious}${persistenceExtension}).getNumericState()`
+ code = (isGraalJs) ? `${itemCode}.persistence.nextState(${skipPrevious}${persistenceExtension})?.numericState` : `${persistence}.nextState(${itemCode}, ${skipPrevious}${persistenceExtension}).getNumericState()`
break
case 'previousStateTime':
- code = (isGraalJs) ? `${itemCode}.persistence.previousState(${skipPrevious}${persistenceExtension})?.timestamp` : `${persistence}.previousState(${itemCode},${skipPrevious}${persistenceExtension}).getTimestamp()`
+ code = (isGraalJs) ? `${itemCode}.persistence.previousState(${skipPrevious}${persistenceExtension})?.timestamp` : `${persistence}.previousState(${itemCode}, ${skipPrevious}${persistenceExtension}).getTimestamp()`
break
case 'nextStateTime':
- code = (isGraalJs) ? `${itemCode}.persistence.nextState(${skipPrevious}${persistenceExtension})?.timestamp` : `${persistence}.nextState(${itemCode},${skipPrevious}${persistenceExtension}).getTimestamp()`
+ code = (isGraalJs) ? `${itemCode}.persistence.nextState(${skipPrevious}${persistenceExtension})?.timestamp` : `${persistence}.nextState(${itemCode}, ${skipPrevious}${persistenceExtension}).getTimestamp()`
break
- // Returning JS PersistedState (GraalJS) or org.openhab.core.types.State
+ // Returning JS PersistedState mapped to return type (GraalJS) or org.openhab.core.types.State cast to float
case 'averageSince':
case 'averageUntil':
+ case 'averageBetween':
case 'deltaSince':
case 'deltaUntil':
+ case 'deltaBetween':
case 'deviationSince':
case 'deviationUntil':
+ case 'deviationBetween':
case 'sumSince':
case 'sumUntil':
+ case 'sumBetween':
case 'varianceSince':
case 'varianceUntil':
- dayInfo = javascriptGenerator.valueToCode(block, 'dayInfo', javascriptGenerator.ORDER_NONE)
- code = (isGraalJs) ? `${itemCode}.persistence.${methodName}(${dayInfo}${persistenceExtension})?.numericState` : `parseFloat(${persistence}.${methodName}(${itemCode}, ${dayInfo}${persistenceExtension}).getState())`
+ case 'varianceBetween':
+ code = (isGraalJs) ? `${itemCode}.persistence.${methodName}(${dayInfo}${persistenceExtension})?.${returnTypeName}` : `parseFloat(${persistence}.${methodName}(${itemCode}, ${dayInfo}${persistenceExtension}).getState())`
+ break
+
+ // Returning JS Array of timestamp and state pairs, whereby PersistedState is mapped to return type (GraalJS) or org.openhab.core.persistence.HistoricItem
+ case 'getAllStatesSince':
+ case 'getAllStatesUntil':
+ case 'getAllStatesBetween':
+ code = (isGraalJs) ? `${itemCode}.persistence.${methodName}(${dayInfo}${persistenceExtension}).map(v => ([v.timestamp, v.${returnTypeName}]))` : `${persistence}.${methodName}(${itemCode}, ${dayInfo}${persistenceExtension}.map(v => ([v.getTimestamp(), v.getState()]))`
break
default:
- dayInfo = javascriptGenerator.valueToCode(block, 'dayInfo', javascriptGenerator.ORDER_NONE)
code = (isGraalJs) ? `${itemCode}.persistence.${methodName}(${dayInfo}${persistenceExtension})` : `${persistence}.${methodName}(${itemCode}, ${dayInfo}${persistenceExtension})`
break
}
@@ -208,9 +360,13 @@ export default function defineOHBlocks_Persistence (f7, isGraalJs, persistenceSe
persistenceNameInput.setShadowDom(Blockly.utils.xml.textToDom(''))
}
this.appendValueInput('dayInfo')
- .appendField(new Blockly.FieldDropdown([['has changed since', 'changedSince'], ['will have changed until', 'changedUntil'], ['has been updated since', 'updatedSince'], ['will have been updated until', 'updatedUntil']]), 'methodName')
+ .appendField(new Blockly.FieldDropdown([
+ ['has changed since', 'changedSince'], ['will have changed until', 'changedUntil'], ['changes between', 'changedBetween'],
+ ['has been updated since', 'updatedSince'], ['will have been updated until', 'updatedUntil'], ['is updated between', 'updatedBetween']
+ ], this.handleTypeSelection.bind(this)), 'methodName')
.setAlign(Blockly.ALIGN_RIGHT)
.setCheck(['ZonedDateTime'])
+ this.methodName = this.getFieldValue('methodName')
this.setInputsInline(false)
this.setOutput(true, null)
@@ -222,13 +378,42 @@ export default function defineOHBlocks_Persistence (f7, isGraalJs, persistenceSe
let TIP = {
'changedSince': 'Checks if the State of the Item has (ever) changed since a certain point in time',
'changedUntil': 'Checks if the State of the Item will have (ever) changed until a certain point in time',
+ 'changedBetween': 'Checks if the State of the Item will have (ever) changed between two points in time',
'updatedSince': 'Checks if the State of the Item has been updated since a certain point in time',
- 'updatedUntil': 'Checks if the State of the Item will have been updated until a certain point in time'
+ 'updatedUntil': 'Checks if the State of the Item will have been updated until a certain point in time',
+ 'updatedBetween': 'Checks if the State of the Item will have been updated between two points in time'
}
return TIP[methodName]
})
this.setHelpUrl('https://www.openhab.org/docs/configuration/blockly/rules-blockly-persistence.html#check-item-change-update-since-a-point-in-time')
+ },
+ handleTypeSelection: function (methodName) {
+ if (this.methodName !== methodName) {
+ this.methodName = methodName
+ this.updateShape()
+ }
+ },
+ updateShape: function () {
+ if (this.methodName.endsWith('Between')) {
+ if (!this.getInput('dayInfo2')) {
+ this.appendValueInput('dayInfo2')
+ .appendField('and')
+ .setCheck(['ZonedDateTime'])
+ this.getInput('dayInfo2').setShadowDom(
+ Blockly.utils.xml.textToDom(`
+
+
+ 1
+
+
+ Hours
+ plus
+ `))
+ }
+ } else if (this.getInput('dayInfo2')) {
+ this.removeInput('dayInfo2')
+ }
}
}
@@ -241,7 +426,9 @@ export default function defineOHBlocks_Persistence (f7, isGraalJs, persistenceSe
const inputType = blockGetCheckedInputType(block, 'itemName')
const methodName = block.getFieldValue('methodName')
- const dayInfo = javascriptGenerator.valueToCode(block, 'dayInfo', javascriptGenerator.ORDER_NONE)
+ const dayInfo1 = javascriptGenerator.valueToCode(block, 'dayInfo', javascriptGenerator.ORDER_NONE)
+ const dayInfo2 = methodName.endsWith('Between') ? javascriptGenerator.valueToCode(block, 'dayInfo2', javascriptGenerator.ORDER_NONE) : undefined
+ const dayInfo = dayInfo2 ? `${dayInfo1}, ${dayInfo2}` : dayInfo1
const persistenceName = javascriptGenerator.valueToCode(block, 'persistenceName', javascriptGenerator.ORDER_NONE)
const persistenceExtension = (persistenceName === '\'default\'') ? '' : `, ${persistenceName}`
@@ -264,10 +451,11 @@ export default function defineOHBlocks_Persistence (f7, isGraalJs, persistenceSe
init: function () {
this.appendDummyInput()
.appendField(new Blockly.FieldDropdown([
- ['last', 'lastUpdate'], ['next', 'nextUpdate']
+ ['last updated', 'lastUpdate'], ['next updated', 'nextUpdate'],
+ ['last changed', 'lastChange'], ['next changed', 'nextChange']
]), 'methodName')
this.appendDummyInput()
- .appendField(' updated date of')
+ .appendField(' date of')
this.appendValueInput('itemName')
.setCheck(['String', 'oh_item', 'oh_itemtype'])
const persistenceNameInput = this.appendValueInput('persistenceName')
@@ -285,8 +473,10 @@ export default function defineOHBlocks_Persistence (f7, isGraalJs, persistenceSe
this.setTooltip(() => {
const methodName = this.getFieldValue('methodName')
const TIP = {
- 'lastUpdate': 'Get the last update time of the provided item',
- 'nextUpdate': 'Get the next update time of the provided item'
+ 'lastUpdate': 'Get the last update time of the provided item (null if the item state changed since last being persisted)',
+ 'nextUpdate': 'Get the next update time of the provided item',
+ 'lastChange': 'Get the last changed time of the provided item (null if the item state changed since last being persisted)',
+ 'nextChange': 'Get the next changed time of the provided item'
}
return TIP[methodName]
})
@@ -304,7 +494,7 @@ export default function defineOHBlocks_Persistence (f7, isGraalJs, persistenceSe
const persistenceName = javascriptGenerator.valueToCode(block, 'persistenceName', javascriptGenerator.ORDER_NONE)
const persistenceExtension = (persistenceName === '\'default\'') ? '' : ((!isGraalJs) ? ',' : '') + ` ${persistenceName}`
- let itemCode = generateItemCode(itemName, inputType)
+ const itemCode = generateItemCode(itemName, inputType)
if (isGraalJs) {
return [`${itemCode}.persistence.${methodName}(${persistenceExtension})`, 0]
@@ -316,6 +506,299 @@ export default function defineOHBlocks_Persistence (f7, isGraalJs, persistenceSe
}
}
+ /*
+ * Persist a state or list of states
+ * Blockly part
+ */
+ Blockly.Blocks['oh_persist'] = {
+ init: function () {
+ const statesInput = this.appendValueInput('states')
+ .appendField('persist')
+ .appendField(new Blockly.FieldDropdown([
+ ['state (at current time)', 'currentState'], ['state (at specific time)', 'stateAt'], ['list of states (adding)', 'statesListADD'], ['list of states (replacing)', 'statesListREPLACE']
+ ], this.handleTypeSelection.bind(this)), 'persistType')
+ .setCheck(['String', 'Array'])
+ statesInput.setShadowDom(
+ Blockly.utils.xml.textToDom(`
+ state
+ `))
+ this.appendValueInput('itemName')
+ .appendField('for item')
+ .setAlign(Blockly.ALIGN_RIGHT)
+ .setCheck(['String', 'oh_item', 'oh_itemtype'])
+ const persistenceNameInput = this.appendValueInput('persistenceName')
+ .appendField('to')
+ .setCheck(null)
+ if (!persistenceNameInput.getShadowDom()) {
+ persistenceNameInput.setShadowDom(Blockly.utils.xml.textToDom(''))
+ }
+
+ this.setInputsInline(false)
+ this.setColour(0)
+
+ this.setTooltip(() => {
+ const persistType = this.getFieldValue('persistType')
+ const TIP = {
+ 'currentState': 'Persist a state to Item Persistence at current time (this does not update the state of the item)',
+ 'stateAt': 'Persist a state to Item Persistence at a given point in time',
+ 'statesListADD': 'Persist a list of timestamp and state pairs to Item Persistence, update/add to existing persisted states',
+ 'statesListREPLACE': 'Persist a list of timestamp and state pairs to Item Persistence, replace all persisted states between earlies and latest of new states'
+ }
+ return TIP[persistType]
+ })
+ this.setHelpUrl('https://www.openhab.org/docs/configuration/blockly/rules-blockly-persistence.html#persist-item')
+
+ this.setPreviousStatement(true, null)
+ this.setNextStatement(true, null)
+ },
+ handleTypeSelection: function (persistType) {
+ if (this.persistType !== persistType) {
+ this.persistType = persistType
+ this.updateShape()
+ }
+ },
+ updateShape: function () {
+ const persistenceNameInput = this.getInput('persistenceName')
+ if (!persistenceNameInput.getShadowDom()) {
+ persistenceNameInput.setShadowDom(Blockly.utils.xml.textToDom(''))
+ }
+
+ const hasAtField = (this.persistType === 'stateAt')
+ if (this.getInput('at') && !hasAtField) {
+ this.removeInput('at')
+ }
+ if (hasAtField && !this.getInput('at')) {
+ this.appendValueInput('at')
+ .appendField('at')
+ .setCheck(['ZonedDateTime'])
+ this.getInput('at').setShadowDom(
+ Blockly.utils.xml.textToDom(`
+
+
+ 1
+
+
+ Hours
+ minus
+ `))
+ this.moveInputBefore('at', 'persistenceName')
+ }
+
+ const hasStatesList = this.persistType.startsWith('statesList')
+ const statesInput = this.getInput('states')
+ if (hasStatesList) {
+ statesInput.setShadowDom(
+ Blockly.utils.xml.textToDom(`
+
+
+
+
+ 1
+ Hours
+ plus
+
+ state
+
+
+
+
+ 2
+ Hours
+ plus
+
+ state
+
+ `))
+ } else {
+ statesInput.setShadowDom(
+ Blockly.utils.xml.textToDom(`
+ state
+ `))
+ }
+ }
+ }
+
+ /*
+ * Persist a state or list of states
+ * Code part
+ */
+ javascriptGenerator.forBlock['oh_persist'] = function (block) {
+ const itemName = javascriptGenerator.valueToCode(block, 'itemName', javascriptGenerator.ORDER_ATOMIC)
+ const inputType = blockGetCheckedInputType(block, 'itemName')
+ const itemCode = generateItemCode(itemName, inputType)
+
+ const persistType = block.getFieldValue('persistType')
+
+ const states = javascriptGenerator.valueToCode(block, 'states', javascriptGenerator.ORDER_ATOMIC)
+ const at = javascriptGenerator.valueToCode(block, 'at', javascriptGenerator.ORDER_NONE)
+ const policy = persistType.endsWith('REPLACE') ? 'REPLACE' : 'ADD'
+
+ const persistence = (isGraalJs) ? null : addPersistence()
+ const persistenceName = javascriptGenerator.valueToCode(block, 'persistenceName', javascriptGenerator.ORDER_NONE)
+ const persistenceExtension = (persistenceName === '\'default\'') ? '' : `, ${persistenceName}`
+
+ let code = ''
+ switch (persistType) {
+ case 'currentState':
+ code += isGraalJs ? `${itemCode}.persistence.persist(${states}${persistenceExtension});` : `${persistence}.persist(${itemCode}, ${states}${persistenceExtension});`
+ break
+ case 'stateAt':
+ code += isGraalJs ? `${itemCode}.persistence.persist(${at}, ${states}${persistenceExtension});` : `${persistence}.persist(${itemCode}, ${at}, ${states}${persistenceExtension});`
+ break
+ case 'statesListADD':
+ case 'statesListREPLACE':
+ const timeSeriesVar = javascriptGenerator.nameDB_.getDistinctName('timeSeries', Blockly.Names.NameType.VARIABLE)
+ code += `var ${timeSeriesVar} = new items.TimeSeries('${policy}');\n`
+ code += `${states}.forEach(s => ${timeSeriesVar}.add(s[0], s[1]));\n`
+ code += isGraalJs ? `${itemCode}.persistence.persist(timeSeries${persistenceExtension});` : `${persistence}.persist(${itemCode}, timeSeries${persistenceExtension});`
+ break
+ default:
+ break
+ }
+ code += '\n'
+ return code
+ }
+
+ /*
+ * Delete persisted values for an item
+ * Blockly part
+ */
+ Blockly.Blocks['oh_delete_persistedvalues'] = {
+ init: function () {
+ this.appendDummyInput()
+ .appendField('remove')
+ .appendField(new Blockly.FieldDropdown([
+ ['all states since', 'removeAllStatesSince'], ['all states until', 'removeAllStatesUntil'], ['all states between', 'removeAllStatesBetween']
+ ], this.handleTypeSelection.bind(this)
+ ), 'methodName')
+ this.methodName = this.getFieldValue('methodName')
+ this.appendValueInput('itemName')
+ .appendField('of item')
+ .setAlign(Blockly.ALIGN_RIGHT)
+ .setCheck(['String', 'oh_item', 'oh_itemtype'])
+ this.appendValueInput('persistenceName')
+ .appendField('from')
+ .setCheck(null)
+ this.updateShape()
+ this.setInputsInline(false)
+ this.setColour(0)
+
+ this.setTooltip(() => {
+ const methodName = this.getFieldValue('methodName')
+ const TIP = {
+ 'removeAllStatesSince': 'Delete all persisted states of an Item since a certain point in time',
+ 'removeAllStatesUntil': 'Delete all persisted states of an Item until a certain point in time',
+ 'removeAllStatesBetween': 'Delete all persisted states of an Item between two points in time'
+ }
+ return TIP[methodName]
+ })
+ this.setHelpUrl('https://www.openhab.org/docs/configuration/blockly/rules-blockly-persistence.html#remove_persisted_states_for_an_item')
+
+ this.setPreviousStatement(true, null)
+ this.setNextStatement(true, null)
+ },
+ handleTypeSelection: function (methodName) {
+ if (this.methodName !== methodName) {
+ this.methodName = methodName
+ this.updateShape()
+ }
+ },
+ updateShape: function () {
+ const persistenceNameInput = this.getInput('persistenceName')
+ if (!persistenceNameInput.getShadowDom()) {
+ persistenceNameInput.setShadowDom(Blockly.utils.xml.textToDom(''))
+ }
+
+ let hasSinceField = this.methodName.endsWith('Since') || this.methodName.endsWith('Between')
+ let hasUntilField = this.methodName.endsWith('Until') || this.methodName.endsWith('Between')
+
+ if (this.getInput('dayInfoSince') && !hasSinceField) {
+ this.removeInput('dayInfoSince')
+ }
+ if (this.getInput('dayInfoUntil') && !hasUntilField) {
+ this.removeInput('dayInfoUntil')
+ }
+
+ const prepositionSince = this.methodName.endsWith('Since') ? 'since' : 'between'
+ const prepositionUntil = this.methodName.endsWith('Until') ? 'until' : 'and'
+
+ if (hasSinceField) {
+ if (!this.getInput('dayInfoSince')) {
+ this.appendValueInput('dayInfoSince')
+ .appendField(prepositionSince, 'prepositionSince')
+ .setCheck(['ZonedDateTime'])
+ this.getInput('dayInfoSince').setShadowDom(
+ Blockly.utils.xml.textToDom(`
+
+
+ 1
+
+
+ Hours
+ minus
+ `))
+ if (this.getInput('dayInfoUntil')) {
+ this.moveInputBefore('dayInfoSince', 'dayInfoUntil')
+ } else {
+ this.moveInputBefore('dayInfoSince', 'persistenceName')
+ }
+ } else {
+ const prepositionField = this.getField('prepositionSince')
+ if (prepositionField.getText() !== prepositionSince) {
+ prepositionField.setValue(prepositionSince)
+ }
+ }
+ }
+
+ if (hasUntilField) {
+ if (!this.getInput('dayInfoUntil')) {
+ this.appendValueInput('dayInfoUntil')
+ .appendField(prepositionUntil, 'prepositionUntil')
+ .setCheck(['ZonedDateTime'])
+ this.getInput('dayInfoUntil').setShadowDom(
+ Blockly.utils.xml.textToDom(`
+
+
+ 1
+
+
+ Hours
+ plus
+ `))
+ this.moveInputBefore('dayInfoUntil', 'persistenceName')
+ } else {
+ const prepositionField = this.getField('prepositionUntil')
+ if (prepositionField.getText() !== prepositionUntil) {
+ prepositionField.setValue(prepositionUntil)
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * Delete persisted values for an item
+ * Code part
+ */
+ javascriptGenerator.forBlock['oh_delete_persistedvalues'] = function (block) {
+ const itemName = javascriptGenerator.valueToCode(block, 'itemName', javascriptGenerator.ORDER_ATOMIC)
+ const inputType = blockGetCheckedInputType(block, 'itemName')
+ const itemCode = generateItemCode(itemName, inputType)
+
+ const methodName = block.getFieldValue('methodName')
+
+ const dayInfoSince = javascriptGenerator.valueToCode(block, 'dayInfoSince', javascriptGenerator.ORDER_NONE)
+ const dayInfoUntil = javascriptGenerator.valueToCode(block, 'dayInfoUntil', javascriptGenerator.ORDER_NONE)
+ const dayInfo = dayInfoSince + ((dayInfoSince && dayInfoUntil) ? ' ,' : '') + dayInfoUntil
+
+ const persistence = (isGraalJs) ? null : addPersistence()
+ const persistenceName = javascriptGenerator.valueToCode(block, 'persistenceName', javascriptGenerator.ORDER_NONE)
+ const persistenceExtension = (persistenceName === '\'default\'') ? '' : `, ${persistenceName}`
+
+ const code = (isGraalJs) ? `${itemCode}.persistence.${methodName}(${dayInfo}${persistenceExtension});\n` : `${persistence}.${methodName}(${itemCode}, ${dayInfo}${persistenceExtension});\n`
+ return code
+ }
+
function generateItemCode (itemName, inputType) {
if (isGraalJs) {
return (inputType === 'oh_item' || inputType === 'String') ? `items.getItem(${itemName})` : `${itemName}`
diff --git a/bundles/org.openhab.ui/web/src/components/config/controls/blockly-editor.vue b/bundles/org.openhab.ui/web/src/components/config/controls/blockly-editor.vue
index 2401ea2927..6a2d0e84c5 100644
--- a/bundles/org.openhab.ui/web/src/components/config/controls/blockly-editor.vue
+++ b/bundles/org.openhab.ui/web/src/components/config/controls/blockly-editor.vue
@@ -921,7 +921,7 @@
-
+
@@ -954,6 +954,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+ Hours
+ minus
+
+
+