UNPKG

openhab_rules_tools

Version:

Functions and classes to make writing openHAB rules in JS Scripting easier.

156 lines (143 loc) 6.15 kB
const { actions, time, utils } = require('openhab'); const VERSION = require('./package.json').version; /** * Utility function to create a named timer. * * The name can be set with the name parameter or is autogenerated from the ruleUID (for UI) or filename and the timer's key (if available). * * @param {*} when any representation of time or duration, see {@link https://openhab.github.io/openhab-js/time.html#.toZDT time.toZDT} * @param {function} func function to call when the timer expires * @param {string} [name] name for the timer * @param {string} [key] key of the timer to append to the generated name * @returns openHAB Java {@link https://www.openhab.org/javadoc/latest/org/openhab/core/model/script/actions/timer Timer} */ const createTimer = (when, func, name, key) => { const timeout = time.toZDT(when); if (name === null || name === undefined) { if (global.ruleUID !== undefined) { // Use UI ruleUID and key if available name = 'ui.' + global.ruleUID + ((key !== undefined) ? '.' + key : ''); } else if (global['javax.script.filename'] !== undefined) { // Use filename and key if available name = 'file.' + global['javax.script.filename'].replace(/^.*[\\/]/, '') + ((key !== undefined) ? '.' + key : ''); } } return actions.ScriptExecution.createTimer(name, timeout, func); }; /** * Utility function mostly used by rule templates to check the metadata and Group membership * of the Items that drive a rule like Debounce or Time Based State Machine. An Item metadata * namespace and Group is passed along with a function and usage text. * * The function validates that all Items that have the given namespace are members of grp, * no members of grp lack the namespace metadata, and the validateFunc is called to validate * the metadata on each individual Item. validateFunc should throw an exception with the * reason why the Item failed validation when it fails. The usage text will be logged if one * or more Items fail validations. * * @param {string} namespace the Item metadata namespace to check * @param {string} grp name of the Group Item all the Items with namespace should belong to * @param {function} validateFunc function called to validate the metadata is valid * @param {string} usage text to log out when an Item fails validation * @returns {boolean} true if validation passes, false if there's a problem **/ const checkGrpAndMetadata = (namespace, grp, validateFunc, usage) => { let isGood = true; const badItems = []; // Get all the Items with NAMESPACE metadata const allItems = items.getItems(); const filtered = allItems.filter(item => item.getMetadata(namespace)); // Check the metadata of members of the Group membership filtered.forEach(item => { try { const cfg = validateFunc(item.name); if (!item.groupNames.includes(grp)) { console.warn(item.name + ' has ' + namespace + ' metadata but is not a member of ' + grp + '!'); isGood = false; badItems.push(item.name); } } catch (e) { console.warn('Item ' + item.name + "'s configuration is invalid:\n" + e); isGood = false; badItems.push(item.name); } }); // Check Group membership if (!items[grp]) { console.warn('There is no ' + grp + ' Group!'); isGood = false; } else if (!items[grp].members) { console.warn(grp + ' does not have any members!'); isGood = false; } else { items[grp].members.filter(item => item.getMetadata(namespace) === null).forEach(item => { console.warn(item.name + ' is a member of ' + grp + ' but lacks ' + namespace + ' metadata!'); isGood = false; badItems.push(item.name); }); } // Report the bad Items and print usage info if (isGood) { console.info('All ' + namespace + ' Items are configured correctly'); } else { if (badItems.length) console.warn('The following Items have an invalid configuration: ' + badItems + '\n'); console.warn(usage); } return isGood; }; /** * Validates that the passed in version number is of the format X.Y.Z where * X, Y, and Z are numbers. * * @param {string} version a version string to validate * @returns {boolean} true if valid, false otherwise */ const _validateVersionNum = (version) => { const re = /\d+\.\d+\.\d+/; if (version === null || version === undefined || !(typeof version === 'string')) return false; return re.test(version); }; /** * Compare version numbers of the format X.Y.Z. * * @param {string} v1 the first version number * @param {string} v2 the second version number * @throws error if v1 or v2 are not parsable * @returns {-1|0|1} 0 if the versions are equal, -1 if v1 is lower and 1 if v1 is higher */ const compareVersions = (v1, v2) => { if (!_validateVersionNum(v1)) throw v1 + ' is not a valid version number in the format X.Y.Z where X, Y, and Z are numbers!'; if (!_validateVersionNum(v2)) throw v2 + ' is not a valid version number in the format X.Y.Z where X, Y, and Z are numbers!'; const v1Version = Number.parseFloat(v1); const v1Point = Number.parseInt(v1.split('.')[2]); const v2Version = Number.parseFloat(v2); const v2Point = Number.parseFloat(v2.split('.')[2]); if (v1 == v2) return 0; else if (v1Version > v2Version) return 1; else if (v1Version == v2Version && v1Point > v2Point) return 1; else return -1; }; /** * Checks to see if the minimum versions of openhab-js and openhab_rules_tools * are met. * * @throws error if one or more of the version numbers are malformed * @throws error if one or more of the versions are too old */ const validateLibraries = (minOHJS, minOHRT) => { if (compareVersions(utils.OPENHAB_JS_VERSION, minOHJS) < 0) throw 'Minimum library version not met: openhab-js ' + utils.OPENHAB_JS_VERSION + ' installed, ' + minOHJS + ' required'; if (compareVersions(VERSION, minOHRT) < 0) throw 'Minimum library version not met: openhab_rules_tools ' + VERSION + ' installed, ' + minOHRT + ' required'; }; module.exports = { createTimer, checkGrpAndMetadata, compareVersions, validateLibraries, OHRT_VERSION: VERSION };