Test Bundle (DO NOT INSTALL) [2.0.0;2.0.1)

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