Improved templates

This commit is contained in:
Thomas Lovén 2019-02-27 16:22:57 +01:00
parent 01e9afef1b
commit a89860815f
2 changed files with 67 additions and 32 deletions

View File

@ -85,7 +85,7 @@ The following functions are defined:
| `cardTools.moreInfo(entity)` | 0.1 | Brings up the `more-info` dialog for the specified `entity` id | | `cardTools.moreInfo(entity)` | 0.1 | Brings up the `more-info` dialog for the specified `entity` id |
| `cardTools.longPress(element)` | 0.1 | Bind `element` to the long-press handler of lovelace | | `cardTools.longPress(element)` | 0.1 | Bind `element` to the long-press handler of lovelace |
| `cardTools.hasTemplate(text)` | 0.2 | Check if `text` contains a simple state template | | `cardTools.hasTemplate(text)` | 0.2 | Check if `text` contains a simple state template |
| `cardTools.parseTemplate(text, [error])` | 0.2 | Parse a simple state template and return results | | `cardTools.parseTemplate(text, [data])` | 0.2 | Parse a simple state template and return results |
| `cardTools.args(script)` | 0.3 | Returns URL parameters of the script from `resources:` | | `cardTools.args(script)` | 0.3 | Returns URL parameters of the script from `resources:` |
| `cardTools.localize(key)` | 0.3 | Returns translations of certains strings to the users language | | `cardTools.localize(key)` | 0.3 | Returns translations of certains strings to the users language |
| `cardTools.lovelace`| 0.4 | A reference to a structure containing some information about the users lovelace configuration | | `cardTools.lovelace`| 0.4 | A reference to a structure containing some information about the users lovelace configuration |
@ -220,7 +220,7 @@ Next is one of:
The function replaces any template found in the supplied string with the requested value, or an error message on failure. The function replaces any template found in the supplied string with the requested value, or an error message on failure.
The optional argument `error` is a string that replaces the default error message. The optional argument `data` can be an object containing extra data for templates. In a template `{key}` will be evaluated to `data[key]`.
`cardTools.hasTemplate` just checks if a string contains a simple state template. `cardTools.hasTemplate` just checks if a string contains a simple state template.
@ -286,3 +286,4 @@ The `script` parameter is required if `cardTools.logger` is called from within a
- Added `script` parameter to `cardTools.args` - Added `script` parameter to `cardTools.args`
--- ---
<a href="https://www.buymeacoffee.com/uqD6KHCdJ" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/white_img.png" alt="Buy Me A Coffee" style="height: auto !important;width: auto !important;" ></a>

View File

@ -211,23 +211,60 @@ class {
return /\[\[\s+.*\s+\]\]/.test(text); return /\[\[\s+.*\s+\]\]/.test(text);
} }
static parseTemplateString(str) { static parseTemplateString(str, specialData = {}) {
if(typeof(str) !== "string") return text; if(typeof(str) !== "string") return text;
var RE_entity = /^[a-zA-Z0-9_.]+\.[a-zA-Z0-9_]+$/; const FUNCTION = /^[a-zA-Z0-9_]+\(.*\)$/;
var RE_if = /^if\(([^,]*),([^,]*),(.*)\)$/; const EXPR = /([^=<>!]+)\s*(==|!=|<|>|<=|>=)\s*([^=<>!]+)/;
var RE_expr = /([^=<>!]+)\s*(==|<|>|<=|>=|!=)\s*([^=<>!]+)/ const SPECIAL = /^\{.+\}$/;
const STRING = /^"[^"]*"|'[^']*'$/;
if(typeof(specialData) === "string") specialData = {};
specialData = Object.assign({
user: this.hass.user.name,
browser: this.deviceID,
hash: location.hash.substr(1) || ' ',
}, specialData);
const _parse_function = (str) => {
let args = [str.substr(0, str.indexOf('(')).trim()]
str = str.substr(str.indexOf('(')+1);
while(str) {
let index = 0;
let stack = [];
while(str[index]) {
if(",)".includes(str[index]) && !stack.length) break;
if(str[index] == '(') stack.push(')');
if(stack[stack.length - 1] === str[index]) stack.pop();
else if(`"'`.includes(str[index])) stack.push(str[index]);
index = index + 1;
}
args.push(str.substr(0, index).trim());
str = str.substr(index+1);
}
return args;
};
const _parse_special = (str) => {
str = str.substr(1, str.length - 2);
return specialData[str] || `{${str}}`;
};
const _parse_entity = (str) => { const _parse_entity = (str) => {
str = str.trim(); str = str.split(".");
const parts = str.split("."); let v;
let v = this.hass().states[`${parts.shift()}.${parts.shift()}`]; if(str[0].match(SPECIAL)) {
if(!parts.length) return v['state']; v = _parse_special(str.shift());
parts.forEach(item => v=v[item]); v = this.hass().states[v] || v;
} else {
v = this.hass().states[`${str.shift()}.${str.shift()}`];
if(!str.length) return v['state'];
}
str.forEach(item => v=v[item]);
return v; return v;
} }
const _parse_expr = (str) => { const _eval_expr = (str) => {
str = RE_expr.exec(str); str = EXPR.exec(str);
if(str === null) return false; if(str === null) return false;
const lhs = this.parseTemplateString(str[1]); const lhs = this.parseTemplateString(str[1]);
const rhs = this.parseTemplateString(str[3]); const rhs = this.parseTemplateString(str[3]);
@ -239,38 +276,35 @@ class {
return eval(expr); return eval(expr);
} }
const _parse_if = (str) => { const _eval_function = (args) => {
str = RE_if.exec(str); if(args[0] === "if") {
if(_parse_expr(str[1])) if(_eval_expr(args[1]))
return this.parseTemplateString(str[2]); return this.parseTemplateString(args[2]);
return this.parseTemplateString(str[3]); return this.parseTemplateString(args[3]);
}
} }
try { try {
str = str.trim(); str = str.trim();
if(str.match(RE_if)) if(str.match(STRING))
return _parse_if(str);
if(str.match(RE_entity))
return _parse_entity(str);
if(str.match(/^".*"$/) || str.match(/^'.*'$/))
return str.substr(1, str.length - 2); return str.substr(1, str.length - 2);
if(str.match(/{user}/)) if(str.match(SPECIAL))
return this.hass().user.name; return _parse_special(str);
if(str.match(/{browser}/)) if(str.match(FUNCTION))
return this.deviceID(); return _eval_function(_parse_function(str));
if(str.match(/{hash}/)) if(str.includes("."))
return location.hash.substr(1); return _parse_entity(str);
return str; return str;
} catch (err) { } catch (err) {
return `[[ Template matching failed ${str} ]]`; return `[[ Template matching failed: ${str} ]]`;
} }
} }
static parseTemplate(text, error) { static parseTemplate(text, data = {}) {
if(typeof(text) !== "string") return text; if(typeof(text) !== "string") return text;
// Note: .*? is javascript regex syntax for NON-greedy matching // Note: .*? is javascript regex syntax for NON-greedy matching
var RE_template = /\[\[\s(.*?)\s\]\]/g; var RE_template = /\[\[\s(.*?)\s\]\]/g;
text = text.replace(RE_template, (str, p1, offset, s) => this.parseTemplateString(p1)); text = text.replace(RE_template, (str, p1, offset, s) => this.parseTemplateString(p1, data));
return text; return text;
} }