diff --git a/bundles/org.openhab.ui/web/src/assets/definitions/blockly/_tmp/blocks-callscript.js b/bundles/org.openhab.ui/web/src/assets/definitions/blockly/_tmp/blocks-callscript.js
deleted file mode 100644
index 2acface1a1..0000000000
--- a/bundles/org.openhab.ui/web/src/assets/definitions/blockly/_tmp/blocks-callscript.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import Blockly from 'blockly'
-import { FieldItemModelPicker } from './ohitemfield'
-
-export default function defineOHBlocks_Subsystem (f7, scripts) {
- Blockly.Blocks['oh_callscript'] = {
- init: function () {
- this.appendValueInput('script')
- .setCheck(null)
- .appendField('Call Script')
- this.setInputsInline(true)
- this.setPreviousStatement(true, null)
- this.setNextStatement(true, null)
- this.setColour(230)
- this.setTooltip('Calls a script which must be located in the $OPENHAB_CONF/scripts folder')
- this.setHelpUrl('https://www.openhab.org/docs/configuration/actions.html')
- }
- }
-
- Blockly.JavaScript['oh_callscript'] = function (block) {
- const scriptExecution = Blockly.JavaScript.provideFunction_(
- 'scriptExecution',
- ['var ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + ' = Java.type(\'org.openhab.core.model.script.actions.ScriptExecution\');'])
- let script = Blockly.JavaScript.valueToCode(block, 'script', Blockly.JavaScript.ORDER_ATOMIC)
- let code = scriptExecution + '.callScript(' + script + ');\n'
- return code
- }
-}
diff --git a/bundles/org.openhab.ui/web/src/assets/definitions/blockly/_tmp/blocks-ephemeris.js b/bundles/org.openhab.ui/web/src/assets/definitions/blockly/_tmp/blocks-ephemeris.js
deleted file mode 100644
index b14a29f982..0000000000
--- a/bundles/org.openhab.ui/web/src/assets/definitions/blockly/_tmp/blocks-ephemeris.js
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
-* Ephemeris provides calendar related information
-* This code has been originally provided by https://github.com/bigbasec
-*
-* See more background info on openHAB ephemeris here: https://www.openhab.org/docs/configuration/actions.html#ephemeris
-*/
-
-import Blockly from 'blockly'
-import { FieldItemModelPicker } from './ohitemfield'
-
-export default function defineOHBlocks_Ephemeris (f7) {
- Blockly.Blocks['oh_ephemeris_basic'] = {
- init: function () {
- this.appendDummyInput()
- .appendField(new Blockly.FieldDropdown([['isWeekend', 'isWeekend'], ['getBankHolidayName', 'getBankHolidayName'], ['getNextBankHoliday', 'getNextBankHoliday'], ['isBankHoliday', 'isBankHoliday']]), 'type')
- this.setOutput(true, null)
- this.setColour(0)
- this.setTooltip('provides calendar related information')
- this.setHelpUrl('https://www.openhab.org/docs/configuration/actions.html#ephemeris')
- }
- }
-
- Blockly.JavaScript['oh_ephemeris_basic'] = function (block) {
- addEphemeris()
- let type = block.getFieldValue('type')
- let code = `ephemeris.${type}()`
- return [code, Blockly.JavaScript.ORDER_NONE]
- }
-
- Blockly.Blocks['oh_ephemeris_offset'] = {
- init: function () {
- this.appendValueInput('offset')
- .setCheck('Number')
- .appendField(new Blockly.FieldDropdown([['isWeekend', 'isWeekend'], ['getBankHolidayName', 'getBankHolidayName'], ['getNextBankHoliday', 'getNextBankHoliday'], ['isBankHoliday', 'isBankHoliday']]), 'type')
- .appendField('offset days')
- this.setOutput(true, null)
- this.setColour(0)
- this.setTooltip('the offset to the given type in days')
- this.setHelpUrl('https://www.openhab.org/docs/configuration/actions.html#ephemeris')
- }
- }
-
- Blockly.JavaScript['oh_ephemeris_offset'] = function (block) {
- addEphemeris()
- let type = block.getFieldValue('type')
- let offsetValue = Blockly.JavaScript.valueToCode(block, 'offset', Blockly.JavaScript.ORDER_ATOMIC)
- let code = `ephemeris.${type}(${offsetValue})`
- return [code, Blockly.JavaScript.ORDER_NONE]
- }
-
- Blockly.Blocks['oh_ephemeris_getBankHolidayName'] = {
- init: function () {
- this.appendValueInput('offsetDays')
- .appendField('getBankHolidayName')
- this.setColour(0)
- this.setInputsInline(true)
- this.setTooltip('name of the holiday today, or null if today is not a bank holiday')
- this.setOutput(true, null)
- this.setHelpUrl('https://www.openhab.org/docs/configuration/actions.html#ephemeris')
- }
- }
-
- Blockly.JavaScript['oh_ephemeris_getBankHolidayName'] = function (block) {
- addEphemeris()
- let code = 'ephemeris.getBankHolidayName'
- return [code, 0]
- }
-
- Blockly.Blocks['oh_Ephemeris_getNextBankHoliday'] = {
- init: function () {
- this.appendValueInput('offsetDays')
- .appendField('getNextBankHoliday')
- this.setColour(0)
- this.setInputsInline(true)
- this.setTooltip('name of the next bank holiday')
- this.setOutput(true, null)
- this.setHelpUrl('https://www.openhab.org/docs/configuration/actions.html#ephemeris')
- }
- }
-
- Blockly.JavaScript['oh_Ephemeris_getNextBankHoliday'] = function (block) {
- addEphemeris()
- let code = 'ephemeris.getNextBankHoliday'
- return [code, 0]
- }
-
- Blockly.Blocks['oh_Ephemeris_isBankHoliday'] = {
- init: function () {
- this.appendValueInput('offsetDays')
- .appendField('isBankHoliday')
- this.setColour(0)
- this.setInputsInline(true)
- this.setTooltip('true if today is a bank holiday, false otherwise')
- this.setOutput(true, null)
- this.setHelpUrl('https://www.openhab.org/docs/configuration/actions.html#ephemeris')
- }
- }
-
- Blockly.JavaScript['oh_Ephemeris_isBankHoliday'] = function (block) {
- addEphemeris()
- let code = 'ephemeris.isBankHoliday'
- return [code, 0]
- }
-
- Blockly.Blocks['oh_Ephemeris_isWeekend'] = {
- init: function () {
- this.appendValueInput('offsetDays')
- .appendField('isWeekend')
- this.setColour(0)
- this.setInputsInline(true)
- this.setTooltip('true if today is a weekend, false otherwise')
- this.setOutput(true, null)
- this.setHelpUrl('https://www.openhab.org/docs/configuration/actions.html#ephemeris')
- }
- }
-
- Blockly.JavaScript['oh_Ephemeris_isWeekend'] = function (block) {
- addEphemeris()
- let code = 'ephemeris.isWeekend'
- return [code, 0]
- }
-
- function addEphemeris () {
- Blockly.JavaScript.provideFunction_(
- 'ephemeris',
- ['var ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + ' = Java.type(\'org.openhab.core.model.script.actions.Ephemeris\')'])
- }
-}
diff --git a/bundles/org.openhab.ui/web/src/assets/definitions/blockly/blocks-ephemeris.js b/bundles/org.openhab.ui/web/src/assets/definitions/blockly/blocks-ephemeris.js
new file mode 100644
index 0000000000..3c40525664
--- /dev/null
+++ b/bundles/org.openhab.ui/web/src/assets/definitions/blockly/blocks-ephemeris.js
@@ -0,0 +1,289 @@
+/*
+* Ephemeris provides calendar related information
+* @author stefan.hoehn
+*
+* See more background info on openHAB ephemeris here: https://www.openhab.org/docs/configuration/actions.html#ephemeris
+* See usage discussion here: https://community.openhab.org/t/wip-ephemeris-documentation/84536
+*/
+import Blockly from 'blockly'
+import { FieldDatePicker } from './fields/date-field'
+
+export default function (f7) {
+ /*
+ * Typed (EphemerisDay) block that can be used with the Ephemeris check block
+ * Note that the block basically returns a zero day offset for the check
+ * Blockly part
+ */
+ Blockly.Blocks['oh_ephemeris_today'] = {
+ init: function () {
+ this.appendDummyInput()
+ .appendField('today')
+ this.setOutput(true, 'EphemerisDay')
+ this.setColour(70)
+ this.setTooltip('today\'s date for ephemeris check block')
+ this.setHelpUrl('https://www.openhab.org/docs/configuration/actions.html#ephemeris')
+ }
+ }
+
+ /*
+ * Typed block that can be used with Ephemeris check block
+ * Code part
+ */
+ Blockly.JavaScript['oh_ephemeris_today'] = function (block) {
+ addEphemeris()
+ let code = '0'
+ return [code, Blockly.JavaScript.ORDER_NONE]
+ }
+
+ /*
+ * Typed (EphemerisDay) block with a day positve or negative offset that can be used with the Ephemeris check block
+ * Blockly part
+ */
+ Blockly.Blocks['oh_ephemeris_today_offset'] = {
+ init: function () {
+ this.appendValueInput('offset')
+ .setCheck('Number')
+ .appendField('today +/-')
+ this.appendDummyInput()
+ .appendField('days')
+ this.setOutput(true, 'EphemerisDay')
+ this.setColour(70)
+ this.setTooltip('today with a positive or negative day offset for ephemeris check block')
+ this.setHelpUrl('https://www.openhab.org/docs/configuration/actions.html#ephemeris')
+ }
+ }
+
+ /*
+ * Typed (EphemerisDay) block with a day positve or negative offset that can be used with the Ephemeris check block
+ * Code part
+ */
+ Blockly.JavaScript['oh_ephemeris_today_offset'] = function (block) {
+ let offsetValue = Blockly.JavaScript.valueToCode(block, 'offset', Blockly.JavaScript.ORDER_ATOMIC)
+ let code = `${offsetValue}`
+ return [code, Blockly.JavaScript.ORDER_NONE]
+ }
+
+ /*
+ * Typed (EphemerisDate) block that can be used with the Ephemeris check block
+ * Allows the selection of a date. The default is the current date
+ * Blockly part
+ */
+ Blockly.Blocks['oh_ephemeris_date'] = {
+ init: function () {
+ this.appendDummyInput()
+ .appendField('date')
+ .appendField(new FieldDatePicker('', null, { f7 }, 'date'), 'day')
+ this.setOutput(true, 'EphemerisDate')
+ this.setColour(70)
+ this.setTooltip('Calender entry for ephemeris check block or other openHAB Blocks that require a day input')
+ this.setHelpUrl('https://www.openhab.org/docs/configuration/actions.html#ephemeris')
+ }
+ }
+
+ /*
+ * Typed (EphemerisDate) block that can be used with the Ephemeris check block
+ * Code part
+ */
+ Blockly.JavaScript['oh_ephemeris_date'] = function (block) {
+ const { dtf, zdt, getZonedDateTime } = addDateSupport()
+ let day = block.getFieldValue('day')
+ let code = `${getZonedDateTime}('${day}')`
+ return [code, Blockly.JavaScript.ORDER_NONE]
+ }
+
+ /*
+ * Typed (EphemerisDate) block that can be used with the Ephemeris check block
+ * Allows input as string in the format yyyy-MM-dd
+ * Blockly part
+ */
+ Blockly.Blocks['oh_ephemeris_date_text'] = {
+ init: function () {
+ this.appendValueInput('day')
+ .appendField('date')
+ this.setOutput(true, 'EphemerisDate')
+ this.setColour(70)
+ this.setTooltip('Calender entry as yyyy-MM-dd for ephemeris check block or other openHAB Blocks that require a day input')
+ this.setHelpUrl('https://www.openhab.org/docs/configuration/actions.html#ephemeris')
+ }
+ }
+
+ /*
+ * Typed (EphemerisDate) block that can be used with the Ephemeris check block
+ * Code part
+ */
+ Blockly.JavaScript['oh_ephemeris_date_text'] = function (block) {
+ const { dtf, zdt, getZonedDateTime } = addDateSupport()
+ let day = Blockly.JavaScript.valueToCode(block, 'day', Blockly.JavaScript.ORDER_ATOMIC)
+ let code = `${getZonedDateTime}(${day})`
+ return [code, Blockly.JavaScript.ORDER_NONE]
+ }
+
+ /*
+ * Returns a string representation of an ephemeris date
+ * Blockly part
+ */
+ Blockly.Blocks['oh_ephemeris_text_of_date'] = {
+ init: function () {
+ this.appendValueInput('date')
+ .appendField('text of')
+ .setCheck('EphemerisDate')
+ this.appendDummyInput()
+ .appendField(new Blockly.FieldDropdown([['without time', 'without'], ['with time', 'with']]), 'withtime')
+
+ this.setOutput(true, 'String')
+ this.setColour(160)
+ this.setTooltip('converts an ephemeris date into a date string')
+ this.setHelpUrl('https://www.openhab.org/docs/configuration/actions.html#ephemeris')
+ }
+ }
+
+ /*
+ * Returns a string representation of an ephemeris date
+ * Code part
+ */
+ Blockly.JavaScript['oh_ephemeris_text_of_date'] = function (block) {
+ const { dtf, zdt, getZonedDatetime } = addDateSupport()
+ let date = Blockly.JavaScript.valueToCode(block, 'date', Blockly.JavaScript.ORDER_ATOMIC)
+ let withtime = block.getFieldValue('withtime')
+ let pattern = 'yyyy-MM-dd'
+ if (withtime === 'with') {
+ pattern = 'yyyy-MM-dd HH:mm:ss'
+ }
+ let code = `${date}.format(${dtf}.ofPattern('${pattern}'))`
+ return [code, Blockly.JavaScript.ORDER_NONE]
+ }
+
+ /*
+ * Checks if the provided day is a
+ * - bank holiday (needs to be configured in openHAB
+ * - weekend
+ * - weekday
+ * Only EphemerisDay and EphemerisDate blocks are allowed as an input
+ * Blockly part
+ */
+ Blockly.Blocks['oh_ephemeris_check'] = {
+ init: function () {
+ this.appendValueInput('dayInfo')
+ .setCheck(['EphemerisDay', 'EphemerisDate'])
+ this.appendDummyInput()
+ .appendField('is')
+ .appendField(new Blockly.FieldDropdown([['a holiday', 'holiday'], ['the weekend', 'weekend'], ['a weekday', 'weekday']]), 'checkType')
+ this.setColour(0)
+ this.setInputsInline(true)
+ this.setTooltip('checks if the given day is a holiday, weekend or weekday')
+ this.setOutput(true, null)
+ this.setHelpUrl('https://www.openhab.org/docs/configuration/actions.html#ephemeris')
+ }
+ }
+
+ /*
+ * Checks if the provided day is a bank holiday, weekend or weekday
+ * Code part
+ */
+ Blockly.JavaScript['oh_ephemeris_check'] = function (block) {
+ const ephemeris = addEphemeris()
+
+ let dayInfo = Blockly.JavaScript.valueToCode(block, 'dayInfo', Blockly.JavaScript.ORDER_NONE)
+ let checkType = block.getFieldValue('checkType')
+ let code = ''
+
+ switch (checkType) {
+ case 'weekend':
+ code += `${ephemeris}.isWeekend(${dayInfo})`
+ break
+ case 'weekday':
+ code += `!${ephemeris}.isWeekend(${dayInfo})`
+ break
+ case 'holiday':
+ code += `${ephemeris}.isBankHoliday(${dayInfo})`
+ break
+ }
+ return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]
+ }
+
+ /*
+ * Retrieve the current bonk holiday name
+ * Only EphemerisDay and EphemerisDate blocks are allowed as an input
+ * Blockly part
+ */
+ Blockly.Blocks['oh_ephemeris_getHolidayName'] = {
+ init: function () {
+ this.appendValueInput('dayInfo')
+ .appendField('holiday name for')
+ .setCheck(['EphemerisDay', 'EphemerisDate'])
+ this.setColour(0)
+ this.setInputsInline(true)
+ this.setTooltip('name of the holiday for the given day')
+ this.setOutput(true, null)
+ this.setHelpUrl('https://www.openhab.org/docs/configuration/actions.html#ephemeris')
+ }
+ }
+
+ /*
+ * Retrieve the current bonk holiday name
+ * Code part
+ */
+ Blockly.JavaScript['oh_ephemeris_getHolidayName'] = function (block) {
+ const ephemeris = addEphemeris()
+ let dayInfo = Blockly.JavaScript.valueToCode(block, 'dayInfo', Blockly.JavaScript.ORDER_NONE)
+ let code = `${ephemeris}.getBankHolidayName(${dayInfo})`
+ return [code, Blockly.JavaScript.ORDER_NONE]
+ }
+
+ /*
+ * Retrieve the number of days from today until the given bank holiday name
+ * Blockly part
+ */
+ Blockly.Blocks['oh_ephemeris_getDaysUntilHoliday'] = {
+ init: function () {
+ this.appendValueInput('holidayName')
+ .appendField('days until holiday named')
+ .setCheck('String')
+ this.setColour(0)
+ this.setInputsInline(true)
+ this.setTooltip('days from today until the given bank holiday name')
+ this.setOutput(true, null)
+ this.setHelpUrl('https://www.openhab.org/docs/configuration/actions.html#ephemeris')
+ }
+ }
+
+ /*
+ * Retrieve the number of days from today until the given bank holiday name
+ * Code part
+ */
+ Blockly.JavaScript['oh_ephemeris_getDaysUntilHoliday'] = function (block) {
+ const ephemeris = addEphemeris()
+ let holidayName = Blockly.JavaScript.valueToCode(block, 'holidayName', Blockly.JavaScript.ORDER_NONE)
+ let code = `${ephemeris}.getDaysUntil(${holidayName})`
+ return [code, Blockly.JavaScript.ORDER_NONE]
+ }
+
+ /*
+ * Add ephemeris support to rule
+ */
+ function addEphemeris () {
+ return Blockly.JavaScript.provideFunction_(
+ 'ephemeris',
+ ['var ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + ' = Java.type("org.openhab.core.model.script.actions.Ephemeris");'])
+ }
+
+ /*
+ * Add ZoneDateTime and DateTimeFormatter support to rule
+ */
+ function addDateSupport () {
+ const dtf = Blockly.JavaScript.provideFunction_(
+ 'dtf',
+ ['var ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + ' = Java.type("java.time.format.DateTimeFormatter");'])
+ const zdt = Blockly.JavaScript.provideFunction_(
+ 'zdt',
+ ['var ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + ' = Java.type("java.time.ZonedDateTime");'])
+ const getZonedDateTime = Blockly.JavaScript.provideFunction_(
+ 'getZonedDateTime',
+ [
+ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + ' (datetime) {',
+ ` return ${zdt}.parse(datetime + ' 00:00:00 +00:00', dtf.ofPattern('yyyy-MM-dd HH:mm:ss z'))`,
+ '}'
+ ])
+ return { dtf, zdt, getZonedDateTime }
+ }
+}
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
new file mode 100644
index 0000000000..4880bde1f4
--- /dev/null
+++ b/bundles/org.openhab.ui/web/src/assets/definitions/blockly/blocks-persistence.js
@@ -0,0 +1,168 @@
+/*
+* These blocks support the persistence module which stores the data in the database and allows to retrieve historical and statistical data
+*/
+import Blockly from 'blockly'
+
+export default function defineOHBlocks_Persistence (f7) {
+ /*
+ * Checks if an item has changed or has been updated since some given date
+ * Blockly part
+ */
+ Blockly.Blocks['oh_persist_changed'] = {
+ init: function () {
+ this.appendValueInput('itemName')
+ this.appendDummyInput()
+ .appendField(new Blockly.FieldDropdown([['has changed since', 'changedSince'], ['has been updated since', 'updatedSince']]), 'methodName')
+ this.appendValueInput('dayInfo')
+ .setCheck(['EphemerisDay', 'EphemerisDate'])
+ this.setInputsInline(true)
+ this.setOutput(true, null)
+ this.setColour(0)
+
+ let thisBlock = this
+ this.setTooltip(function () {
+ let methodName = thisBlock.getFieldValue('methodName')
+ let TIP = {
+ 'changedSince': 'Checks if the State of the Item has (ever) changed since a certain point in time',
+ 'updatedSince': 'Checks if the State of the Item has been updated since a certain point in time'
+ }
+ return TIP[methodName]
+ })
+
+ this.setHelpUrl('https://www.openhab.org/docs/configuration/persistence.html')
+ }
+ }
+
+ /*
+ * Checks if an item has changed or has been updated since some given date
+ * Code part
+ */
+ Blockly.JavaScript['oh_persist_changed'] = function (block) {
+ addPersistence()
+ addDateSupport()
+ const itemName = Blockly.JavaScript.valueToCode(block, 'itemName', Blockly.JavaScript.ORDER_ATOMIC)
+ const methodName = block.getFieldValue('methodName')
+ const dayInfo = Blockly.JavaScript.valueToCode(block, 'dayInfo', Blockly.JavaScript.ORDER_ATOMIC)
+ let code = `persistence.${methodName}(itemRegistry.getItem(${itemName}),${dayInfo})`
+ return [code, Blockly.JavaScript.ORDER_NONE]
+ }
+
+ /*
+ * Provides a number of different (non-)statistical metrics for an item according to the given date
+ * Blockly part
+ */
+ Blockly.Blocks['oh_get_persistvalue'] = {
+ init: function () {
+ this.appendValueInput('itemName')
+ .appendField('get')
+ .appendField(new Blockly.FieldDropdown([
+ ['average value', 'averageSince'], ['delta value', 'deltaSince'],
+ ['deviation value', 'deviationSince'], ['variance value', 'varianceSince'], ['evolution rate value', 'evolutionRate'],
+ ['minimum value', 'minimumSince'], ['maximum value', 'maximumSince'],
+ ['summed up value', 'sumSince'], ['previous value', 'previousState']
+ ]
+ ), 'methodName')
+ .appendField('of item')
+ .setCheck('oh_itemtype')
+ this.appendValueInput('dayInfo')
+ .appendField('since')
+ .setCheck(['EphemerisDay', 'EphemerisDate'])
+ this.setInputsInline(true)
+ this.setOutput(true, null)
+ this.setColour(0)
+ let thisBlock = this
+ this.setTooltip(function () {
+ let methodName = thisBlock.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',
+ 'deltaSince': 'Gets the difference in value of the State of a given Item since a certain point in time',
+ 'deviationSince': 'Gets the standard deviation of the state of the given Item since a certain point in time',
+ 'varianceSince': 'Gets the variance of the state of the given Item since a certain point in time',
+ 'evolutionRate': 'Gets the evolution rate of the state of a given Item since a certain point in time',
+ 'minimumSince': 'Gets the minimum value of the State of a persisted Item since a certain point in time',
+ 'maximumSince': 'Gets the maximum value of the State of a persisted Item since a certain point in time',
+ 'sumSince': 'Gets the sum of the previous States of a persisted Item since a certain point in time',
+ 'previousState': 'Gets the previous value of the State of a persisted Item'
+ }
+ return TIP[methodName]
+ })
+ this.setHelpUrl('https://www.openhab.org/docs/configuration/persistence.html')
+ }
+ }
+
+ /*
+ * Provides a number of different (non-)statistical metrics for an item according to the given date
+ * Code part
+ */
+ Blockly.JavaScript['oh_get_persistvalue'] = function (block) {
+ addPersistence()
+ addDateSupport()
+ const itemName = Blockly.JavaScript.valueToCode(block, 'itemName', Blockly.JavaScript.ORDER_ATOMIC)
+ const methodName = block.getFieldValue('methodName')
+ const dayInfo = Blockly.JavaScript.valueToCode(block, 'dayInfo', Blockly.JavaScript.ORDER_ATOMIC)
+ let code = ''
+ if (methodName === 'maximumSince' || methodName === 'minimumSince') {
+ code = `persistence.${methodName}(itemRegistry.getItem(${itemName}),${dayInfo}).getState()`
+ } else if (methodName === 'previousState') {
+ code = `persistence.${methodName}(itemRegistry.getItem(${itemName}))`
+ } else {
+ code = `persistence.${methodName}(itemRegistry.getItem(${itemName}),${dayInfo})`
+ }
+
+ return [code, Blockly.JavaScript.ORDER_NONE]
+ }
+
+ /*
+ * Returns the state before the current state of that item
+ * Blockly part
+ */
+ Blockly.Blocks['oh_get_persistence_lastupdate'] = {
+ init: function () {
+ this.appendDummyInput()
+ .appendField('last updated date of')
+ this.appendValueInput('itemName')
+ this.setInputsInline(true)
+ this.setOutput(true, 'EphemerisDate')
+ this.setColour(0)
+
+ let thisBlock = this
+ this.setTooltip(function () {
+ let methodName = thisBlock.getFieldValue('methodName')
+ let TIP = {
+ 'changedSince': 'Checks if the State of the Item has (ever) changed since a certain point in time',
+ 'updatedSince': 'Checks if the State of the Item has been updated since a certain point in time'
+ }
+ return TIP[methodName]
+ })
+
+ this.setHelpUrl('https://www.openhab.org/docs/configuration/persistence.html')
+ }
+ }
+
+ /*
+ * Returns the state before the current state of that item
+ * Code part
+ */
+ Blockly.JavaScript['oh_get_persistence_lastupdate'] = function (block) {
+ addPersistence()
+ addDateSupport()
+ const itemName = Blockly.JavaScript.valueToCode(block, 'itemName', Blockly.JavaScript.ORDER_ATOMIC)
+ let code = `persistence.lastUpdate(itemRegistry.getItem(${itemName}))`
+ return [code, 0]
+ }
+
+ function addPersistence () {
+ Blockly.JavaScript.provideFunction_(
+ 'persistence',
+ ['var ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + ' = Java.type(\'org.openhab.core.persistence.extensions.PersistenceExtensions\');'])
+ }
+
+ function addDateSupport () {
+ Blockly.JavaScript.provideFunction_(
+ 'dtf',
+ ['var ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + ' = Java.type("java.time.format.DateTimeFormatter");'])
+ Blockly.JavaScript.provideFunction_(
+ 'zdt',
+ ['var ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + ' = Java.type("java.time.ZonedDateTime");'])
+ }
+}
diff --git a/bundles/org.openhab.ui/web/src/assets/definitions/blockly/blocks-scripts.js b/bundles/org.openhab.ui/web/src/assets/definitions/blockly/blocks-scripts.js
new file mode 100644
index 0000000000..1f370309ab
--- /dev/null
+++ b/bundles/org.openhab.ui/web/src/assets/definitions/blockly/blocks-scripts.js
@@ -0,0 +1,161 @@
+/*
+* These blocks allow to run scripts from the current rule. There are two types of scripts which are supported by different blocks
+* - Script Files that are stored on openHAB's server in the scripts folder
+* - Scripts that have been provided via the openHAB UI
+* Additionally there is a block that allows transformations based on the Map-File functionality, regular-expressions and applying JSON-paths
+*/
+import Blockly from 'blockly'
+import { addOSGiService } from './utils'
+
+export default function defineOHBlocks_Scripts (f7, scripts) {
+ /*
+ * Calls a script that is provided in openHABs scripts folder
+ * Blockly part
+ */
+ Blockly.Blocks['oh_callscriptfile'] = {
+ init: function () {
+ this.appendValueInput('scriptfile')
+ .setCheck('String')
+ .appendField('call script file')
+ this.setInputsInline(true)
+ this.setPreviousStatement(true, null)
+ this.setNextStatement(true, null)
+ this.setColour(0)
+ this.setTooltip('Calls a script file which must be located in the $OPENHAB_CONF/scripts folder')
+ this.setHelpUrl('https://www.openhab.org/docs/configuration/actions.html')
+ }
+ }
+
+ /*
+ * Calls a script that is provided in openHABs scripts folder
+ * Code part
+ */
+ Blockly.JavaScript['oh_callscriptfile'] = function (block) {
+ const scriptExecution = Blockly.JavaScript.provideFunction_(
+ 'scriptExecution',
+ ['var ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + ' = Java.type(\'org.openhab.core.model.script.actions.ScriptExecution\');'])
+ let scriptfile = Blockly.JavaScript.valueToCode(block, 'scriptfile', Blockly.JavaScript.ORDER_ATOMIC)
+ let code = `${scriptExecution}.callScript(${scriptfile});\n`
+ return code
+ }
+
+ /*
+ * Calls a script that has been provided via the UI.
+ * Parameters can be provided with the special parameter block oh_scriptparam
+ * Blockly part
+ */
+ Blockly.Blocks['oh_runrule'] = {
+ init: function () {
+ this.appendValueInput('ruleUID')
+ .setCheck('String')
+ .appendField('run rule or script')
+ this.appendValueInput('parameters')
+ .appendField('with parameters')
+ .setCheck('Dictionary')
+ this.setInputsInline(false)
+ this.setPreviousStatement(true, null)
+ this.setNextStatement(true, null)
+ this.setColour(0)
+ this.setTooltip('Run a rule or script with a certain UID, and optional parameters')
+ // this.setHelpUrl('') // TODO provide a openhab documentation URL
+ }
+ }
+
+ /*
+ * Calls a script that has been provided via the UI.
+ * Parameters can be provided with the special parameter block oh_scriptparam
+ * Code part
+ */
+ Blockly.JavaScript['oh_runrule'] = function (block) {
+ const ruleManager = addOSGiService('ruleManager', 'org.openhab.core.automation.RuleManager')
+ const ruleUID = Blockly.JavaScript.valueToCode(block, 'ruleUID', Blockly.JavaScript.ORDER_ATOMIC)
+ const scriptParameters = Blockly.JavaScript.valueToCode(block, 'parameters', Blockly.JavaScript.ORDER_ATOMIC)
+
+ // create a function for the generated code that maps json key-values into a map structure
+ const convertDictionaryToHashMap = Blockly.JavaScript.provideFunction_(
+ 'convertDictionaryToHashMap',
+ [
+ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + ' (dict) {',
+ ' if (!dict || dict.length === 0) return null;',
+ ' var map = new java.util.HashMap();',
+ ' Object.keys(dict).forEach(function (key) {',
+ ' map.put(key, dict[key]);',
+ ' });',
+ ' return map;',
+ '}'
+ ])
+
+ let code = `${ruleManager}.runNow(${ruleUID}, true, ${convertDictionaryToHashMap}(${scriptParameters}));\n`
+ return code
+ }
+
+ /*
+ * Allow transformations via different methods
+ * inputs
+ * - value to be transformed
+ * - method Map, Regular Expression, JSON-Path
+ * - transformation method input
+ * - Map: map file found in openHABs transform-folder
+ * - Regex: regex-expression
+ * - JSON-Path: JSON-Path
+ * Blockly part
+ */
+ Blockly.Blocks['oh_transformation'] = {
+ init: function () {
+ this.appendValueInput('value')
+ .setAlign(Blockly.ALIGN_RIGHT)
+ .appendField('transform')
+ this.appendValueInput('function')
+ .appendField('apply')
+ .appendField(new Blockly.FieldTextInput('MAP'), 'type')
+ // .appendField(new Blockly.FieldDropdown([['MAP', 'MAP'], ['REGEX', 'REGEX'], ['JSONPATH', 'JSONPATH']]), 'type')
+ .appendField('with')
+
+ this.setInputsInline(false)
+ this.setOutput(true, null)
+ this.setColour(0)
+
+ let thisBlock = this
+ this.setTooltip(function () {
+ const type = thisBlock.getFieldValue('type')
+ switch (type) {
+ case 'MAP':
+ return 'transforms an input via a map file. Specify the file as the function'
+ case 'REGEX':
+ return 'transforms / filters an input by applying the provided regular expression'
+ case 'JSONPATH':
+ return 'transforms / filters an JSON input by executing the provided JSONPath query'
+ default:
+ return 'transforms the input with the ' + type + ' transformation'
+ }
+ })
+ this.setHelpUrl(function () {
+ const type = thisBlock.getFieldValue('type')
+ return 'https://www.openhab.org/addons/transformations/' + type.toLowerCase() + '/'
+ })
+ }
+ }
+
+ /*
+ * Allow transformations via different methods
+ * Code part
+ */
+ Blockly.JavaScript['oh_transformation'] = function (block) {
+ const transformation = addTransformation()
+ const transformationType = block.getFieldValue('type')
+ const transformationFunction = Blockly.JavaScript.valueToCode(block, 'function', Blockly.JavaScript.ORDER_ATOMIC)
+ const transformationValue = Blockly.JavaScript.valueToCode(block, 'value', Blockly.JavaScript.ORDER_ATOMIC)
+
+ let code = `${transformation}.transform('${transformationType}', ${transformationFunction}, ${transformationValue})`
+ return [code, 0]
+ }
+
+ /*
+ * add transformation class to rule
+ */
+ function addTransformation () {
+ return Blockly.JavaScript.provideFunction_(
+ 'transformation',
+ ['var ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + ' = Java.type(\'org.openhab.core.transform.actions.Transformation\');'])
+ }
+}
diff --git a/bundles/org.openhab.ui/web/src/assets/definitions/blockly/fields/date-field.js b/bundles/org.openhab.ui/web/src/assets/definitions/blockly/fields/date-field.js
new file mode 100644
index 0000000000..3592f471ad
--- /dev/null
+++ b/bundles/org.openhab.ui/web/src/assets/definitions/blockly/fields/date-field.js
@@ -0,0 +1,57 @@
+/*
+ * Field allowing to pick a date from a calendar
+ */
+
+import Blockly from 'blockly'
+import dayjs from 'dayjs'
+
+export class FieldDatePicker extends Blockly.FieldTextInput {
+ constructor (optValue, optValidator, optConfig) {
+ let value = optValue
+ if (value === '') {
+ value = dayjs().format('YYYY-MM-DD')
+ }
+ super(value, optValidator, optConfig)
+ if (optConfig.f7) this.f7 = optConfig.f7
+ }
+
+ static fromJson (options) {
+ return new FieldDatePicker(options['options'], undefined, options)
+ }
+
+ showEditor_ (options) {
+ if (this.f7) {
+ let inputEl = document.createElement('input')
+ inputEl.setAttribute('type', 'text')
+ inputEl.setAttribute('value', this.value_)
+ options.target.appendChild(inputEl)
+ const self = this
+ if (this.calendarPicker_) {
+ this.calendarPicker_.setValue([this.value_])
+ } else {
+ this.calendarPicker_ = this.f7.calendar.create({
+ inputEl: options.target,
+ openIn: 'popup',
+ closeOnSelect: true,
+ value: (this.value_) ? [this.value_] : undefined,
+ on: {
+ change (calendar, value) {
+ if (value.length < 1) return
+ if (!value[0].toISOString) return
+ self.value_ = dayjs(value[0]).format('YYYY-MM-DD')
+ self.setEditorValue_(self.value)
+ }
+ }
+ })
+ }
+ this.calendarPicker_.open()
+ }
+ }
+
+ dispose () {
+ if (this.calendarPicker_) this.calendarPicker_.destroy()
+ super.dispose()
+ }
+}
+
+Blockly.fieldRegistry.register('oh_date_field', FieldDatePicker)
diff --git a/bundles/org.openhab.ui/web/src/assets/definitions/blockly/index.js b/bundles/org.openhab.ui/web/src/assets/definitions/blockly/index.js
index 6e1b698cd2..bff304438a 100644
--- a/bundles/org.openhab.ui/web/src/assets/definitions/blockly/index.js
+++ b/bundles/org.openhab.ui/web/src/assets/definitions/blockly/index.js
@@ -7,6 +7,9 @@ import defineLoggingBlocks from './blocks-logging'
import defineNotificationBlocks from './blocks-notifications'
import defineTimerBlocks from './blocks-timers'
import defineValueStorageBlocks from './blocks-valuestorage'
+import defineEphemerisBlocks from './blocks-ephemeris'
+import defineOHBlocksScripts from './blocks-scripts'
+import defineOHBlocksPersistence from './blocks-persistence'
export default function (f7, data) {
defineDictionaryBlocks(f7)
@@ -18,4 +21,7 @@ export default function (f7, data) {
defineLoggingBlocks(f7)
defineTimerBlocks(f7)
defineValueStorageBlocks(f7)
+ defineEphemerisBlocks(f7)
+ defineOHBlocksScripts(f7)
+ defineOHBlocksPersistence(f7)
}
diff --git a/bundles/org.openhab.ui/web/src/assets/definitions/blockly/utils.js b/bundles/org.openhab.ui/web/src/assets/definitions/blockly/utils.js
new file mode 100644
index 0000000000..e5b331426a
--- /dev/null
+++ b/bundles/org.openhab.ui/web/src/assets/definitions/blockly/utils.js
@@ -0,0 +1,19 @@
+import Blockly from 'blockly'
+
+/*
+* function that allow to call classes within the osgi container
+* e.g. service -> 'ruleManager', class -> 'org.openhab.core.automation.RuleManager'
+*/
+export function addOSGiService (serviceName, serviceClass) {
+ const addServiceName = Blockly.JavaScript.provideFunction_(
+ 'addFrameworkService', [
+ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + ' (serviceClass) {',
+ ' var bundleContext = Java.type(\'org.osgi.framework.FrameworkUtil\').getBundle(scriptExtension.class).getBundleContext();',
+ ' var serviceReference = bundleContext.getServiceReference(serviceClass);',
+ ' return bundleContext.getService(serviceReference);',
+ '}'
+ ])
+ return Blockly.JavaScript.provideFunction_(
+ serviceName,
+ [`var ${Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_} = ${addServiceName}('${serviceClass}');`])
+}
diff --git a/bundles/org.openhab.ui/web/src/pages/settings/rules/script/blockly-editor.vue b/bundles/org.openhab.ui/web/src/pages/settings/rules/script/blockly-editor.vue
index 29b7b6effc..f6b8006c55 100644
--- a/bundles/org.openhab.ui/web/src/pages/settings/rules/script/blockly-editor.vue
+++ b/bundles/org.openhab.ui/web/src/pages/settings/rules/script/blockly-editor.vue
@@ -476,6 +476,50 @@
+
+
+
+
+
+
+ 0
+
+
+
+
+
+
+
+
+ {{ new Date().toISOString().split('T')[0] }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ CHRISTMAS
+
+
+
+
+
@@ -518,6 +562,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -540,6 +602,41 @@
+
+
+
+
+ scriptname.script
+
+
+
+
+
+
+ ruleUID
+
+
+
+
+
+
+
+
+
+
+
+
+ function
+
+
+
+
+ value
+
+
+
+
+