ha-nunjucks 1.4.0 → 1.6.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 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
 
@@ -136,15 +168,17 @@ Functions used to determine an entity's state or an attribute.
136
168
  | device_attr | function, filter | device_or_entity_id, attr_name | Returns the value of attr_name for the given device or entity ID. |
137
169
  | is_device_attr | function | device_or_entity_id, attr_name, attr_value | Returns whether the value of attr_name for the given device or entity ID matches attr_value. |
138
170
  | device_id | function, filter | entity_id | Returns the device ID for a given entity ID or device name. |
171
+ | device_name | function, filter | device_or_entity_id | Returns the device name as defined by user or default for a give entity or device ID. |
139
172
 
140
173
  ### [Floors](https://www.home-assistant.io/docs/configuration/templating/#floors)
141
174
 
142
- | Name | Type | Arguments | Description |
143
- | ----------- | ---------------- | ---------------- | ----------------------------------------------------------------------------------------- |
144
- | floors | function | | Returns the full list of floor IDs that include an area. |
145
- | floor_id | function, filter | lookup_value | Returns the floor ID for a given device ID, entity ID, area ID, or area name. |
146
- | floor_name | function, filter | lookup_value | Returns the floor name for a given device ID, entity ID, area ID, area name, or floor ID. |
147
- | floor_areas | function, filter | floor_name_or_id | Returns the list of area IDs tied to a given floor ID or name. |
175
+ | Name | Type | Arguments | Description |
176
+ | -------------- | ---------------- | ---------------- | ----------------------------------------------------------------------------------------------------------- |
177
+ | floors | function | | Returns the full list of floor IDs that include an area. |
178
+ | floor_id | function, filter | lookup_value | Returns the floor ID for a given floor name or alias, device ID, entity ID, area ID, or area name or alias. |
179
+ | floor_name | function, filter | lookup_value | Returns the floor name for a given device ID, entity ID, area ID, area name, or floor ID. |
180
+ | floor_areas | function, filter | floor_name_or_id | Returns the list of area IDs tied to a given floor ID or name. |
181
+ | floor_entities | function, filter | floor_name_or_id | Returns the list of entity IDs tied to a given floor ID or name. |
148
182
 
149
183
  ### [Areas](https://www.home-assistant.io/docs/configuration/templating/#areas)
150
184
 
@@ -164,16 +198,17 @@ Functions used to determine an entity's state or an attribute.
164
198
 
165
199
  ### [Labels](https://www.home-assistant.io/docs/configuration/templating/#labels)
166
200
 
167
- **NOTE**: Labels are not available in the `hass` object and must be retrieved asynchronously from the Home Assistant backend the first time `renderTemplate` is called. 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.
201
+ **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
202
 
169
- | Name | Type | Arguments | Description |
170
- | -------------- | ---------------- | ----------------------- | ------------------------------------------------------------------------------------------ |
171
- | labels | function, filter | lookup_value (optional) | Returns the full list of label IDs, or those for a given area ID, device ID, or entity ID. |
172
- | label_id | function, filter | lookup_value | Returns the label ID for a given label name. |
173
- | label_name | function, filter | lookup_value | Returns the label name for a given label ID. |
174
- | label_areas | function, filter | label_name_or_id | Returns the list of area IDs tied to a given label ID or name. |
175
- | label_devices | function, filter | label_name_or_id | Returns the list of device IDs tied to a given label ID or name. |
176
- | label_entities | function, filter | label_name_or_id | Returns the list of entity IDs tied to a given label ID or name. |
203
+ | Name | Type | Arguments | Description |
204
+ | ----------------- | ---------------- | ----------------------- | ------------------------------------------------------------------------------------------ |
205
+ | labels | function, filter | lookup_value (optional) | Returns the full list of label IDs, or those for a given area ID, device ID, or entity ID. |
206
+ | label_id | function, filter | lookup_value | Returns the label ID for a given label name. |
207
+ | label_name | function, filter | lookup_value | Returns the label name for a given label ID. |
208
+ | label_description | function, filter | lookup_value | Returns the label description for a given label ID. |
209
+ | label_areas | function, filter | label_name_or_id | Returns the list of area IDs tied to a given label ID or name. |
210
+ | label_devices | function, filter | label_name_or_id | Returns the list of device IDs tied to a given label ID or name. |
211
+ | label_entities | function, filter | label_name_or_id | Returns the list of entity IDs tied to a given label ID or name. |
177
212
 
178
213
  ### [Immediate If](https://www.home-assistant.io/docs/configuration/templating/#immediate-if-iif)
179
214
 
@@ -191,21 +226,22 @@ A shorthand for an if else statement.
191
226
  - JS Date is not as good at handling timezones as Python datetime. Be careful about timezone differences! You can try to account for this using the `utc` flags and/or by including a timezone offset in a datetime string to parse using `as_datetime` or `strptime`.
192
227
  - Including time extensions in your templates does not cause them to refresh more regularly by themselves, although they will still update whenever the `hass` object does. If you are a developer, you have to implement this behavior yourself in your custom cards.
193
228
 
