ha-nunjucks 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -1
- package/dist/filters.js +5 -1
- package/dist/globals.js +5 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.js +54 -31
- package/dist/models/interfaces/hass.d.ts +3 -0
- package/dist/utils/labels.d.ts +1 -1
- package/dist/utils/labels.js +7 -10
- package/dist/utils/state_translated.d.ts +5 -0
- package/dist/utils/state_translated.js +30 -0
- package/dist/utils/states.d.ts +1 -1
- package/dist/utils/states.js +8 -9
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -54,6 +54,34 @@ const context = {
|
|
|
54
54
|
const renderedString = renderTemplate(this.hass, templateString, context);
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
+
`renderTemplate` will try to validate that inputs contain valid templates by default using the exported function `hasTemplate`. You can disable this by setting the fourth argument of `renderTemplate` to `false`. This way you can perform this check yourself before any additional templating setup you perform in your code.
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { hasTemplate, renderTemplate } from 'ha-nunjucks';
|
|
61
|
+
|
|
62
|
+
if (!hasTemplate(templateString)) {
|
|
63
|
+
return templateString;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const context = {
|
|
67
|
+
foo: 'bar',
|
|
68
|
+
doThing(thing: string) {
|
|
69
|
+
return `doing ${thing}!`;
|
|
70
|
+
},
|
|
71
|
+
config: {
|
|
72
|
+
entity: 'foo.bar',
|
|
73
|
+
attribute: 'baz_bah',
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const renderedString = renderTemplate(
|
|
78
|
+
this.hass,
|
|
79
|
+
templateString,
|
|
80
|
+
context,
|
|
81
|
+
false,
|
|
82
|
+
);
|
|
83
|
+
```
|
|
84
|
+
|
|
57
85
|
### Return Types
|
|
58
86
|
|
|
59
87
|
`renderTemplate` will return a string unless the result is `true` or `false` (_not_ case sensitive), in which case it will return a boolean.
|
|
@@ -115,6 +143,10 @@ Functions used to determine an entity's state or an attribute.
|
|
|
115
143
|
| state_translated | function, filter | entity_id, state (optional) | Returns the formatted and translated state of an entity or provided state using a language that is currently configured in the general settings. |
|
|
116
144
|
| attr_name_translated | function, filter | entity_id, attr_name | Returns the formatted and translated attribute name of an entity using a language that is currently configured in the general settings. |
|
|
117
145
|
| attr_value_translated | function, filter | entity_id, attr_name, attr_value (optional) | Returns the formatted and translated attribute value of an entity or provided attribute value using a language that is currently configured in the general settings. |
|
|
146
|
+
| number_translated | function, filter | value | Returns the formatted and translated input number using a language that is currently configured in the general settings. |
|
|
147
|
+
| date_translated | function, filter | value | Returns the formatted and translated input date or datetime as a date using a language that is currently configured in the general settings. |
|
|
148
|
+
| time_translated | function, filter | value | Returns the formatted and translated input time or datetime as a time using a language that is currently configured in the general settings. |
|
|
149
|
+
| datetime_translated | function, filter | value | Returns the formatted and translated input datetime using a language that is currently configured in the general settings. |
|
|
118
150
|
|
|
119
151
|
### [Groups](https://www.home-assistant.io/docs/configuration/templating/#working-with-groups)
|
|
120
152
|
|
|
@@ -164,7 +196,7 @@ Functions used to determine an entity's state or an attribute.
|
|
|
164
196
|
|
|
165
197
|
### [Labels](https://www.home-assistant.io/docs/configuration/templating/#labels)
|
|
166
198
|
|
|
167
|
-
**NOTE**: Labels are not available in the `hass` object and must be retrieved asynchronously from the Home Assistant backend the first time `
|
|
199
|
+
**NOTE**: Labels are not available in the `hass` object and must be retrieved asynchronously from the Home Assistant backend the first time `ha-nunjucks` is imported. Since this package is otherwise synchronous, this can cause a race condition where no labels are found the first time `renderTemplate` is run. This generally resolves itself once the template re-renders.
|
|
168
200
|
|
|
169
201
|
| Name | Type | Arguments | Description |
|
|
170
202
|
| -------------- | ---------------- | ----------------------- | ------------------------------------------------------------------------------------------ |
|
package/dist/filters.js
CHANGED
|
@@ -10,7 +10,7 @@ import { label_areas, label_devices, label_entities, label_id, label_name, label
|
|
|
10
10
|
import { str } from './utils/miscellaneous';
|
|
11
11
|
import { acos, add, asin, atan, atan2, average, bitwise_and, bitwise_not, bitwise_or, bitwise_xor, bool, cos, is_number, log, max, median, min, multiply, ord, sin, sqrt, statistical_mode, tan, } from './utils/numeric';
|
|
12
12
|
import { regex_findall, regex_findall_index, regex_replace, } from './utils/regexp';
|
|
13
|
-
import { attr_name_translated, attr_value_translated, state_translated, } from './utils/state_translated';
|
|
13
|
+
import { attr_name_translated, attr_value_translated, date_translated, datetime_translated, number_translated, state_translated, time_translated, } from './utils/state_translated';
|
|
14
14
|
import { has_value, state_attr, states } from './utils/states';
|
|
15
15
|
import { as_datetime, as_local, as_timestamp, time_since, time_until, timestamp_custom, timestamp_local, timestamp_utc, today_at, } from './utils/time';
|
|
16
16
|
export function addFilters(env) {
|
|
@@ -74,6 +74,9 @@ const FILTERS = {
|
|
|
74
74
|
timestamp_local,
|
|
75
75
|
timestamp_utc,
|
|
76
76
|
timestamp_custom,
|
|
77
|
+
date_translated,
|
|
78
|
+
time_translated,
|
|
79
|
+
datetime_translated,
|
|
77
80
|
// To/From JSON
|
|
78
81
|
to_json,
|
|
79
82
|
from_json,
|
|
@@ -107,6 +110,7 @@ const FILTERS = {
|
|
|
107
110
|
ord,
|
|
108
111
|
multiply,
|
|
109
112
|
add,
|
|
113
|
+
number_translated,
|
|
110
114
|
// Regular Expressions
|
|
111
115
|
regex_replace,
|
|
112
116
|
regex_findall,
|
package/dist/globals.js
CHANGED
|
@@ -9,7 +9,7 @@ import { integration_entities } from './utils/integrations';
|
|
|
9
9
|
import { label_areas, label_devices, label_entities, label_id, label_name, labels, } from './utils/labels';
|
|
10
10
|
import { match_media, str } from './utils/miscellaneous';
|
|
11
11
|
import { acos, asin, atan, atan2, average, bool, cos, e, float, inf, int, is_number, log, max, median, min, pi, sin, sqrt, statistical_mode, tan, tau, } from './utils/numeric';
|
|
12
|
-
import { attr_name_translated, attr_value_translated, state_translated, } from './utils/state_translated';
|
|
12
|
+
import { attr_name_translated, attr_value_translated, date_translated, datetime_translated, number_translated, state_translated, time_translated, } from './utils/state_translated';
|
|
13
13
|
import { has_value, is_state, is_state_attr, state_attr, states, } from './utils/states';
|
|
14
14
|
import { as_datetime, as_local, as_timedelta, as_timestamp, now, strptime, time_since, time_until, today_at, utcnow, } from './utils/time';
|
|
15
15
|
import { list, set } from './utils/type_conversions';
|
|
@@ -95,6 +95,9 @@ const GLOBALS = {
|
|
|
95
95
|
time_since,
|
|
96
96
|
time_until,
|
|
97
97
|
as_timedelta,
|
|
98
|
+
date_translated,
|
|
99
|
+
time_translated,
|
|
100
|
+
datetime_translated,
|
|
98
101
|
// Numeric,
|
|
99
102
|
float,
|
|
100
103
|
is_number,
|
|
@@ -114,6 +117,7 @@ const GLOBALS = {
|
|
|
114
117
|
average,
|
|
115
118
|
median,
|
|
116
119
|
statistical_mode,
|
|
120
|
+
number_translated,
|
|
117
121
|
// Type Conversions
|
|
118
122
|
set,
|
|
119
123
|
list,
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,13 @@ import { HomeAssistant } from './models/interfaces/hass';
|
|
|
4
4
|
* @param {HomeAssistant} hass The Home Assistant object
|
|
5
5
|
* @param {string} str The template string to render
|
|
6
6
|
* @param {object} [context] Additional context to expose to nunjucks
|
|
7
|
+
* @param {boolean} [validate=true] Validate that the input contains a template.
|
|
7
8
|
* @returns {string | boolean} The rendered template string if a string was provided, otherwise the unaltered input
|
|
8
9
|
*/
|
|
9
|
-
export declare function renderTemplate(hass: HomeAssistant, str: string, context?: object): string | boolean;
|
|
10
|
+
export declare function renderTemplate(hass: HomeAssistant, str: string, context?: object, validate?: boolean): string | boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Test if the input contains a valid template
|
|
13
|
+
* @param {any} str the variable to check
|
|
14
|
+
* @returns if the input is a string that contains a template
|
|
15
|
+
*/
|
|
16
|
+
export declare function hasTemplate(str: any): boolean;
|
package/dist/index.js
CHANGED
|
@@ -5,49 +5,72 @@ import { addTests } from './tests';
|
|
|
5
5
|
import { fetchLabelRegistry } from './utils/labels';
|
|
6
6
|
import { buildStatesObject } from './utils/states';
|
|
7
7
|
if (!window.haNunjucks) {
|
|
8
|
-
window.haNunjucks = {
|
|
8
|
+
window.haNunjucks = {
|
|
9
|
+
states: {},
|
|
10
|
+
labelRegistry: {},
|
|
11
|
+
};
|
|
12
|
+
// Async setup label registry and states object on import
|
|
13
|
+
const registrySetup = async () => {
|
|
14
|
+
const ha = document.querySelector('home-assistant');
|
|
15
|
+
if (!ha ||
|
|
16
|
+
!ha.hass ||
|
|
17
|
+
!ha.hass.connected ||
|
|
18
|
+
!ha.hass.connection ||
|
|
19
|
+
!ha.hass.connection.connected) {
|
|
20
|
+
setTimeout(registrySetup, 10);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
// Number and datetime translators
|
|
24
|
+
window.haNunjucks.numberFormat = new Intl.NumberFormat(ha.hass.language);
|
|
25
|
+
window.haNunjucks.dateFormat = new Intl.DateTimeFormat(ha.hass.language, { dateStyle: 'full' });
|
|
26
|
+
window.haNunjucks.timeFormat = new Intl.DateTimeFormat(ha.hass.language, { timeStyle: 'long' });
|
|
27
|
+
window.haNunjucks.datetimeFormat = new Intl.DateTimeFormat(ha.hass.language, { dateStyle: 'full', timeStyle: 'long' });
|
|
28
|
+
// Label registry and states object
|
|
29
|
+
window.haNunjucks.hass = ha.hass;
|
|
30
|
+
fetchLabelRegistry();
|
|
31
|
+
buildStatesObject();
|
|
32
|
+
};
|
|
33
|
+
registrySetup();
|
|
34
|
+
// Initialize global ha-nunjucks environment
|
|
9
35
|
nunjucks.installJinjaCompat();
|
|
10
36
|
window.haNunjucks.env = addTests(addFilters(addGlobals(new nunjucks.Environment())));
|
|
11
|
-
window.haNunjucks.states = {};
|
|
12
|
-
window.hassConnection?.then((hassConnection) => {
|
|
13
|
-
const entities = hassConnection?.conn?._entityRegistryDisplay?.state
|
|
14
|
-
?.entities;
|
|
15
|
-
for (const entity of entities) {
|
|
16
|
-
const [domain, _id] = entity.ei.split('.');
|
|
17
|
-
window.haNunjucks.states[domain] ??= {};
|
|
18
|
-
}
|
|
19
|
-
});
|
|
20
37
|
}
|
|
21
38
|
/**
|
|
22
39
|
* Render a Home Assistant template string using nunjucks
|
|
23
40
|
* @param {HomeAssistant} hass The Home Assistant object
|
|
24
41
|
* @param {string} str The template string to render
|
|
25
42
|
* @param {object} [context] Additional context to expose to nunjucks
|
|
43
|
+
* @param {boolean} [validate=true] Validate that the input contains a template.
|
|
26
44
|
* @returns {string | boolean} The rendered template string if a string was provided, otherwise the unaltered input
|
|
27
45
|
*/
|
|
28
|
-
export function renderTemplate(hass, str, context) {
|
|
29
|
-
if (!
|
|
30
|
-
|
|
46
|
+
export function renderTemplate(hass, str, context, validate = true) {
|
|
47
|
+
if (validate && !hasTemplate(str)) {
|
|
48
|
+
return str;
|
|
31
49
|
}
|
|
32
50
|
window.haNunjucks.hass = hass;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (['true', 'false'].includes(lowerStr)) {
|
|
48
|
-
return lowerStr == 'true';
|
|
49
|
-
}
|
|
50
|
-
return str;
|
|
51
|
+
buildStatesObject();
|
|
52
|
+
str = window.haNunjucks.env
|
|
53
|
+
.renderString(structuredClone(str), {
|
|
54
|
+
hass,
|
|
55
|
+
_states: window.haNunjucks.states,
|
|
56
|
+
...context,
|
|
57
|
+
})
|
|
58
|
+
.trim();
|
|
59
|
+
if ([undefined, null, 'undefined', 'null', 'None'].includes(str)) {
|
|
60
|
+
return '';
|
|
61
|
+
}
|
|
62
|
+
const lowerStr = str.toLowerCase();
|
|
63
|
+
if (['true', 'false'].includes(lowerStr)) {
|
|
64
|
+
return lowerStr == 'true';
|
|
51
65
|
}
|
|
52
66
|
return str;
|
|
53
67
|
}
|
|
68
|
+
const hasTemplateRegex = /{{.*?}}|{%.*?%}/;
|
|
69
|
+
/**
|
|
70
|
+
* Test if the input contains a valid template
|
|
71
|
+
* @param {any} str the variable to check
|
|
72
|
+
* @returns if the input is a string that contains a template
|
|
73
|
+
*/
|
|
74
|
+
export function hasTemplate(str) {
|
|
75
|
+
return hasTemplateRegex.test(str);
|
|
76
|
+
}
|
|
@@ -37,3 +37,6 @@ export interface HomeAssistant {
|
|
|
37
37
|
formatEntityAttributeValue(stateObj: HassEntity, attribute: string, value?: any): string;
|
|
38
38
|
formatEntityAttributeName(stateObj: HassEntity, attribute: string): string;
|
|
39
39
|
}
|
|
40
|
+
export interface HassElement extends HTMLElement {
|
|
41
|
+
hass: HomeAssistant;
|
|
42
|
+
}
|
package/dist/utils/labels.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { HomeAssistant } from '../models/interfaces/hass';
|
|
2
|
-
export declare function fetchLabelRegistry(
|
|
2
|
+
export declare function fetchLabelRegistry(): Promise<void>;
|
|
3
3
|
export declare function labels(hass: HomeAssistant, lookup_value?: string): string[];
|
|
4
4
|
export declare function label_id(lookup_value: string): string | undefined;
|
|
5
5
|
export declare function label_name(lookup_value: string): string;
|
package/dist/utils/labels.js
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
export async function fetchLabelRegistry(
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
for (const label of labels) {
|
|
9
|
-
window.haNunjucks.labelRegistry[label.label_id] = label;
|
|
10
|
-
}
|
|
1
|
+
export async function fetchLabelRegistry() {
|
|
2
|
+
const labels = await window.haNunjucks.hass.connection.sendMessagePromise({
|
|
3
|
+
type: 'config/label_registry/list',
|
|
4
|
+
});
|
|
5
|
+
labels.sort((ent1, ent2) => ent1.name.localeCompare(ent2.name));
|
|
6
|
+
for (const label of labels) {
|
|
7
|
+
window.haNunjucks.labelRegistry[label.label_id] = label;
|
|
11
8
|
}
|
|
12
9
|
}
|
|
13
10
|
export function labels(hass, lookup_value) {
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
+
import { date, datetime, time } from 'ts-py-datetime';
|
|
1
2
|
import { HomeAssistant } from '../models/interfaces/hass';
|
|
2
3
|
export declare function state_translated(hass: HomeAssistant, entity_id: string, state?: string): string;
|
|
3
4
|
export declare function attr_name_translated(hass: HomeAssistant, entity_id: string, attr_name: string): string;
|
|
4
5
|
export declare function attr_value_translated(hass: HomeAssistant, entity_id: string, attr_name: string, attr_value?: string): any;
|
|
6
|
+
export declare function number_translated(value: number): string | number;
|
|
7
|
+
export declare function date_translated(value: date | datetime): string | date | datetime;
|
|
8
|
+
export declare function time_translated(value: time | datetime): string | datetime | time;
|
|
9
|
+
export declare function datetime_translated(value: datetime): string | datetime;
|
|
@@ -26,3 +26,33 @@ export function attr_value_translated(hass, entity_id, attr_name, attr_value) {
|
|
|
26
26
|
undefined);
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
|
+
export function number_translated(value) {
|
|
30
|
+
if (isNaN(value)) {
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
return window.haNunjucks.numberFormat.format(value);
|
|
34
|
+
}
|
|
35
|
+
export function date_translated(value) {
|
|
36
|
+
try {
|
|
37
|
+
return window.haNunjucks.dateFormat.format(value.jsDate);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return value;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export function time_translated(value) {
|
|
44
|
+
try {
|
|
45
|
+
return window.haNunjucks.timeFormat.format(value.jsDate);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return value;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export function datetime_translated(value) {
|
|
52
|
+
try {
|
|
53
|
+
return window.haNunjucks.datetimeFormat.format(value.jsDate);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return value;
|
|
57
|
+
}
|
|
58
|
+
}
|
package/dist/utils/states.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { HomeAssistant } from '../models/interfaces/hass';
|
|
2
|
+
export declare function buildStatesObject(): void;
|
|
2
3
|
export declare function states(hass: HomeAssistant, entity_id: string, rounded?: boolean | Record<string, boolean>, with_unit?: boolean): string | undefined;
|
|
3
4
|
export declare function is_state(hass: HomeAssistant, entity_id: string, value: string | string[]): boolean;
|
|
4
5
|
export declare function state_attr(hass: HomeAssistant, entity_id: string, attribute: string): any;
|
|
5
6
|
export declare function is_state_attr(hass: HomeAssistant, entity_id: string, attribute: string, value: string): boolean;
|
|
6
7
|
export declare function has_value(hass: HomeAssistant, entity_id: string): boolean;
|
|
7
|
-
export declare function buildStatesObject(): Record<string, Record<string, import("home-assistant-js-websocket").HassEntity>>;
|
package/dist/utils/states.js
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
export function buildStatesObject() {
|
|
2
|
+
for (const entityId in window.haNunjucks.hass.states) {
|
|
3
|
+
const [domain, id] = entityId.split('.');
|
|
4
|
+
window.haNunjucks.states[domain] ??= {};
|
|
5
|
+
window.haNunjucks.states[domain][id] =
|
|
6
|
+
window.haNunjucks.hass.states[entityId];
|
|
7
|
+
}
|
|
8
|
+
}
|
|
1
9
|
export function states(hass, entity_id, rounded, with_unit) {
|
|
2
10
|
if (typeof rounded == 'object' && !Array.isArray(rounded)) {
|
|
3
11
|
with_unit = rounded.with_unit ?? with_unit;
|
|
@@ -64,12 +72,3 @@ export function has_value(hass, entity_id) {
|
|
|
64
72
|
return false;
|
|
65
73
|
}
|
|
66
74
|
}
|
|
67
|
-
export function buildStatesObject() {
|
|
68
|
-
for (const entityId in window.haNunjucks.hass.states) {
|
|
69
|
-
const [domain, id] = entityId.split('.');
|
|
70
|
-
window.haNunjucks.states[domain] ??= {};
|
|
71
|
-
window.haNunjucks.states[domain][id] =
|
|
72
|
-
window.haNunjucks.hass.states[entityId];
|
|
73
|
-
}
|
|
74
|
-
return window.haNunjucks.states;
|
|
75
|
-
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ha-nunjucks",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Wrapper for nunjucks for use with Home Assistant frontend custom components to render templates",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"files": [
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
],
|
|
9
9
|
"type": "module",
|
|
10
10
|
"scripts": {
|
|
11
|
-
"test": "ts-mocha tests/**/*.test.ts",
|
|
11
|
+
"test": "ts-mocha tests/**/*.test.ts --require fixtures.cjs",
|
|
12
12
|
"build": "tsc",
|
|
13
13
|
"prelint": "tsc --noemit",
|
|
14
14
|
"lint": "eslint --config ./.eslintrc.config.cjs"
|