Warning! This topic is for development testing only. Do not install, it will probably cause issues with your system.
Version: 0.0.1
DependsOn: ui-basic
Keywords: test, debug
Countries: ; NO, SE,
License: GPL
Connection: local
LoggerPackages: com.example.test
Documentation: https://github.com/Nadahar/Mill-LAN-openHAB-Binding/blob/master/bundles/org.openhab.binding.milllan/README.md
Issues: https://github.com/Nadahar/Mill-LAN-openHAB-Binding/issues
Version: 0.0.2
Keywords: test, debug, testing
Countries: DE
CoreRange: [3.1.0:5)
Maturity: immature
Documentation: https://github.com/Nadahar/Mill-LAN-openHAB-Binding/blob/master/README.md
Description: An even more unstable version
LoggerPackages: com.example.test2
URL: https://github.com/Nadahar/Mill-LAN-openHAB-Binding/releases/download/v1.0.0/org.openhab.binding.milllan-1.0.0.jar
[
{
"actions": [
{
"configuration": {
"script": "// Version 1.0\nvar {TimerMgr, helpers} = require('openhab_rules_tools');\nconsole.loggerName = 'org.openhab.automation.rules_tools.TimeStateMachine';\n//osgi.getService('org.apache.karaf.log.core.LogService').setLevel(console.loggerName, 'DEBUG');\n\nhelpers.validateLibraries('4.2.0', '2.0.3');\n\nconsole.debug('Starting state machine in ten seconds...');\n\n// Properties\nvar STATE_ITEM = \"{{timeOfDay}}\";\nvar DT_GROUP = \"{{timesOfDayGrp}}\";\nvar DAY_TYPES = ['custom', 'holiday', 'dayset', 'weekend', 'weekday', 'default'];\nvar NAMESPACE = '{{namespace}}';\nvar USAGE = 'Time Based State Machine Usage:\\n'\n + 'All date times must be a member of ' + DT_GROUP + '.\\n'\n + 'Each member of the Group must have ' + NAMESPACE + ' Item metadata of the following format:\\n'\n + ' .items file: ' + NAMESPACE +'=\"STATE\"[type=\"daytype\", set=\"dayset\", file=\"uri\"]\\n'\n + \" UI YAML: use '\" + NAMESPACE + \"' for the namespace and metadata format:\\n\"\n + ' value: STATE\\n'\n + ' config:\\n'\n + ' type: daytype\\n'\n + ' set: dayset\\n'\n + ' file: uri\\n'\n + 'Where \"STATE\" is the state machine state that begins at the time stored in that Item, '\n + '\"daytype\" is one of \"default\", \"weekday\", \"weekend\", \"dayset\", \"holiday\", or \"custom\". '\n + 'If \"dayset\" is chosen for the type, the \"set\" property is required indicating the name of the '\n + 'custom dayset configured in Ephemeris. If \"custom\" is chosen as the type, the \"file\" property '\n + 'is required and should be the fully qualified path the the Ephemeris XML file with the custom '\n + 'holidays defined. The \"set\" and \"file\" properties are invalid when choosing any of the other '\n + '\"types\".';\n\n/**\n * Validates the passed in Item has valid NAMESPACE metadata.\n *\n * @param {string} itemName name of the Item to check\n * @throws exception if the metadata doesn't exist or is invalid\n */\nvar validateItemConfig = (itemName) => {\n const md = items[itemName].getMetadata()[NAMESPACE];\n\n if(md.value === undefined || md.value === null || md.value === '') {\n throw itemName + ' has malformed ' + NAMESPACE + ' metadata, no value found!';\n }\n\n const dayType = md.configuration['type'];\n if(!dayType) {\n throw itemName + ' has malformed ' + NAMESPACE + ' metadata, required \"type\" property is not found!';\n }\n\n if(dayType == 'dayset' && !md.configuration['set']) {\n throw itemName + ' has malformed ' + NAMESPACE + ' metadata, type is \"dayset\" but required \"set\" property is not found!';\n }\n\n if(dayType == 'custom' && !md.configuration['file']) {\n throw itemName + ' has malformed ' + NAMESPACE + ' metadata, type is \"custom\" but required \"file\" property is not found!';\n }\n\n if(!items[itemName].type.startsWith('DateTime')) {\n throw itemName + ' is not a DateTime Item!';\n }\n\n if(items[itemName].isUninitialized) {\n throw itemName + \" is not initialized!: \" + items[itemName].state;\n }\n\n console.debug(itemName+ ' is valid');\n};\n\n/**\n * Return all members of the DT_GROUP that has a \"type\" metadata configuration property that\n * matches the passed in type.\n *\n * @param {string} type the day type defined in the metadata we want to get the Items for\n * @returns {Array} all the Items with the matching type in the metadata\n */\nvar getItemsOfType = (type) => {\n const allItems = items[DT_GROUP].members;\n return allItems.filter( item => item.getMetadata()[NAMESPACE].configuration['type'] == type);\n};\n\n/**\n * Returns true if all the Items of the given type have a unique \"state\" value\n * in the metadata.\n *\n * @param {string} the day type\n * @returns {boolean} true if all states are unique, false otherwise\n */\nvar checkUniqueStates = (type) => {\n const allItems = getItemsOfType(type);\n const states = new Set(allItems.map(i => { return i.getMetadata()[NAMESPACE].value; }));\n return !allItems.length || allItems.length == states.size;\n};\n\n/**\n * Check that all Items are configured correctly.\n */\nvar validateAllConfigs = () => {\n console.debug('Validating Item types, Item metadata, and Group membership');\n\n // Check that all members of the Group have metadata\n const itemsWithMD = items[DT_GROUP].members.filter(item => item.getMetadata(NAMESPACE)).length;\n if(itemsWithMD != items[DT_GROUP].members.length) {\n const noMdItems = items[DT_GROUP].members.filter(item => !item.getMetadata(NAMESPACE));\n console.warn('The following Items do not have required ' + NAMESPACE + ' metadata: ' + noMdItems.map(item => item.name).join(', '));\n return false; // no sense on performing any additional tests\n }\n\n // Check each Item's metadata\n let isGood = helpers.checkGrpAndMetadata(NAMESPACE, DT_GROUP, validateItemConfig, USAGE);\n\n // Check the state item\n if(!items[STATE_ITEM]){\n console.warn('The state Item ' + STATE_ITEM + ' does not exist!');\n isGood = false;\n }\n\n if(!items[STATE_ITEM].type.startsWith('String')) {\n console.warn('The state Item ' + STATE_ITEM + ' is not a String Item!');\n isGood = false;\n }\n\n // Check to see if we have a default set of Items\n if(!getItemsOfType('default')) {\n console.warn('There are no \"default\" day type Items defined! Make sure you have all day types covered!');\n // we'll not invalidate if there are no \"default\" items\n }\n\n // Check that each data set has a unique state for each Item\n DAY_TYPES.forEach(type => {\n if(!checkUniqueStates(type)) {\n console.warn('Not all the metadata values for Items of type ' + type + ' are unique!');\n isGood = false;\n }\n })\n\n // Report if all configs are good or not\n if(isGood) {\n console.debug('All ' + NAMESPACE + ' Items are configured correctly');\n }\n return isGood;\n};\n\n/**\n * Pull the set of Items for today based on Ephemeris. The Ephemeris hierarchy is\n * - custom\n * - holiday\n * - dayset\n * - weeekend\n * - weekday\n * - default\n *\n * If there are no DateTime Items defined for today's type, null is returned.\n */\nvar getTodayItems = () => {\n // Get all the DateTime Items that might apply to today given what type of day it is\n // For example, if it's a weekend, there will be no weekday Items pulled. Whether or not\n // the entry in this dict has an array of Items determines whether today is of that day\n // type.\n const startTimes = [\n { 'type': 'custom', 'times' : getItemsOfType('custom').filter(item => actions.Ephemeris.isBankHoliday(0, item.getMetadata()[NAMESPACE].configuration['file'])) },\n { 'type': 'holiday', 'times' : (actions.Ephemeris.isBankHoliday()) ? getItemsOfType('holiday') : [] },\n { 'type': 'dayset', 'times' : getItemsOfType('dayset').filter(item => actions.Ephemeris.isInDayset(items.getMetadata()[NAMESPACE].configuration['set'])) },\n { 'type': 'weekend', 'times' : (actions.Ephemeris.isWeekend()) ? getItemsOfType('weekend') : [] },\n { 'type': 'weekday', 'times' : (!actions.Ephemeris.isWeekend()) ? getItemsOfType('weekday') : [] },\n { 'type': 'default', 'times' : getItemsOfType('default') }\n ];\n\n // Go through startTimes in order and choose the first one that has a non-empty list of Items\n const dayStartTimes = startTimes.find(dayset => dayset.times.length);\n\n if(dayStartTimes === null) {\n console.warn('No DateTime Items found for today');\n return null;\n }\n else {\n console.info('Today is a ' + dayStartTimes.type + ' day.');\n return dayStartTimes.times;\n }\n};\n\n/**\n * Returns a function called to transition the state machine from one state to the next\n *\n * @param {string} state the new state to transition to\n * @param {function} the function that transitions the state\n */\nvar stateTransitionGenerator = (state) => {\n return function() {\n console.info('Transitioning Time State Machine from ' + items[STATE_ITEM].state + ' to ' + state);\n items[STATE_ITEM].sendCommand(state);\n }\n}\n\n/**\n * Returns a function that generates the timers for all the passed in startTimes\n *\n * @param {Array} startTimes list of today's state start times\n * @param {timerMgr.TimerMgr} timers collection of timers\n * @returns {function} called to generate the timers to transition between the states\n */\nvar createTimersGenerator = (timers) => {\n return function() {\n\n if(validateAllConfigs()) {\n\n // Cancel the timers, skipping the debounce timer\n console.debug('Cancelling existing timers');\n timers.cancelAll();\n\n // Get the set of Items for today's state machine\n console.debug(\"Acquiring today's state start times\");\n const startTimes = getTodayItems();\n\n // Get the state and start time, sort them ignoring the date, skip the ones that have\n // already passed and create a timer to transition for the rest.\n console.debug('Creating timers for times that have not already passed');\n var mapped = startTimes.map(i => { return { 'state': i.getMetadata()[NAMESPACE].value,\n 'time' : time.toZDT(i.state).toToday() } });\n mapped.sort((a,b) => {\n if(a.time.isBefore(b.time)) return -1;\n else if(a.time.isAfter(b.time)) return 1;\n else return 0;\n })\n .filter(tod => tod.time.isAfter(time.toZDT()))\n .forEach(tod => {\n // TODO: see if we can move to rules instead of timers\n console.debug('Creating timer for ' + tod.state + ' at ' + tod.time);\n timers.check(tod.state, tod.time.toString(), stateTransitionGenerator(tod.state));\n });\n\n // Figure out the current time of day and move to that state if necessary\n var beforeTimes = mapped.sort((a,b) => {\n if(a.time.isAfter(b.time)) return -1;\n else if(a.time.isBefore(b.time)) return 1;\n else return 0;\n })\n .filter(tod => tod.time.isBefore(time.toZDT()));\n if(!beforeTimes.length) {\n console.debug(\"There is no date time for today before now, we can't know what the current state is, keeping the current time of day state of \" + items[STATE_ITEM].state + \".\");\n }\n else {\n const currState = beforeTimes[0].state\n const stateItem = items[STATE_ITEM];\n console.info('The current state is ' + currState);\n if(stateItem.state != currState) stateItem.sendCommand(currState)\n }\n }\n else {\n console.warn('The config is not valid, cannot proceed!');\n }\n\n };\n};\n\nvar timers = cache.private.get('timers', () => TimerMgr());\n\n// Wait a minute after the last time the rule is triggered to make sure all Items are done changing (e.g.\n// Astro Items) before calculating the new state.\ntimers.check('debounce',\n 'PT10S',\n createTimersGenerator(timers),\n true,\n () => { console.debug('Flapping detected, waiting before creating timers for today'); });\n",
"type": "application/javascript"
},
"id": "3",
"inputs": {
},
"type": "script.ScriptAction"
}
],
"conditions": [
],
"configDescriptions": [
{
"context": "item",
"description": "String Item that holds the current time of day's state.",
"filterCriteria": [
{
"name": "type",
"value": "String"
}
],
"label": "Time of Day State Item",
"name": "timeOfDay",
"required": true,
"type": "TEXT"
},
{
"context": "item",
"description": "Has as members all the DateTime Items that define time of day states.",
"filterCriteria": [
{
"name": "type",
"value": "Group"
}
],
"label": "Times of Day Group",
"name": "timesOfDayGrp",
"required": true,
"type": "TEXT"
},
{
"description": "The Item metadata namespace (e.g. \"tsm\").",
"label": "Time of Day Namespace",
"name": "namespace",
"required": true,
"type": "TEXT"
}
],
"description": "Creates timers to transition a state Item to a new state at defined times of day.",
"label": "Time Based State Machine4",
"triggers": [
{
"configuration": {
"groupName": "{{timesOfDayGrp}}"
},
"id": "1",
"type": "core.GroupStateChangeTrigger"
},
{
"configuration": {
"startlevel": 100
},
"id": "2",
"type": "core.SystemStartlevelTrigger"
},
{
"configuration": {
"time": "00:05"
},
"id": "4",
"type": "timer.TimeOfDayTrigger"
}
],
"uid": "rules_tools:tsm_m"
},
{
"actions": [
{
"configuration": {
"script": "var from = parseFloat(oldState.toString().split(' ')[0]);\nvar to = parseFloat(newState.toString().split(' ')[0]);\n\nprint(from + '>' + to);\n\nif (to < {{threshold}} && from >= {{threshold}}) {\n events.sendCommand('{{alertItem}}', '{{alertCommand}}');\n}\n",
"type": "application/javascript"
},
"id": "2",
"inputs": {
},
"type": "script.ScriptAction"
}
],
"conditions": [
],
"configDescriptions": [
{
"context": "item",
"description": "Item that holds the power (in watts) of the washing machine. Can be a quantity type (Number:Power).",
"label": "Power Item",
"name": "powerItem",
"required": true,
"type": "TEXT"
},
{
"defaultValue": 2,
"description": "When the power measurement was at or above the threshold and crosses below it, trigger the alert.",
"label": "Threshold",
"name": "threshold",
"required": true,
"type": "DECIMAL"
},
{
"context": "item",
"description": "Item to send a command to when the measured power gets below the threshold. For instance, a Hue light advanced Alert channel.",
"label": "Alert Item",
"name": "alertItem",
"required": true,
"type": "TEXT"
},
{
"defaultValue": "LSELECT",
"description": "Command to send to the alert item (for an item linked to a Hue light alert channel, LSELECT will flash the light for a few seconds).",
"label": "Alert Command",
"name": "alertCommand",
"required": true,
"type": "TEXT"
}
],
"description": "This will monitor the power consumption of a washing machine and send an alert command when it gets below a threshold, meaning it has finished.",
"label": "Alert when Washing Machine Finished2",
"triggers": [
{
"configuration": {
"itemName": "{{powerItem}}",
"state": ""
},
"id": "1",
"type": "core.ItemStateChangeTrigger"
}
],
"uid": "ysc:washing_machine_alert_m"
}
]
Version: 0.0.2-alpha
Keywords: test, debug, testing
Countries: DE, CZ
CoreRange: [3.1.0:5)
DependsOn:
Maturity: alpha
Documentation: https://github.com/Nadahar/Mill-LAN-openHAB-Binding/blob/master/README.md
Description: An extremely unstable version
LoggerPackages: com.example.test2a
URL: https://github.com/Nadahar/Mill-LAN-openHAB-Binding/releases/download/v1.0.1/org.openhab.binding.milllan-1.0.1.jar
some:alpha:json
Version: 0.0.3
CoreRange: [4.2.2:6)
uid: test_widget
props:
parameterGroups: []
parameters:
- name: prop1
label: Prop 1
type: TEXT
description: A text prop
- name: item
label: Item
type: TEXT
context: item
description: An item to control
tags: []
component: f7-card
config:
title: '=(props.item) ? "State of " + props.item : "Set props to test!"'
footer: =props.prop1
content: =items[props.item].displayState || items[props.item].state
Version: 0.0.4.beta1
Maturity: beta
coreRange: [4.2..5.1]
Description: The old tried and tested version
Issues: https://github.com/Nadahar/Mill-LAN-openHAB-Binding/blob/master/bundles/org.openhab.binding.milllan/README.md
BLOCKURL: https://raw.githubusercontent.com/deibich/openhab-libraries/23565b891b4399aaca3e878071dc963b43673d19/blockly/libraries/datetime/datetime.yaml
URL: https://github.com/Nadahar/Mill-LAN-openHAB-Binding/releases/download/v1.0.0/org.openhab.binding.milllan-1.0.0.jar
RULEURL: https://raw.githubusercontent.com/mherwege/openhab-rule-templates/refs/heads/main/wasteBelgium/wasteBelgium-ES6.yaml
Countries: UA
uid: config:dsl:doNothing
label: Does Nothing
type: DSL
configuration:
function: >
logInfo("transform", "this transform does nothing")
input
Version: 0.0.5.alpha
Maturity: beta
coreRange: [4.2..5.2]
Description: The latest hype
Issues: https://github.com/Nadahar/Mill-LAN-openHAB-Binding/blob/master/bundles/org.openhab.binding.milllan/README.md
Countries: UA, ES
uid: config:dsl:doNothing
label: Still Does Nothing
type: DSL
configuration:
function: >
logInfo("transform", "this transform does nothing either")
input