194
- | Name | Type | Arguments | Description |
195
- | ---------------- | ---------------- | -------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
196
- | now | function | | Returns a datetime object that represents the current time in your time zone. |
197
- | utcnow | function | | Returns a datetime object of the current time in the UTC timezone. |
198
- | today_at | function, filter | value | Converts a string containing a military time format to a datetime object with today’s date in your time zone. Defaults to midnight (00:00). |
199
- | as_datetime | function, filter | value, fallback (optional), utc (default true) | Converts a string containing a timestamp, or valid UNIX timestamp, to a datetime object. If that fails, it returns the fallback value or, if omitted, raises an error. When the input is already a datetime object it will be returned as is. in case the input is a datetime.date object, midnight will be added as time. |
200
- | as_timestamp | function, filter | value, fallback (optional) | Converts a datetime object or string to UNIX timestamp. If that fails, returns the fallback value, or if omitted raises an error. |
201
- | as_local | function, filter | value | Converts a datetime object to local time. |
202
- | strptime | function | value, format, fallback (optional), utc (default false) | Parses a string based on a [format](https://d3js.org/d3-time-format#locale_format) and returns a datetime object. If that fails, it returns the default value or, if omitted, raises an error. |
203
- | time_since | function, filter | value, precision (default 1) | Returns a human readable string indicating the difference between now and an input past datetime object. `precision` indicates how many units (years, months, days, hours, minutes, seconds) to use, with the last unit being rounded and 0 being the same as 6. If the input datetime is in the past it returns the input. If the input datetime is not a datetime object it returns nothing. |
204
- | time_until | function, filter | value, precision (default 1) | Returns a human readable string indicating the difference between now and an input future datetime object. `precision` indicates how many units (years, months, days, hours, minutes, seconds) to use, with the last unit being rounded and 0 being the same as 6. If the input datetime is in the future it returns the input. If the input datetime is not a datetime object it returns nothing. |
205
- | as_timedelta | function, filter | value | Converts a string to a timedelta object. Expects data in the format `DD HH:MM:SS.uuuuuu`, `DD HH:MM:SS,uuuuuu`, or as specified by ISO 8601 (e.g. `P4DT1H15M20S` which is equivalent to `4 1:15:20`) or PostgreSQL’s day-time interval format (e.g. `3 days 04:05:06`). |
206
- | timestamp_local | filter | value, fallback (optional) | Converts a UNIX timestamp to the ISO format string representation as date/time in your local timezone. If that fails, returns the `fallback` value, or if omitted raises an error. |
207
- | timestamp_utc | filter | value, fallback (optional) | Converts a UNIX timestamp to the ISO format string representation as date/time in UTC timezone. If that fails, returns the `fallback` value, or if omitted raises an error. |
208
- | timestamp_custom | filter | value, format, local (default true), fallback (optional) | Converts a UNIX timestamp to its string representation based on a custom format. Uses the local timezone by default. If that fails, returns the `fallback` value, or if omitted raises an error. |
229
+ | Name | Type | Arguments | Description |
230
+ | ---------------- | ---------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
231
+ | now | function | | Returns a datetime object that represents the current time in your time zone. |
232
+ | utcnow | function | | Returns a datetime object of the current time in the UTC timezone. |
233
+ | today_at | function, filter | value | Converts a string containing a military time format to a datetime object with today’s date in your time zone. Defaults to midnight (00:00). |
234
+ | as_datetime | function, filter | value, fallback (optional), utc (default true) | Converts a string containing a timestamp, or valid UNIX timestamp, to a datetime object. If that fails, it returns the fallback value or, if omitted, raises an error. When the input is already a datetime object it will be returned as is. in case the input is a datetime.date object, midnight will be added as time. |
235
+ | as_timestamp | function, filter | value, fallback (optional) | Converts a datetime object or string to UNIX timestamp. If that fails, returns the fallback value, or if omitted raises an error. |
236
+ | as_local | function, filter | value | Converts a datetime object to local time. |
237
+ | strptime | function | value, format, fallback (optional), utc (default false) | Parses a string based on a [format](https://d3js.org/d3-time-format#locale_format) and returns a datetime object. If that fails, it returns the default value or, if omitted, raises an error. |
238
+ | relative_time | function, filter | value | Returns a human readable string indicating the differenceb between now and an input past datetime object. Only uses the largest unit (years, months, days, hours, minutes, seconds) rounded. |
239
+ | time_since | function, filter | value, precision (default 1) | Returns a human readable string indicating the difference between now and an input past datetime object. `precision` indicates how many units (years, months, days, hours, minutes, seconds) to use, with the last unit being rounded and 0 being the same as 6. If the input datetime is in the future it returns the input. If the input datetime is not a datetime object it returns nothing. |
240
+ | time_until | function, filter | value, precision (default 1) | Returns a human readable string indicating the difference between now and an input future datetime object. `precision` indicates how many units (years, months, days, hours, minutes, seconds) to use, with the last unit being rounded and 0 being the same as 6. If the input datetime is in the past it returns the input. If the input datetime is not a datetime object it returns nothing. |
241
+ | as_timedelta | function, filter | value | Converts a string to a timedelta object. Expects data in the format `DD HH:MM:SS.uuuuuu`, `DD HH:MM:SS,uuuuuu`, or as specified by ISO 8601 (e.g. `P4DT1H15M20S` which is equivalent to `4 1:15:20`) or PostgreSQL’s day-time interval format (e.g. `3 days 04:05:06`). |
242
+ | timestamp_local | filter | value, fallback (optional) | Converts a UNIX timestamp to the ISO format string representation as date/time in your local timezone. If that fails, returns the `fallback` value, or if omitted raises an error. |
243
+ | timestamp_utc | filter | value, fallback (optional) | Converts a UNIX timestamp to the ISO format string representation as date/time in UTC timezone. If that fails, returns the `fallback` value, or if omitted raises an error. |
244
+ | timestamp_custom | filter | value, format, local (default true), fallback (optional) | Converts a UNIX timestamp to its string representation based on a custom format. Uses the local timezone by default. If that fails, returns the `fallback` value, or if omitted raises an error. |
209
245
 
210
246
  In addition to these functions, you have access to [a datetime library](https://github.com/Nerwyn/ts-py-datetime) which emulates the Python datetime module in TypeScript. You can instantiate `date`, `time`, `datetime`, and `timedelta` objects using the `dt` object. You can then access it's class methods using these objects. To use the static method and constants of these classes, you can reference them directly without prefixing them with `dt`. See the README in the datetime repository linked above for more information on how to use it.
211
247
 
@@ -228,6 +264,12 @@ In addition to these functions, you have access to [a datetime library](https://
228
264
  | to_json | filter | obj, ensure_ascii, pretty_print, sort_keys | Turn an object into a JSON string. `ensure_ascii` converts unicode characters into escape sequences. `pretty_print` formats the output with new lines and an indent of two spaces. `sort_keys` sorts the keys of the JSON object. **Consider using the nunjucks `safe` filter with this, or the nunjucks `dump` filter instead.** |
229
265
  | from_json | filter | value | Parse a string as JSON. |
230
266
 
267
+ ### [Is Defined](https://www.home-assistant.io/docs/configuration/templating/#is-defined)
268
+
269
+ | Name | Type | Arguments | Description |
270
+ | ---------- | ------ | --------- | -------------------------------------------------------------- |
271
+ | is_defined | filter | value | Returns the value if it is defined, otherwise throws an error. |
272
+
231
273
  ### [Distance](https://www.home-assistant.io/docs/configuration/templating/#distance)
232
274
 
233
275
  | Name | Type | Arguments | Description |
@@ -287,10 +329,11 @@ In addition to these functions, you have access to [a datetime library](https://
287
329
 
288
330
  ### [Type Conversions](https://www.home-assistant.io/docs/configuration/templating/#type-conversions)
289
331
 
290
- | Name | Type | Arguments | Description |
291
- | ---- | -------- | --------- | ------------------------------------------------------ |
292
- | set | function | args | Convert a list/array to a set. Removes duplicates. |
293
- | list | function | args | Convert a set to an array. Does not remove duplicates. |
332
+ | Name | Type | Arguments | Description |
333
+ | ---- | ---------------- | --------- | ------------------------------------------------------ |
334
+ | set | function | args | Convert a list/array to a set. Removes duplicates. |
335
+ | list | function | args | Convert a set to an array. Does not remove duplicates. |
336
+ | str | function, filter | value | Return the string representation of the input. |
294
337
 
295
338
  ### [Iterating Multiple Objects](https://www.home-assistant.io/docs/configuration/templating/#iterating-multiple-objects)
296
339
 
@@ -298,6 +341,16 @@ In addition to these functions, you have access to [a datetime library](https://
298
341
  | ---- | -------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------- |
299
342
  | zip | function | args | Use to iterate over multiple collections in one operation. If given one array will perform the opposite action and unzip the list. |
300
343
 
344
+ ### [String filters](https://www.home-assistant.io/docs/configuration/templating/#string-filters)
345
+
346
+ | Name | Type | Arguments | Description |
347
+ | ------------- | ------ | ----------------------------- | -------------------------------------------------------------------------------------------------------- |
348
+ | urlencode | filter | value | Convert an object to a percent-encoded ASCII text string. |
349
+ | slugify | filter | value, separator (default \_) | Convert a given string into a "slug". |
350
+ | ordinal | filter | value | Convert an integer into a number defining a position in a series (e.g. `1st`, `2nd`, `3rd`, `4th`, etc). |
351
+ | base64_encode | filter | value | Encodes a string or bytes to a base 64 string. |
352
+ | base64_decode | filter | value | Decodes a base 64 string to a UTF-8 string. |
353
+
301
354
  ### [Regular Expressions](https://www.home-assistant.io/docs/configuration/templating/#regular-expressions)
302
355
 
303
356
  **NOTE**: The format of regular expressions in nunjucks is different than jinja2. You may want to read the [Nunjucks](https://mozilla.github.io/nunjucks/templating.html#regular-expressions) and [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions) documentation.
@@ -315,10 +368,9 @@ In addition to these functions, you have access to [a datetime library](https://
315
368
 
316
369
  Functions that are not from the Home Assistant templating documentation.
317
370
 
318
- | Name | Type | Arguments | Description |
319
- | ----------- | ---------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
320
- | match_media | function | value | Returns the boolean result of the provided [CSS media query](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_media_queries/Using_media_queries). |
321
- | str | function, filter | value | Return the string representation of the input. |
371
+ | Name | Type | Arguments | Description |
372
+ | ----------- | -------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
373
+ | match_media | function | value | Returns the boolean result of the provided [CSS media query](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_media_queries/Using_media_queries). |
322
374
 
323
375
  [last-commit-shield]: https://img.shields.io/github/last-commit/Nerwyn/ha-nunjucks?style=for-the-badge
324
376
  [commits]: https://github.com/Nerwyn/ha-nunjucks/commits/main
package/dist/filters.js CHANGED
@@ -1,18 +1,19 @@
1
1
  import { area_devices, area_entities, area_id, area_name } from './utils/areas';
2
2
  import { contains } from './utils/contains';
3
- import { device_attr, device_entities, device_id } from './utils/devices';
3
+ import { device_attr, device_entities, device_id, device_name, } from './utils/devices';
4
4
  import { closest } from './utils/distance';
5
- import { floor_areas, floor_id, floor_name } from './utils/floors';
5
+ import { floor_areas, floor_entities, floor_id, floor_name, } from './utils/floors';
6
6
  import { expand } from './utils/groups';
7
7
  import { iif } from './utils/iif';
8
- import { from_json, to_json } from './utils/json';
9
- import { label_areas, label_devices, label_entities, label_id, label_name, labels, } from './utils/labels';
8
+ import { from_json, is_defined, to_json } from './utils/json';
9
+ import { label_areas, label_description, label_devices, label_entities, label_id, label_name, labels, } from './utils/labels';
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
- import { as_datetime, as_local, as_timestamp, time_since, time_until, timestamp_custom, timestamp_local, timestamp_utc, today_at, } from './utils/time';
15
+ import { base64_decode, base64_encode, ordinal, slugify, } from './utils/string_filters';
16
+ import { as_datetime, as_local, as_timedelta, as_timestamp, relative_time, time_since, time_until, timestamp_custom, timestamp_local, timestamp_utc, today_at, } from './utils/time';
16
17
  export function addFilters(env) {
17
18
  for (const func in FILTERS) {
18
19
  env.addFilter(func, function (...args) {
@@ -41,10 +42,12 @@ const HASS_FILTERS = {
41
42
  device_entities,
42
43
  device_attr,
43
44
  device_id,
45
+ device_name,
44
46
  // Floors
45
47
  floor_id,
46
48
  floor_name,
47
49
  floor_areas,
50
+ floor_entities,
48
51
  // Areas
49
52
  area_id,
50
53
  area_name,
@@ -64,19 +67,27 @@ const FILTERS = {
64
67
  // Labels
65
68
  label_id,
66
69
  label_name,
70
+ label_description,
67
71
  // Time
68
72
  today_at,
73
+ as_timedelta,
69
74
  as_datetime,
70
75
  as_timestamp,
71
76
  as_local,
77
+ relative_time,
72
78
  time_since,
73
79
  time_until,
74
80
  timestamp_local,
75
81
  timestamp_utc,
76
82
  timestamp_custom,
83
+ date_translated,
84
+ time_translated,
85
+ datetime_translated,
77
86
  // To/From JSON
78
87
  to_json,
79
88
  from_json,
89
+ // Is Defined
90
+ is_defined,
80
91
  // Distance
81
92
  closest,
82
93
  // Contains
@@ -107,10 +118,17 @@ const FILTERS = {
107
118
  ord,
108
119
  multiply,
109
120
  add,
121
+ number_translated,
122
+ // Type conversions
123
+ str,
124
+ // String filters
125
+ // urlencode filter is built into nunjucks
126
+ slugify,
127
+ ordinal,
128
+ base64_encode,
129
+ base64_decode,
110
130
  // Regular Expressions
111
131
  regex_replace,
112
132
  regex_findall,
113
133
  regex_findall_index,
114
- // Miscellaneous
115
- str,
116
134
  };
package/dist/globals.js CHANGED
@@ -1,17 +1,18 @@
1
1
  import { area_devices, area_entities, area_id, area_name, areas, } from './utils/areas';
2
- import { device_attr, device_entities, device_id, is_device_attr, } from './utils/devices';
2
+ import { device_attr, device_entities, device_id, device_name, is_device_attr, } from './utils/devices';
3
3
  import { closest, distance } from './utils/distance';
4
4
  import { is_hidden_entity } from './utils/entities';
5
- import { floor_areas, floor_id, floor_name, floors } from './utils/floors';
5
+ import { floor_areas, floor_entities, floor_id, floor_name, floors, } from './utils/floors';
6
6
  import { expand } from './utils/groups';
7
7
  import { iif } from './utils/iif';
8
8
  import { integration_entities } from './utils/integrations';
9
- import { label_areas, label_devices, label_entities, label_id, label_name, labels, } from './utils/labels';
9
+ import { label_areas, label_description, 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
- import { as_datetime, as_local, as_timedelta, as_timestamp, now, strptime, time_since, time_until, today_at, utcnow, } from './utils/time';
14
+ import { slugify } from './utils/string_filters';
15
+ import { as_datetime, as_local, as_timedelta, as_timestamp, now, relative_time, strptime, time_since, time_until, today_at, utcnow, } from './utils/time';
15
16
  import { list, set } from './utils/type_conversions';
16
17
  import { zip } from './utils/zip';
17
18
  import dt, { date, datetime, time, timedelta } from 'ts-py-datetime';
@@ -56,11 +57,13 @@ const HASS_GLOBALS = {
56
57
  device_attr,
57
58
  is_device_attr,
58
59
  device_id,
60
+ device_name,
59
61
  // Floors
60
62
  floors,
61
63
  floor_id,
62
64
  floor_name,
63
65
  floor_areas,
66
+ floor_entities,
64
67
  // Areas
65
68
  areas,
66
69
  area_id,
@@ -84,6 +87,7 @@ const GLOBALS = {
84
87
  // Labels
85
88
  label_id,
86
89
  label_name,
90
+ label_description,
87
91
  // Time
88
92
  now,
89
93
  utcnow,
@@ -92,9 +96,13 @@ const GLOBALS = {
92
96
  as_timestamp,
93
97
  as_local,
94
98
  strptime,
99
+ relative_time,
95
100
  time_since,
96
101
  time_until,
97
102
  as_timedelta,
103
+ date_translated,
104
+ time_translated,
105
+ datetime_translated,
98
106
  // Numeric,
99
107
  float,
100
108
  is_number,
@@ -114,14 +122,17 @@ const GLOBALS = {
114
122
  average,
115
123
  median,
116
124
  statistical_mode,
125
+ number_translated,
117
126
  // Type Conversions
118
127
  set,
119
128
  list,
129
+ str,
120
130
  // Iterating Multiple Objects
121
131
  zip,
132
+ // String fitlers
133
+ slugify,
122
134
  // Miscellaneous
123
135
  match_media,
124
- str,
125
136
  };
126
137
  const CONST_GLOBALS = {
127
138
  e,
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,74 @@ 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
+ window.haNunjucks.ordinalFormat = new Intl.PluralRules('en-US', // ha.hass.language, // Use english for proper numeric suffixes
29
+ { type: 'ordinal' });
30
+ // Label registry and states object
31
+ window.haNunjucks.hass = ha.hass;
32
+ fetchLabelRegistry();
33
+ buildStatesObject();
34
+ };
35
+ registrySetup();
36
+ // Initialize global ha-nunjucks environment
9
37
  nunjucks.installJinjaCompat();
10
38
  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
39
  }
21
40
  /**
22
41
  * Render a Home Assistant template string using nunjucks
23
42
  * @param {HomeAssistant} hass The Home Assistant object
24
43
  * @param {string} str The template string to render
25
44
  * @param {object} [context] Additional context to expose to nunjucks
45
+ * @param {boolean} [validate=true] Validate that the input contains a template.
26
46
  * @returns {string | boolean} The rendered template string if a string was provided, otherwise the unaltered input
27
47
  */
28
- export function renderTemplate(hass, str, context) {
29
- if (!window.haNunjucks?.labelRegistry) {
30
- fetchLabelRegistry(hass);
48
+ export function renderTemplate(hass, str, context, validate = true) {
49
+ if (validate && !hasTemplate(str)) {
50
+ return str;
31
51
  }
32
52
  window.haNunjucks.hass = hass;
33
- if (typeof str == 'string' &&
34
- ((str.includes('{{') && str.includes('}}')) ||
35
- (str.includes('{%') && str.includes('%}')))) {
36
- str = window.haNunjucks.env
37
- .renderString(structuredClone(str), {
38
- hass,
39
- _states: buildStatesObject(),
40
- ...context,
41
- })
42
- .trim();
43
- if ([undefined, null, 'undefined', 'null', 'None'].includes(str)) {
44
- return '';
45
- }
46
- const lowerStr = str.toLowerCase();
47
- if (['true', 'false'].includes(lowerStr)) {
48
- return lowerStr == 'true';
49
- }
50
- return str;
53
+ buildStatesObject();
54
+ str = window.haNunjucks.env
55
+ .renderString(structuredClone(str), {
56
+ hass,
57
+ _states: window.haNunjucks.states,
58
+ ...context,
59
+ })
60
+ .trim();
61
+ if ([undefined, null, 'undefined', 'null', 'None'].includes(str)) {
62
+ return '';
63
+ }
64
+ const lowerStr = str.toLowerCase();
65
+ if (['true', 'false'].includes(lowerStr)) {
66
+ return lowerStr == 'true';
51
67
  }
52
68
  return str;
53
69
  }
70
+ const hasTemplateRegex = /{{.*?}}|{%.*?%}/;
71
+ /**
72
+ * Test if the input contains a valid template
73
+ * @param {any} str the variable to check
74
+ * @returns if the input is a string that contains a template
75
+ */
76
+ export function hasTemplate(str) {
77
+ return hasTemplateRegex.test(str);
78
+ }
@@ -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
+ }
@@ -20,7 +20,8 @@ export function area_id(hass, lookup_value) {
20
20
  return hass.devices[lookup_value].area_id;
21
21
  }
22
22
  for (const areaId in hass.areas) {
23
- if (hass.areas[areaId].name == lookup_value) {
23
+ if (hass.areas[areaId].name == lookup_value ||
24
+ hass.areas[areaId].aliases?.includes(lookup_value)) {
24
25
  return areaId;
25
26
  }
26
27
  }
@@ -57,12 +58,15 @@ export function area_entities(hass, area_name_or_id) {
57
58
  try {
58
59
  const entityIds = [];
59
60
  if (area_name_or_id) {
61
+ let areaId = area_name_or_id;
62
+ if (!hass.areas[area_name_or_id]) {
63
+ areaId = area_id(hass, area_name_or_id) ?? area_name_or_id;
64
+ }
60
65
  const deviceIds = area_devices(hass, area_name_or_id);
61
- for (const deviceId of deviceIds) {
62
- for (const entityId in hass.entities) {
63
- if (hass.entities[entityId].device_id == deviceId) {
64
- entityIds.push(entityId);
65
- }
66
+ for (const entityId in hass.entities) {
67
+ if (deviceIds.includes(hass.entities[entityId].device_id) ||
68
+ hass.entities[entityId].area_id == areaId) {
69
+ entityIds.push(entityId);
66
70
  }
67
71
  }
68
72
  entityIds.sort();
@@ -79,7 +83,8 @@ export function area_devices(hass, area_name_or_id) {
79
83
  if (area_name_or_id) {
80
84
  if (!(area_name_or_id in hass.areas)) {
81
85
  for (const areaId in hass.areas) {
82
- if (hass.areas[areaId].name == area_name_or_id) {
86
+ if (hass.areas[areaId].name == area_name_or_id ||
87
+ hass.areas[areaId].aliases?.includes(area_name_or_id)) {
83
88
  area_name_or_id = areaId;
84
89
  break;
85
90
  }
@@ -4,3 +4,4 @@ export declare function device_entities(hass: HomeAssistant, device_id: string):
4
4
  export declare function device_attr(hass: HomeAssistant, device_or_entity_id: string, attr_name: keyof DeviceRegistryEntry): any;
5
5
  export declare function is_device_attr(hass: HomeAssistant, device_or_entity_id: string, attr_name: keyof DeviceRegistryEntry, attr_value: string): boolean;
6
6
  export declare function device_id(hass: HomeAssistant, entity_id: string): string | undefined;
7
+ export declare function device_name(hass: HomeAssistant, lookup_value: string): string | null | undefined;
@@ -55,3 +55,18 @@ export function device_id(hass, entity_id) {
55
55
  return undefined;
56
56
  }
57
57
  }
58
+ export function device_name(hass, lookup_value) {
59
+ try {
60
+ if (hass.entities[lookup_value]) {
61
+ lookup_value = hass.entities[lookup_value].device_id;
62
+ }
63
+ const device = hass.devices[lookup_value];
64
+ if (device) {
65
+ return device.name_by_user ?? device.name;
66
+ }
67
+ return undefined;
68
+ }
69
+ catch {
70
+ return undefined;
71
+ }
72
+ }
@@ -3,3 +3,4 @@ export declare function floors(hass: HomeAssistant): string[];
3
3
  export declare function floor_id(hass: HomeAssistant, lookup_value: string): string | null | undefined;
4
4
  export declare function floor_name(hass: HomeAssistant, lookup_value: string): string | undefined;
5
5
  export declare function floor_areas(hass: HomeAssistant, floor_name_or_id: string): string[];
6
+ export declare function floor_entities(hass: HomeAssistant, floor_name_or_id: string): string[];
@@ -4,25 +4,37 @@ export function floors(hass) {
4
4
  export function floor_id(hass, lookup_value) {
5
5
  try {
6
6
  let areaId = lookup_value;
7
+ // Entity ID
7
8
  if (hass.entities[lookup_value]) {
8
9
  areaId = hass.entities[lookup_value].area_id ?? areaId;
9
10
  lookup_value =
10
11
  hass.entities[lookup_value].device_id ?? lookup_value;
11
12
  }
12
13
  if (lookup_value) {
14
+ // Device ID
13
15
  if (hass.devices[lookup_value]) {
14
16
  areaId = hass.devices[lookup_value].area_id ?? areaId;
15
17
  }
18
+ // Area ID
16
19
  if (hass.areas[areaId]) {
17
20
  return hass.areas[areaId].floor_id;
18
21
  }
19
22
  else {
23
+ // Area name or alias
20
24
  for (const areaId in hass.areas) {
21
- if (hass.areas[areaId].name == lookup_value) {
25
+ if (hass.areas[areaId].name == lookup_value ||
26
+ hass.areas[areaId].aliases?.includes(lookup_value)) {
22
27
  return hass.areas[areaId].floor_id;
23
28
  }
24
29
  }
25
30
  }
31
+ // Floor name or alias
32
+ for (const floorId in hass.floors) {
33
+ if (hass.floors[floorId].name == lookup_value ||
34
+ hass.floors[floorId].aliases?.includes(lookup_value)) {
35
+ return floorId;
36
+ }
37
+ }
26
38
  }
27
39
  return undefined;
28
40
  }
@@ -50,7 +62,8 @@ export function floor_areas(hass, floor_name_or_id) {
50
62
  }
51
63
  else {
52
64
  for (const id in hass.floors) {
53
- if (hass.floors[id].name == floor_name_or_id) {
65
+ if (hass.floors[id].name == floor_name_or_id ||
66
+ hass.floors[id].aliases?.includes(floor_name_or_id)) {
54
67
  floorId = id;
55
68
  break;
56
69
  }
@@ -71,3 +84,22 @@ export function floor_areas(hass, floor_name_or_id) {
71
84
  return [];
72
85
  }
73
86
  }
87
+ export function floor_entities(hass, floor_name_or_id) {
88
+ try {
89
+ const res = new Set();
90
+ const areas = floor_areas(hass, floor_name_or_id);
91
+ for (const entityId in hass.entities) {
92
+ if (areas.includes(hass.entities[entityId].area_id)) {
93
+ res.add(entityId);
94
+ for (const subEntityId of hass.states[entityId]?.attributes
95
+ ?.entity_id ?? []) {
96
+ res.add(subEntityId);
97
+ }
98
+ }
99
+ }
100
+ return Array.from(res);
101
+ }
102
+ catch {
103
+ return [];
104
+ }
105
+ }
@@ -1,2 +1,3 @@
1
1
  export declare function to_json(obj: object, ensure_ascii?: boolean | Record<string, boolean>, pretty_print?: boolean, sort_keys?: boolean): string;
2
2
  export declare function from_json(value: string): any;
3
+ export declare function is_defined(value: any): any;
@@ -21,3 +21,9 @@ export function to_json(obj, ensure_ascii = false, pretty_print = false, sort_ke
21
21
  export function from_json(value) {
22
22
  return JSON.parse(value);
23
23
  }
24
+ export function is_defined(value) {
25
+ if (value == undefined) {
26
+ throw Error('UndefinedError: input is undefined');
27
+ }
28
+ return value;
29
+ }
@@ -1,8 +1,9 @@
1
1
  import { HomeAssistant } from '../models/interfaces/hass';
2
- export declare function fetchLabelRegistry(hass: HomeAssistant): Promise<void>;
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;
6
+ export declare function label_description(lookup_value: string): string | undefined;
6
7
  export declare function label_areas(hass: HomeAssistant, label_name_or_id: string): string[];
7
8
  export declare function label_devices(hass: HomeAssistant, label_name_or_id: string): string[];
8
9
  export declare function label_entities(hass: HomeAssistant, label_name_or_id: string): string[];
@@ -1,13 +1,10 @@
1
- export async function fetchLabelRegistry(hass) {
2
- if (hass.connection) {
3
- window.haNunjucks.labelRegistry = {};
4
- const labels = await hass.connection.sendMessagePromise({
5
- type: 'config/label_registry/list',
6
- });
7
- labels.sort((ent1, ent2) => ent1.name.localeCompare(ent2.name));
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) {
@@ -35,6 +32,9 @@ export function label_id(lookup_value) {
35
32
  export function label_name(lookup_value) {
36
33
  return window.haNunjucks.labelRegistry[lookup_value]?.name;
37
34
  }
35
+ export function label_description(lookup_value) {
36
+ return window.haNunjucks.labelRegistry[lookup_value]?.description;
37
+ }
38
38
  export function label_areas(hass, label_name_or_id) {
39
39
  try {
40
40
  const areaIds = [];
@@ -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, precision?: 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,40 @@ 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, precision) {
30
+ value = Number(value);
31
+ if (isNaN(value)) {
32
+ return value;
33
+ }
34
+ if (precision) {
35
+ return value.toLocaleString(window.haNunjucks.hass.language, {
36
+ minimumFractionDigits: precision,
37
+ maximumFractionDigits: precision,
38
+ });
39
+ }
40
+ return window.haNunjucks.numberFormat.format(value);
41
+ }
42
+ export function date_translated(value) {
43
+ try {
44
+ return window.haNunjucks.dateFormat.format(value.jsDate);
45
+ }
46
+ catch {
47
+ return value;
48
+ }
49
+ }
50
+ export function time_translated(value) {
51
+ try {
52
+ return window.haNunjucks.timeFormat.format(value.jsDate);
53
+ }
54
+ catch {
55
+ return value;
56
+ }
57
+ }
58
+ export function datetime_translated(value) {
59
+ try {
60
+ return window.haNunjucks.datetimeFormat.format(value.jsDate);
61
+ }
62
+ catch {
63
+ return value;
64
+ }
65
+ }
@@ -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>>;
@@ -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
- }
@@ -0,0 +1,4 @@
1
+ export declare function slugify(str: string, separator?: string): string;
2
+ export declare function ordinal(num: number): string;
3
+ export declare function base64_encode(value: string): string;
4
+ export declare function base64_decode(value: string): string;
@@ -0,0 +1,27 @@
1
+ import slugifyLib from 'slugify';
2
+ export function slugify(str, separator = '_') {
3
+ return slugifyLib(str, {
4
+ replacement: separator,
5
+ lower: true,
6
+ strict: true,
7
+ });
8
+ }
9
+ export function ordinal(num) {
10
+ if (isNaN(num)) {
11
+ throw Error('Input must be a number');
12
+ }
13
+ const suffixes = {
14
+ one: 'st',
15
+ two: 'nd',
16
+ few: 'rd',
17
+ other: 'th',
18
+ };
19
+ const suffix = suffixes[window.haNunjucks.ordinalFormat.select(num)] || 'th';
20
+ return `${num}${suffix}`;
21
+ }
22
+ export function base64_encode(value) {
23
+ return btoa(value);
24
+ }
25
+ export function base64_decode(value) {
26
+ return atob(value);
27
+ }
@@ -6,6 +6,7 @@ export declare function as_datetime(value: number | string | datetime | date, fa
6
6
  export declare function as_timestamp(value: number | string | datetime | date, fallback?: string): string | number;
7
7
  export declare function as_local(value: datetime): datetime;
8
8
  export declare function strptime(value: string, format: string, fallback?: datetime | string | undefined | Record<string, datetime | string | boolean>, utc?: boolean): string | datetime | Record<string, string | boolean | datetime>;
9
+ export declare function relative_time(input: datetime): string | datetime;
9
10
  export declare function time_since(input: datetime, precision?: number): string | datetime;
10
11
  export declare function time_until(input: datetime, precision?: number): string | datetime;
11
12
  export declare function as_timedelta(value: string): timedelta | null;
@@ -130,7 +130,7 @@ function timeDiff(input, precision = 1, until = false) {
130
130
  if (diff <= 0) {
131
131
  return input;
132
132
  }
133
- if (precision == 0 || precision > 6) {
133
+ if (precision < 1 || precision > 6) {
134
134
  precision = 6;
135
135
  }
136
136
  const toSeconds = {
@@ -143,23 +143,29 @@ function timeDiff(input, precision = 1, until = false) {
143
143
  };
144
144
  const units = Object.keys(toSeconds);
145
145
  let res = '';
146
- let startRes = false;
147
- for (let i = 0; i < precision; i++) {
146
+ let p = 0;
147
+ for (let i = 0; i < units.length; i++) {
148
148
  let value = diff / toSeconds[units[i]];
149
- if (i == precision - 1) {
149
+ if (i == p - 1) {
150
150
  value = Math.round(value);
151
151
  }
152
152
  else {
153
153
  value = Math.floor(value);
154
154
  }
155
- if (startRes || value > 0) {
156
- startRes = true;
155
+ if (value > 0) {
156
+ p += 1;
157
157
  res += ` ${value} ${units[i]}${value != 1 ? 's' : ''}`;
158
158
  diff -= value * toSeconds[units[i]];
159
159
  }
160
+ if (res.length && precision == p) {
161
+ break;
162
+ }
160
163
  }
161
164
  return res.trim();
162
165
  }
166
+ export function relative_time(input) {
167
+ return timeDiff(input);
168
+ }
163
169
  export function time_since(input, precision = 1) {
164
170
  return timeDiff(input, precision);
165
171
  }
@@ -174,12 +180,10 @@ export function as_timedelta(value) {
174
180
  /^\d*\.?\d*$/.test(value)) {
175
181
  let daysStr, timeStr;
176
182
  if (value.includes(' ')) {
177
- if (value.includes('days')) {
178
- [daysStr, timeStr] = value.split(' days ');
179
- }
180
- else {
181
- [daysStr, timeStr] = value.split(' ');
182
- }
183
+ [daysStr, timeStr] = value
184
+ .replace(/day(s?)/g, '')
185
+ .replace(/\s+/g, ' ')
186
+ .split(' ');
183
187
  }
184
188
  else {
185
189
  daysStr = 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ha-nunjucks",
3
- "version": "1.4.0",
3
+ "version": "1.6.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"
@@ -17,7 +17,7 @@
17
17
  "*": "prettier -w"
18
18
  },
19
19
  "author": "Nerwyn",
20
- "license": "ISC",
20
+ "license": "Apache-2.0",
21
21
  "repository": {
22
22
  "type": "git",
23
23
  "url": "git+https://github.com/Nerwyn/ha-nunjucks.git"
@@ -29,6 +29,7 @@
29
29
  "dependencies": {
30
30
  "home-assistant-js-websocket": "latest",
31
31
  "nunjucks": "latest",
32
+ "slugify": "latest",
32
33
  "ts-py-datetime": "latest"
33
34
  },
34
35
  "devDependencies": {
@@ -39,14 +40,14 @@
39
40
  "eslint": "latest",
40
41
  "eslint-config-prettier": "latest",
41
42
  "eslint-plugin-prettier": "latest",
42
- "jsdom-global": "latest",
43
+ "global-jsdom": "latest",
43
44
  "lint-staged": "latest",
44
45
  "mocha": "latest",
45
46
  "prettier": "latest",
46
47
  "prettier-plugin-organize-imports": "latest",
47
48
  "ts-loader": "latest",
48
49
  "ts-mocha": "latest",
49
- "ts-node": "^10.9.2",
50
+ "ts-node": "latest",
50
51
  "tsx": "latest",
51
52
  "typescript": "latest"
52
53
  }