home-assistant-javascript-templates 3.1.1 → 5.0.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
@@ -34,11 +34,17 @@ pnpm add home-assistant-javascript-templates
34
34
  ```javascript
35
35
  const HomeAssistantJavaScriptTemplates = require('home-assistant-javascript-templates');
36
36
 
37
- const renderer = new HomeAssistantJavaScriptTemplates(
37
+ const haJsTemplates = new HomeAssistantJavaScriptTemplates(
38
38
  document.querySelector('home-assistant')
39
39
  );
40
40
 
41
- renderer.renderTemplate('... template string ...');
41
+ haJsTemplates.getRenderer()
42
+ then((renderer) => {
43
+ renderer.renderTemplate('... template string ...');
44
+ renderer.trackTemplate('... template string ...', () => {
45
+ // execute this function every time that en entity used in the template changes
46
+ });
47
+ });
42
48
  ```
43
49
 
44
50
  #### Usage with ES6 modules
@@ -46,46 +52,55 @@ renderer.renderTemplate('... template string ...');
46
52
  ```javascript
47
53
  import HomeAssistantJavaScriptTemplates from 'home-assistant-javascript-templates';
48
54
 
49
- const renderer = new HomeAssistantJavaScriptTemplates(
55
+ const haJsTemplates = new HomeAssistantJavaScriptTemplates(
50
56
  document.querySelector('home-assistant')
51
57
  );
52
58
 
53
- renderer.renderTemplate('... template string ...');
59
+ haJsTemplates.getRenderer()
60
+ then((renderer) => {
61
+ renderer.renderTemplate('... template string ...');
62
+ renderer.trackTemplate('... template string ...', () => {
63
+ // execute this function every time that en entity used in the template changes
64
+ });
65
+ });
54
66
  ```
55
67
 
56
68
  ## API
57
69
 
58
- The package exposes a class that needs to be instantiated and is this instance the one that you need to use in your code.
70
+ The package exposes a class that needs to be instantiated and the resolved promise that returns the `getRenderer` method of this instance is what you need to use in your code to render `JavaScript` templates.
59
71
 
60
72
  ### HomeAssistantJavaScriptTemplates class
61
73
 
62
74
  Main class of the library, it is the `default` export in the package.
63
75
 
64
76
  ```typescript
65
- new HomeAssistantJavaScriptTemplates(ha, throwErrors = false);
77
+ new HomeAssistantJavaScriptTemplates(
78
+ ha,
79
+ options
80
+ );
66
81
  ```
67
82
 
68
- | Parameter | Optional | Description |
69
- | ------------- | ------------- | -------------------------------------------------- |
70
- | `ha` | no | An HTML element that has the `hass` object as a property (e.g. the `home-assistant` custom element). |
71
- | `throwErrors` | yes | Indicates if the library should throw if the template contains any error. If not it will log the errors as a warning in the console and return `undefined` instead. |
83
+ | Parameter | Optional | Description |
84
+ | ------------------ | ------------- | -------------------------------------------------- |
85
+ | `ha` | no | An HTML element that has the `hass` object as a property (e.g. the `home-assistant` custom element). |
86
+ | `options` | yes | An object containing the configuration options. |
72
87
 
73
- ### Properties
88
+ #### Configuration options
74
89
 
75
- #### tracked
90
+ | Parameter | Optional | Default | Description |
91
+ | ------------------ | ------------- | ------- | -------------------------------------------------- |
92
+ | `throwErrors` | yes | false | Indicates if the library should throw if the template contains any error. If not, it will log the errors as a warning in the console and return `undefined` instead. |
93
+ | `throwWarnings` | yes | true | Indicates if the library should throw warnings in the console, either when there is an error in the templates and `throwErrors` is configured in `false`, or when a non-existent entity or domain is used in the templates. |
76
94
 
77
- ```typescript
78
- interface Tracked {
79
- entities: string[];
80
- domains: string[];
81
- }
95
+ ### Methods
82
96
 
83
- get tracked(): Tracked
84
- ```
97
+ #### getRenderer
98
+
99
+ Returns a `Promise` than once it resolved returns an instance of the [HomeAssistantJavaScriptTemplatesRenderer](#homeassistantjavascripttemplatesrenderer-class) class.
85
100
 
86
- This property will return an object with two properties (`entities` and `domains`). Each of these properties will be an array containing the entities or ids that have been tracked when the templates have been rendered. If some domain or entity was not reached because it was inside a condition that never met, then it will not be included in the `tracked` property. Only those entities or domains that were called during the rendering by the code using [states](#states), [is_state](#is_state), [state_attr](#state_attr), [is_state_attr](#is_state_attr) or [has_value](#has_value) will be included.
101
+ ### HomeAssistantJavaScriptTemplatesRenderer class
87
102
 
88
- >Note: take into account that the domains will be only tracked if the `states` object is used accesing a domain. For example `states('device_tracker.paulus')` or `states['device_tracker.paulus']` will track the entity `device_tracker.paulus` but not the domain `device_tracker` but `states.device_tracker.paulus` will track both, the domain `device_tracker` and the entity `device_tracker.paulus`. The rest of the methods will track only entities.
103
+ This class is only exported as a type in the package, you cannot import it directly. An instance of this class will be returned by the promise that is returned by the [getRenderer method](#getrenderer) of the [HomeAssistantJavaScriptTemplates class](#homeassistantjavascripttemplates-class).
89
104
 
90
105
  ### Methods
91
106
 
@@ -95,31 +110,28 @@ This property will return an object with two properties (`entities` and `domains
95
110
  renderTemplate(template: string): any
96
111
  ```
97
112
 
98
- This is the main method to render `JavaScript` templates, it needs a string as a parameter. Inside this string you can use [several objects and methods](#objects-and-methods-available-in-the-templates). It returns whatever the `JavaScript` code returns, because of that it is typed as `any`.
113
+ This method renders a `JavaScript` template and return its result. It needs a string as a parameter. Inside this string you can use [several objects and methods](#objects-and-methods-available-in-the-templates). It returns whatever the `JavaScript` code returns, because of that it is typed as `any`.
99
114
 
100
- #### cleanTrackedEntities
115
+ #### trackTemplate
101
116
 
102
117
  ```typescript
103
- cleanTrackedEntities(): void
118
+ trackTemplate(
119
+ template: string,
120
+ renderingFunction: (result?: any) => void
121
+ ): void
104
122
  ```
105
123
 
106
- This method will clean all the tracked entities until the moment, so after being called, the `tracked` property will return an empty array as `entities`.
124
+ This method registers a template tracking. It executes the `renderingFunction` sent to the method with the result of the rendered `template` and will execute `renderingFunction` with an updated result of the rendered `template` every time that the entities used in the template update. You can use [several objects and methods](#objects-and-methods-available-in-the-templates) inside the `template` string.
107
125
 
108
- #### cleanTrackedDomains
109
-
110
- ```typescript
111
- cleanTrackedDomains(): void
112
- ```
113
-
114
- This method will clean all the tracked domains until the moment, so after being called, the `tracked` property will return an empty array as `domains`.
126
+ If some entity was not reached in the template code because it was inside a condition that never met, then it will not be tracked, so if its state changes it will not trigger the `renderingFunction`. Only those entities that were called during the rendering using [states](#states), [is_state](#is_state), [state_attr](#state_attr), [is_state_attr](#is_state_attr), [has_value](#has_value) [entities](#entities), [entity_prop](#entity_prop), [is_entity_prop](#is_entity_prop) or [device_id](#device_id) will be included.
115
127
 
116
128
  #### cleanTracked
117
129
 
118
130
  ```typescript
119
- cleanTracked(): void
131
+ cleanTracked(entityId?: string): void
120
132
  ```
121
133
 
122
- This method will clean all the tracked entities and domains until the moment. It is the same as calling `cleanTrackedEntities` and `cleanTrackedDomains` consecutively.
134
+ This method will clean the template tracking for a specific entity or will clean all the template trackings if no entity id is specified.
123
135
 
124
136
  ### Objects and methods available in the templates
125
137
 
@@ -129,7 +141,7 @@ The `hass` object
129
141
 
130
142
  #### states
131
143
 
132
- `states` could be used in two ways, as a function or as an object. When using it as function it only allows an entity id (containing the domain) as a parameter and it will return the state of that entity. As an object it allows you to access a domain or the full entity id.
144
+ `states` could be used in two ways, as a function or as an object. When using it as function it only allows an entity id (containing the domain) as a parameter and it will return the state of that entity. As an object it allows you to access a domain or the full entity state object.
133
145
 
134
146
  >Note: If you try to use `states` as a function sending a domain it will throw an error.
135
147
 
@@ -140,7 +152,7 @@ states('device_tracker.paulus') // returns the state of the entity id 'device_tr
140
152
  // Using states as an object
141
153
  states['device_tracker.paulus'].state // returns the state of the entity id 'device_tracker.paulus'
142
154
  states.device_tracker.paulus.state // returns the state of the entity id 'device_tracker.paulus'
143
- states.device_tracker // returns an object constaining all the entities of the 'device_tracker' domain
155
+ states.device_tracker // returns an object containing all the entities states of the 'device_tracker' domain
144
156
  ```
145
157
 
146
158
  >Note: Avoid using `states['device_tracker.paulus'].state` or `states.device_tracker.paulus.state`, instead use `states('device_tracker.paulus')` which will return `undefined` if the device id doesn‘t exist or the entity isn’t ready yet (the former will throw an error). If you still want to use them it is advisable to use the [Optional chaining operator], e.g. `states['device_tracker.paulus']?.state` or `states.device_tracker?.paulus?.state`.
@@ -177,6 +189,51 @@ Method to test if the given entity is not unknown or unavailable. It returns a `
177
189
  has_value('sensor.my_sensor')
178
190
  ```
179
191
 
192
+ #### entities
193
+
194
+ `entities` could be used in two ways, as a function or as an object.
195
+
196
+ ```javascript
197
+ // Using entities as a function
198
+ entities() // return all the entities
199
+ entities('device_tracker') // returns an object containing all the entities of the 'device_tracker' domain
200
+ entities('device_tracker.paulus') // returns the entity 'device_tracker.paulus'
201
+
202
+ // Using entities as an object
203
+ entities.device_tracker // returns an object containing all the entities of the 'device_tracker' domain
204
+ entities['device_tracker.paulus'] // returns the entity 'device_tracker.paulus'
205
+ entities.device_tracker.paulus // returns the entity 'device_tracker.paulus'
206
+ ```
207
+
208
+ #### entity_prop
209
+
210
+ Method that returns the value of a property of an entity or `undefined` if it doesn’t exist.
211
+
212
+ ```javascript
213
+ entity_prop('device_tracker.paulus', 'platform')
214
+ ```
215
+
216
+ #### is_entity_prop
217
+
218
+ Method to test if the value of an entity property matches a value. It returns a `boolean`, if the entity id or the property don‘t exist it returns `false`.
219
+
220
+ ```javascript
221
+ is_entity_prop('device_tracker.paulus', 'platform', 'hacs')
222
+ ```
223
+
224
+ #### devices
225
+
226
+ `devices` could be used in two ways, as a function or as an object.
227
+
228
+ ```javascript
229
+ // Using devices as a function
230
+ devices() // returns all the devices
231
+ devices('706ad0ebe27e105d7cd0b73386deefdd') // returns the device that matches the device id
232
+
233
+ // Using devices as an object
234
+ devices['706ad0ebe27e105d7cd0b73386deefdd'] // returns the device that matches the device id
235
+ ```
236
+
180
237
  #### device_attr
181
238
 
182
239
  Method that returns the value of an attribute for the given device id or `undefined` if it doesn’t exist.
@@ -271,6 +328,14 @@ Property to return if the user logged in in Home Assistant is the owner. It retu
271
328
  user_is_owner
272
329
  ```
273
330
 
331
+ #### user_agent
332
+
333
+ Property to return the user agent of the browser in which Home Assistant is running.
334
+
335
+ ```javascript
336
+ user_agent
337
+ ```
338
+
274
339
  ## Examples
275
340
 
276
341
  #### Get a device attribute and return a formatted text with it
@@ -278,7 +343,7 @@ user_is_owner
278
343
  ```javascript
279
344
  import HomeAssistantJavaScriptTemplates from 'home-assistant-javascript-templates';
280
345
 
281
- const renderer = new HomeAssistantJavaScriptTemplates(
346
+ const haJsTemplates = new HomeAssistantJavaScriptTemplates(
282
347
  document.querySelector('home-assistant')
283
348
  );
284
349
 
@@ -288,28 +353,43 @@ const renderer = new HomeAssistantJavaScriptTemplates(
288
353
  * Return the value of the attribute prefixed with "sn: "
289
354
  * It will return something like "sn: 123456"
290
355
  */
291
- renderer.renderTemplate(`
292
- const deviceId = device_id("binary_sensor.koffiezetapparaat_aan");
293
- const serialNumber = device_attr(deviceId, "serial_number");
294
- return "sn:" + serialNumber;
295
- `);
356
+ haJsTemplates.getRenderer()
357
+ .then((renderer) => {
358
+ const result = renderer.renderTemplate(`
359
+ const deviceId = device_id("binary_sensor.koffiezetapparaat_aan");
360
+ const serialNumber = device_attr(deviceId, "serial_number");
361
+ return "sn:" + serialNumber;
362
+ `);
363
+ console.log(result);
364
+ });
365
+
296
366
  ```
297
367
 
298
- #### Get all the available updates
368
+ #### Get all the available updates and update an HTML element with the result with entity changes
299
369
 
300
370
  ```javascript
301
371
  import HomeAssistantJavaScriptTemplates from 'home-assistant-javascript-templates';
302
372
 
303
- const renderer = new HomeAssistantJavaScriptTemplates(
373
+ const haJsTemplates = new HomeAssistantJavaScriptTemplates(
304
374
  document.querySelector('home-assistant')
305
375
  );
306
376
 
307
- renderer.renderTemplate(`
308
- const udatesEntities = states.update;
309
- const updateEntitiesValues = Object.values(udatesEntities);
310
- const updatesEntitiesOn = updateEntitiesValues.filter((entity) => entity.state === 'on');
311
- return updatesEntitiesOn.length;
312
- `);
377
+ haJsTemplates.getRenderer()
378
+ .then((renderer) => {
379
+ const element = document.querySelector('#my-element');
380
+ renderer.trackTemplate(
381
+ `
382
+ const udatesEntities = states.update;
383
+ const updateEntitiesValues = Object.values(udatesEntities);
384
+ const updatesEntitiesOn = updateEntitiesValues.filter((entity) => entity.state === 'on');
385
+ return updatesEntitiesOn.length;
386
+ `,
387
+ (result) => {
388
+ element.innerHTML = result;
389
+ }
390
+ );
391
+ });
392
+
313
393
  ```
314
394
 
315
395
  [Optional chaining operator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining
@@ -1,3 +1,8 @@
1
+ interface Options {
2
+ throwErrors?: boolean;
3
+ throwWarnings?: boolean;
4
+ }
5
+ type RenderingFunction = (result?: any) => void;
1
6
  interface Area {
2
7
  area_id: string;
3
8
  name: string;
@@ -15,6 +20,7 @@ interface State {
15
20
  interface Entity {
16
21
  area_id: string | null;
17
22
  device_id: string;
23
+ [key: string]: unknown;
18
24
  }
19
25
  interface User {
20
26
  name: string;
@@ -31,18 +37,33 @@ interface Hass {
31
37
  interface HomeAssistant extends HTMLElement {
32
38
  hass: Hass;
33
39
  }
34
- interface Tracked {
35
- entities: string[];
36
- domains: string[];
40
+ interface HassConnection {
41
+ conn: {
42
+ subscribeMessage: <T>(callback: (response: T) => void, options: Record<string, unknown>) => void;
43
+ };
37
44
  }
38
- declare class HomeAssistantJavaScriptTemplates {
39
- constructor(ha: HomeAssistant, throwErrors?: boolean);
45
+ declare global {
46
+ interface Window {
47
+ hassConnection: Promise<HassConnection>;
48
+ }
49
+ }
50
+ declare class HomeAssistantJavaScriptTemplatesRenderer {
51
+ constructor(ha: HomeAssistant, options: Options);
52
+ private _throwErrors;
53
+ private _throwWarnings;
54
+ private _subscriptions;
40
55
  private _scopped;
41
- private _errors;
56
+ private _watchForEntitiesChange;
57
+ private _entityWatchCallback;
58
+ private _storeTracked;
42
59
  renderTemplate(template: string): any;
43
- get tracked(): Tracked;
44
- cleanTrackedEntities(): void;
45
- cleanTrackedDomains(): void;
46
- cleanTracked(): void;
60
+ trackTemplate(template: string, renderingFunction: RenderingFunction): void;
61
+ cleanTracked(entityId?: string): void;
62
+ }
63
+ declare class HomeAssistantJavaScriptTemplates {
64
+ constructor(ha: HomeAssistant, options?: Options);
65
+ private _renderer;
66
+ getRenderer(): Promise<HomeAssistantJavaScriptTemplatesRenderer>;
47
67
  }
48
- export { HomeAssistantJavaScriptTemplates as default, HomeAssistant, Hass };
68
+ export { HomeAssistantJavaScriptTemplates as default };
69
+ export type { HomeAssistant, HomeAssistantJavaScriptTemplatesRenderer, HassConnection, Hass };
package/dist/esm/index.js CHANGED
@@ -1 +1 @@
1
- var e,t,i=/^([a-z_]+)\.(\w+)$/;!function(e){e.UNKNOWN="unknown",e.UNAVAILABLE="unavailable"}(e||(e={})),function(e){e.AREA_ID="area_id",e.NAME="name"}(t||(t={}));var s=function(e){return e.includes(".")};function n(n){var r=function(){return Object.entries(n.hass.areas)},a=new Set,c=new Set,d=function(e){n.hass.states[e]&&a.add(e)};return{get hass(){return n.hass},states:new Proxy((function(e){var t;if(s(e))return d(e),null===(t=n.hass.states[e])||void 0===t?void 0:t.state;throw SyntaxError("".concat("[home-assistant-javascript-templates]",": states method cannot be used with a domain, use it as an object instead."))}),{get:function(e,t){if(s(t))return d(t),n.hass.states[t];var r,a=Object.entries(n.hass.states).filter((function(e){return e[0].startsWith(t)}));return a.length&&(r=t,c.add(r)),new Proxy(a.reduce((function(e,t){var s=t[0],n=t[1];return e[s.replace(i,"$2")]=n,e}),{}),{get:function(e,i){return d("".concat(t,".").concat(i)),e[i]}})}}),is_state:function(e,t){var i;return d(e),(null===(i=n.hass.states[e])||void 0===i?void 0:i.state)===t},state_attr:function(e,t){var i,s;return d(e),null===(s=null===(i=n.hass.states[e])||void 0===i?void 0:i.attributes)||void 0===s?void 0:s[t]},is_state_attr:function(e,t,i){return this.state_attr(e,t)===i},has_value:function(t){return!!this.states(t)&&!(this.is_state(t,e.UNKNOWN)||this.is_state(t,e.UNAVAILABLE))},device_attr:function(e,t){var i;return null===(i=n.hass.devices[e])||void 0===i?void 0:i[t]},is_device_attr:function(e,t,i){return this.device_attr(e,t)===i},device_id:function(e){var t;return null===(t=n.hass.entities[e])||void 0===t?void 0:t.device_id},areas:function(){return r().map((function(e){return e[1].area_id}))},area_id:function(e){var i;if(e in n.hass.devices)return this.device_attr(e,t.AREA_ID);var s=this.device_id(e);if(s)return this.device_attr(s,t.AREA_ID);var a=r().find((function(t){return t[1].name===e}));return null===(i=null==a?void 0:a[1])||void 0===i?void 0:i.area_id},area_name:function(e){var i,s;e in n.hass.devices&&(s=this.device_attr(e,t.AREA_ID));var a=this.device_id(e);a&&(s=this.device_attr(a,t.AREA_ID));var c=r().find((function(t){var i=t[1];return i.area_id===e||i.area_id===s}));return null===(i=null==c?void 0:c[1])||void 0===i?void 0:i.name},area_entities:function(e){var t=r().find((function(t){var i=t[1];return i.area_id===e||i.name===e}));return t?Object.entries(n.hass.entities).filter((function(e){return e[1].area_id===t[1].area_id})).map((function(e){return e[0]})):[]},area_devices:function(e){var t=r().find((function(t){var i=t[1];return i.area_id===e||i.name===e}));return t?Object.entries(n.hass.devices).filter((function(e){return e[1].area_id===t[1].area_id})).map((function(e){return e[1].id})):[]},get user_name(){return n.hass.user.name},get user_is_admin(){return n.hass.user.is_admin},get user_is_owner(){return n.hass.user.is_owner},tracked:{get entities(){return Array.from(a)},get domains(){return Array.from(c)}},cleanTrackedEntities:function(){a.clear()},cleanTrackedDomains:function(){c.clear()}}}var r=function(){function e(e,t){void 0===t&&(t=!1),this._scopped=n(e),this._errors=t}return e.prototype.renderTemplate=function(e){try{var t=e.includes("return")?e:"return ".concat(e);return new Function("hass","states","is_state","state_attr","is_state_attr","has_value","device_attr","is_device_attr","device_id","areas","area_id","area_name","area_entities","area_devices","user_name","user_is_admin","user_is_owner","".concat('"use strict";'," ").concat(t))(this._scopped.hass,this._scopped.states,this._scopped.is_state.bind(this._scopped),this._scopped.state_attr.bind(this._scopped),this._scopped.is_state_attr.bind(this._scopped),this._scopped.has_value.bind(this._scopped),this._scopped.device_attr.bind(this._scopped),this._scopped.is_device_attr.bind(this._scopped),this._scopped.device_id.bind(this._scopped),this._scopped.areas.bind(this._scopped),this._scopped.area_id.bind(this._scopped),this._scopped.area_name.bind(this._scopped),this._scopped.area_entities.bind(this._scopped),this._scopped.area_devices.bind(this._scopped),this._scopped.user_name,this._scopped.user_is_admin,this._scopped.user_is_owner)}catch(e){if(this._errors)throw e;return void console.warn(e)}},Object.defineProperty(e.prototype,"tracked",{get:function(){return this._scopped.tracked},enumerable:!1,configurable:!0}),e.prototype.cleanTrackedEntities=function(){this._scopped.cleanTrackedEntities()},e.prototype.cleanTrackedDomains=function(){this._scopped.cleanTrackedDomains()},e.prototype.cleanTracked=function(){this._scopped.cleanTrackedEntities(),this._scopped.cleanTrackedDomains()},e}();export{r as default};
1
+ var t,e,i="[home-assistant-javascript-templates]",n=/^([a-z_]+)\.(\w+)$/;!function(t){t.UNKNOWN="unknown",t.UNAVAILABLE="unavailable"}(t||(t={})),function(t){t.AREA_ID="area_id",t.NAME="name"}(e||(e={}));var s=function(t){return t.reduce((function(t,e){var i=e[0],s=e[1];return t[i.replace(n,"$2")]=s,t}),{})},r=function(t){return t.includes(".")};function a(n,a){var o=function(){return Object.entries(n.hass.areas)},c=function(){return Object.entries(n.hass.entities)},u=new Set,d=function(t,e){a&&console.warn("".concat(t," ").concat(e," used in a JavaScript template doesn't exist"))},_=function(t){return d("Entity",t)},h=function(t){return d("Domain",t)},p=function(t){n.hass.states[t]?u.add(t):_(t)};return{get hass(){return n.hass},states:new Proxy((function(t){var e;if(r(t))return p(t),null===(e=n.hass.states[t])||void 0===e?void 0:e.state;throw SyntaxError("".concat(i,": states method cannot be used with a domain, use it as an object instead."))}),{get:function(t,e){if(r(e))return p(e),n.hass.states[e];var i=Object.entries(n.hass.states).filter((function(t){return t[0].startsWith(e)}));return i.length||h(e),new Proxy(s(i),{get:function(t,i){return p("".concat(e,".").concat(i)),t[i]}})}}),is_state:function(t,e){var i;return p(t),(null===(i=n.hass.states[t])||void 0===i?void 0:i.state)===e},state_attr:function(t,e){var i,s;return p(t),null===(s=null===(i=n.hass.states[t])||void 0===i?void 0:i.attributes)||void 0===s?void 0:s[e]},is_state_attr:function(t,e,i){return this.state_attr(t,e)===i},has_value:function(e){return this.states(e)?!(this.is_state(e,t.UNKNOWN)||this.is_state(e,t.UNAVAILABLE)):(_(e),!1)},entities:new Proxy((function(t){if(void 0===t)return n.hass.entities;if(r(t))return p(t),n.hass.entities[t];var e=c().filter((function(e){return e[0].startsWith(t)}));return e.length||h(t),new Proxy(s(e),{get:function(e,i){return p("".concat(t,".").concat(i)),e[i]}})}),{get:function(t,e){return t(e)}}),entity_prop:function(t,e){var i;return p(t),null===(i=n.hass.entities[t])||void 0===i?void 0:i[e]},is_entity_prop:function(t,e,i){return this.entity_prop(t,e)===i},devices:new Proxy((function(t){if(void 0===t)return n.hass.devices;if(r(t))throw SyntaxError("".concat(i,": devices method cannot be used with an entity id, you should use a device id instead."));return n.hass.devices[t]}),{get:function(t,e){if(r(e))throw SyntaxError("".concat(i,": devices cannot be accesed using an entity id, you should use a device id instead."));return n.hass.devices[e]}}),device_attr:function(t,e){var i;return null===(i=n.hass.devices[t])||void 0===i?void 0:i[e]},is_device_attr:function(t,e,i){return this.device_attr(t,e)===i},device_id:function(t){var e;return p(t),null===(e=n.hass.entities[t])||void 0===e?void 0:e.device_id},areas:function(){return o().map((function(t){return t[1].area_id}))},area_id:function(t){var i,s;if(t in n.hass.devices)return this.device_attr(t,e.AREA_ID);var r=null===(i=n.hass.entities[t])||void 0===i?void 0:i.device_id;if(r)return this.device_attr(r,e.AREA_ID);var a=o().find((function(e){return e[1].name===t}));return null===(s=null==a?void 0:a[1])||void 0===s?void 0:s.area_id},area_name:function(t){var i,s,r;t in n.hass.devices&&(r=this.device_attr(t,e.AREA_ID));var a=null===(i=n.hass.entities[t])||void 0===i?void 0:i.device_id;a&&(r=this.device_attr(a,e.AREA_ID));var c=o().find((function(e){var i=e[1];return i.area_id===t||i.area_id===r}));return null===(s=null==c?void 0:c[1])||void 0===s?void 0:s.name},area_entities:function(t){var e=o().find((function(e){var i=e[1];return i.area_id===t||i.name===t}));return e?c().filter((function(t){return t[1].area_id===e[1].area_id})).map((function(t){return t[0]})):[]},area_devices:function(t){var e=o().find((function(e){var i=e[1];return i.area_id===t||i.name===t}));return e?Object.entries(n.hass.devices).filter((function(t){return t[1].area_id===e[1].area_id})).map((function(t){return t[1].id})):[]},get user_name(){return n.hass.user.name},get user_is_admin(){return n.hass.user.is_admin},get user_is_owner(){return n.hass.user.is_owner},get user_agent(){return window.navigator.userAgent},get tracked(){return u},cleanTracked:function(){u.clear()}}}var o=function(){function t(t,e){var i=e.throwErrors,n=void 0!==i&&i,s=e.throwWarnings,r=void 0===s||s;this._throwErrors=n,this._throwWarnings=r,this._subscriptions=new Map,this._scopped=a(t,r),this._watchForEntitiesChange()}return t.prototype._watchForEntitiesChange=function(){var t=this;window.hassConnection.then((function(e){e.conn.subscribeMessage((function(e){return t._entityWatchCallback(e)}),{type:"subscribe_events",event_type:"state_changed"})}))},t.prototype._entityWatchCallback=function(t){var e=this;if(this._subscriptions.size){var i=t.data.entity_id;this._subscriptions.has(i)&&this._subscriptions.get(i).forEach((function(t,i){e.trackTemplate(i,t)}))}},t.prototype._storeTracked=function(t,e){var i=this;this._scopped.tracked.forEach((function(n){if(i._subscriptions.has(n)){var s=i._subscriptions.get(n);s.has(t)||s.set(t,e)}else i._subscriptions.set(n,new Map([[t,e]]))}))},t.prototype.renderTemplate=function(t){try{var e=t.includes("return")?t:"return ".concat(t);return new Function("hass","states","is_state","state_attr","is_state_attr","has_value","entities","entity_prop","is_entity_prop","devices","device_attr","is_device_attr","device_id","areas","area_id","area_name","area_entities","area_devices","user_name","user_is_admin","user_is_owner","user_agent","".concat('"use strict";'," ").concat(e))(this._scopped.hass,this._scopped.states,this._scopped.is_state.bind(this._scopped),this._scopped.state_attr.bind(this._scopped),this._scopped.is_state_attr.bind(this._scopped),this._scopped.has_value.bind(this._scopped),this._scopped.entities,this._scopped.entity_prop,this._scopped.is_entity_prop.bind(this._scopped),this._scopped.devices,this._scopped.device_attr.bind(this._scopped),this._scopped.is_device_attr.bind(this._scopped),this._scopped.device_id.bind(this._scopped),this._scopped.areas.bind(this._scopped),this._scopped.area_id.bind(this._scopped),this._scopped.area_name.bind(this._scopped),this._scopped.area_entities.bind(this._scopped),this._scopped.area_devices.bind(this._scopped),this._scopped.user_name,this._scopped.user_is_admin,this._scopped.user_is_owner,this._scopped.user_agent)}catch(t){if(this._throwErrors)throw t;return void(this._throwWarnings&&console.warn(t))}},t.prototype.trackTemplate=function(t,e){this._scopped.cleanTracked();var i=this.renderTemplate(t);this._storeTracked(t,e),e(i)},t.prototype.cleanTracked=function(t){t?this._subscriptions.has(t)&&this._subscriptions.delete(t):this._subscriptions.clear()},t}(),c=function(){function t(t,e){var i,n;void 0===e&&(e={}),this._renderer=(i=function(){return t.hass},n=function(t){return!!(t&&t.areas&&t.devices&&t.entities&&t.states&&t.user)},new Promise((function(t,e){var s=0,r=function(){var a=i();n(a)?t(a):++s<100?setTimeout(r,50):e()};r()}))).then((function(){return new o(t,e)})).catch((function(){throw new Error("The provided element doesn't contain a proper or initialised hass object")}))}return t.prototype.getRenderer=function(){return this._renderer},t}();export{c as default};
package/dist/index.d.ts CHANGED
@@ -1,3 +1,8 @@
1
+ interface Options {
2
+ throwErrors?: boolean;
3
+ throwWarnings?: boolean;
4
+ }
5
+ type RenderingFunction = (result?: any) => void;
1
6
  interface Area {
2
7
  area_id: string;
3
8
  name: string;
@@ -15,6 +20,7 @@ interface State {
15
20
  interface Entity {
16
21
  area_id: string | null;
17
22
  device_id: string;
23
+ [key: string]: unknown;
18
24
  }
19
25
  interface User {
20
26
  name: string;
@@ -31,18 +37,33 @@ interface Hass {
31
37
  interface HomeAssistant extends HTMLElement {
32
38
  hass: Hass;
33
39
  }
34
- interface Tracked {
35
- entities: string[];
36
- domains: string[];
40
+ interface HassConnection {
41
+ conn: {
42
+ subscribeMessage: <T>(callback: (response: T) => void, options: Record<string, unknown>) => void;
43
+ };
37
44
  }
38
- declare class HomeAssistantJavaScriptTemplates {
39
- constructor(ha: HomeAssistant, throwErrors?: boolean);
45
+ declare global {
46
+ interface Window {
47
+ hassConnection: Promise<HassConnection>;
48
+ }
49
+ }
50
+ declare class HomeAssistantJavaScriptTemplatesRenderer {
51
+ constructor(ha: HomeAssistant, options: Options);
52
+ private _throwErrors;
53
+ private _throwWarnings;
54
+ private _subscriptions;
40
55
  private _scopped;
41
- private _errors;
56
+ private _watchForEntitiesChange;
57
+ private _entityWatchCallback;
58
+ private _storeTracked;
42
59
  renderTemplate(template: string): any;
43
- get tracked(): Tracked;
44
- cleanTrackedEntities(): void;
45
- cleanTrackedDomains(): void;
46
- cleanTracked(): void;
60
+ trackTemplate(template: string, renderingFunction: RenderingFunction): void;
61
+ cleanTracked(entityId?: string): void;
62
+ }
63
+ declare class HomeAssistantJavaScriptTemplates {
64
+ constructor(ha: HomeAssistant, options?: Options);
65
+ private _renderer;
66
+ getRenderer(): Promise<HomeAssistantJavaScriptTemplatesRenderer>;
47
67
  }
48
- export { HomeAssistantJavaScriptTemplates as default, HomeAssistant, Hass };
68
+ export { HomeAssistantJavaScriptTemplates as default };
69
+ export type { HomeAssistant, HomeAssistantJavaScriptTemplatesRenderer, HassConnection, Hass };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";var e,t,i=/^([a-z_]+)\.(\w+)$/;!function(e){e.UNKNOWN="unknown",e.UNAVAILABLE="unavailable"}(e||(e={})),function(e){e.AREA_ID="area_id",e.NAME="name"}(t||(t={}));var s=function(e){return e.includes(".")};function n(n){var r=function(){return Object.entries(n.hass.areas)},a=new Set,c=new Set,d=function(e){n.hass.states[e]&&a.add(e)};return{get hass(){return n.hass},states:new Proxy((function(e){var t;if(s(e))return d(e),null===(t=n.hass.states[e])||void 0===t?void 0:t.state;throw SyntaxError("".concat("[home-assistant-javascript-templates]",": states method cannot be used with a domain, use it as an object instead."))}),{get:function(e,t){if(s(t))return d(t),n.hass.states[t];var r,a=Object.entries(n.hass.states).filter((function(e){return e[0].startsWith(t)}));return a.length&&(r=t,c.add(r)),new Proxy(a.reduce((function(e,t){var s=t[0],n=t[1];return e[s.replace(i,"$2")]=n,e}),{}),{get:function(e,i){return d("".concat(t,".").concat(i)),e[i]}})}}),is_state:function(e,t){var i;return d(e),(null===(i=n.hass.states[e])||void 0===i?void 0:i.state)===t},state_attr:function(e,t){var i,s;return d(e),null===(s=null===(i=n.hass.states[e])||void 0===i?void 0:i.attributes)||void 0===s?void 0:s[t]},is_state_attr:function(e,t,i){return this.state_attr(e,t)===i},has_value:function(t){return!!this.states(t)&&!(this.is_state(t,e.UNKNOWN)||this.is_state(t,e.UNAVAILABLE))},device_attr:function(e,t){var i;return null===(i=n.hass.devices[e])||void 0===i?void 0:i[t]},is_device_attr:function(e,t,i){return this.device_attr(e,t)===i},device_id:function(e){var t;return null===(t=n.hass.entities[e])||void 0===t?void 0:t.device_id},areas:function(){return r().map((function(e){return e[1].area_id}))},area_id:function(e){var i;if(e in n.hass.devices)return this.device_attr(e,t.AREA_ID);var s=this.device_id(e);if(s)return this.device_attr(s,t.AREA_ID);var a=r().find((function(t){return t[1].name===e}));return null===(i=null==a?void 0:a[1])||void 0===i?void 0:i.area_id},area_name:function(e){var i,s;e in n.hass.devices&&(s=this.device_attr(e,t.AREA_ID));var a=this.device_id(e);a&&(s=this.device_attr(a,t.AREA_ID));var c=r().find((function(t){var i=t[1];return i.area_id===e||i.area_id===s}));return null===(i=null==c?void 0:c[1])||void 0===i?void 0:i.name},area_entities:function(e){var t=r().find((function(t){var i=t[1];return i.area_id===e||i.name===e}));return t?Object.entries(n.hass.entities).filter((function(e){return e[1].area_id===t[1].area_id})).map((function(e){return e[0]})):[]},area_devices:function(e){var t=r().find((function(t){var i=t[1];return i.area_id===e||i.name===e}));return t?Object.entries(n.hass.devices).filter((function(e){return e[1].area_id===t[1].area_id})).map((function(e){return e[1].id})):[]},get user_name(){return n.hass.user.name},get user_is_admin(){return n.hass.user.is_admin},get user_is_owner(){return n.hass.user.is_owner},tracked:{get entities(){return Array.from(a)},get domains(){return Array.from(c)}},cleanTrackedEntities:function(){a.clear()},cleanTrackedDomains:function(){c.clear()}}}var r=function(){function e(e,t){void 0===t&&(t=!1),this._scopped=n(e),this._errors=t}return e.prototype.renderTemplate=function(e){try{var t=e.includes("return")?e:"return ".concat(e);return new Function("hass","states","is_state","state_attr","is_state_attr","has_value","device_attr","is_device_attr","device_id","areas","area_id","area_name","area_entities","area_devices","user_name","user_is_admin","user_is_owner","".concat('"use strict";'," ").concat(t))(this._scopped.hass,this._scopped.states,this._scopped.is_state.bind(this._scopped),this._scopped.state_attr.bind(this._scopped),this._scopped.is_state_attr.bind(this._scopped),this._scopped.has_value.bind(this._scopped),this._scopped.device_attr.bind(this._scopped),this._scopped.is_device_attr.bind(this._scopped),this._scopped.device_id.bind(this._scopped),this._scopped.areas.bind(this._scopped),this._scopped.area_id.bind(this._scopped),this._scopped.area_name.bind(this._scopped),this._scopped.area_entities.bind(this._scopped),this._scopped.area_devices.bind(this._scopped),this._scopped.user_name,this._scopped.user_is_admin,this._scopped.user_is_owner)}catch(e){if(this._errors)throw e;return void console.warn(e)}},Object.defineProperty(e.prototype,"tracked",{get:function(){return this._scopped.tracked},enumerable:!1,configurable:!0}),e.prototype.cleanTrackedEntities=function(){this._scopped.cleanTrackedEntities()},e.prototype.cleanTrackedDomains=function(){this._scopped.cleanTrackedDomains()},e.prototype.cleanTracked=function(){this._scopped.cleanTrackedEntities(),this._scopped.cleanTrackedDomains()},e}();module.exports=r;
1
+ "use strict";var t,e,i="[home-assistant-javascript-templates]",n=/^([a-z_]+)\.(\w+)$/;!function(t){t.UNKNOWN="unknown",t.UNAVAILABLE="unavailable"}(t||(t={})),function(t){t.AREA_ID="area_id",t.NAME="name"}(e||(e={}));var s=function(t){return t.reduce((function(t,e){var i=e[0],s=e[1];return t[i.replace(n,"$2")]=s,t}),{})},r=function(t){return t.includes(".")};function a(n,a){var o=function(){return Object.entries(n.hass.areas)},c=function(){return Object.entries(n.hass.entities)},u=new Set,d=function(t,e){a&&console.warn("".concat(t," ").concat(e," used in a JavaScript template doesn't exist"))},_=function(t){return d("Entity",t)},h=function(t){return d("Domain",t)},p=function(t){n.hass.states[t]?u.add(t):_(t)};return{get hass(){return n.hass},states:new Proxy((function(t){var e;if(r(t))return p(t),null===(e=n.hass.states[t])||void 0===e?void 0:e.state;throw SyntaxError("".concat(i,": states method cannot be used with a domain, use it as an object instead."))}),{get:function(t,e){if(r(e))return p(e),n.hass.states[e];var i=Object.entries(n.hass.states).filter((function(t){return t[0].startsWith(e)}));return i.length||h(e),new Proxy(s(i),{get:function(t,i){return p("".concat(e,".").concat(i)),t[i]}})}}),is_state:function(t,e){var i;return p(t),(null===(i=n.hass.states[t])||void 0===i?void 0:i.state)===e},state_attr:function(t,e){var i,s;return p(t),null===(s=null===(i=n.hass.states[t])||void 0===i?void 0:i.attributes)||void 0===s?void 0:s[e]},is_state_attr:function(t,e,i){return this.state_attr(t,e)===i},has_value:function(e){return this.states(e)?!(this.is_state(e,t.UNKNOWN)||this.is_state(e,t.UNAVAILABLE)):(_(e),!1)},entities:new Proxy((function(t){if(void 0===t)return n.hass.entities;if(r(t))return p(t),n.hass.entities[t];var e=c().filter((function(e){return e[0].startsWith(t)}));return e.length||h(t),new Proxy(s(e),{get:function(e,i){return p("".concat(t,".").concat(i)),e[i]}})}),{get:function(t,e){return t(e)}}),entity_prop:function(t,e){var i;return p(t),null===(i=n.hass.entities[t])||void 0===i?void 0:i[e]},is_entity_prop:function(t,e,i){return this.entity_prop(t,e)===i},devices:new Proxy((function(t){if(void 0===t)return n.hass.devices;if(r(t))throw SyntaxError("".concat(i,": devices method cannot be used with an entity id, you should use a device id instead."));return n.hass.devices[t]}),{get:function(t,e){if(r(e))throw SyntaxError("".concat(i,": devices cannot be accesed using an entity id, you should use a device id instead."));return n.hass.devices[e]}}),device_attr:function(t,e){var i;return null===(i=n.hass.devices[t])||void 0===i?void 0:i[e]},is_device_attr:function(t,e,i){return this.device_attr(t,e)===i},device_id:function(t){var e;return p(t),null===(e=n.hass.entities[t])||void 0===e?void 0:e.device_id},areas:function(){return o().map((function(t){return t[1].area_id}))},area_id:function(t){var i,s;if(t in n.hass.devices)return this.device_attr(t,e.AREA_ID);var r=null===(i=n.hass.entities[t])||void 0===i?void 0:i.device_id;if(r)return this.device_attr(r,e.AREA_ID);var a=o().find((function(e){return e[1].name===t}));return null===(s=null==a?void 0:a[1])||void 0===s?void 0:s.area_id},area_name:function(t){var i,s,r;t in n.hass.devices&&(r=this.device_attr(t,e.AREA_ID));var a=null===(i=n.hass.entities[t])||void 0===i?void 0:i.device_id;a&&(r=this.device_attr(a,e.AREA_ID));var c=o().find((function(e){var i=e[1];return i.area_id===t||i.area_id===r}));return null===(s=null==c?void 0:c[1])||void 0===s?void 0:s.name},area_entities:function(t){var e=o().find((function(e){var i=e[1];return i.area_id===t||i.name===t}));return e?c().filter((function(t){return t[1].area_id===e[1].area_id})).map((function(t){return t[0]})):[]},area_devices:function(t){var e=o().find((function(e){var i=e[1];return i.area_id===t||i.name===t}));return e?Object.entries(n.hass.devices).filter((function(t){return t[1].area_id===e[1].area_id})).map((function(t){return t[1].id})):[]},get user_name(){return n.hass.user.name},get user_is_admin(){return n.hass.user.is_admin},get user_is_owner(){return n.hass.user.is_owner},get user_agent(){return window.navigator.userAgent},get tracked(){return u},cleanTracked:function(){u.clear()}}}var o=function(){function t(t,e){var i=e.throwErrors,n=void 0!==i&&i,s=e.throwWarnings,r=void 0===s||s;this._throwErrors=n,this._throwWarnings=r,this._subscriptions=new Map,this._scopped=a(t,r),this._watchForEntitiesChange()}return t.prototype._watchForEntitiesChange=function(){var t=this;window.hassConnection.then((function(e){e.conn.subscribeMessage((function(e){return t._entityWatchCallback(e)}),{type:"subscribe_events",event_type:"state_changed"})}))},t.prototype._entityWatchCallback=function(t){var e=this;if(this._subscriptions.size){var i=t.data.entity_id;this._subscriptions.has(i)&&this._subscriptions.get(i).forEach((function(t,i){e.trackTemplate(i,t)}))}},t.prototype._storeTracked=function(t,e){var i=this;this._scopped.tracked.forEach((function(n){if(i._subscriptions.has(n)){var s=i._subscriptions.get(n);s.has(t)||s.set(t,e)}else i._subscriptions.set(n,new Map([[t,e]]))}))},t.prototype.renderTemplate=function(t){try{var e=t.includes("return")?t:"return ".concat(t);return new Function("hass","states","is_state","state_attr","is_state_attr","has_value","entities","entity_prop","is_entity_prop","devices","device_attr","is_device_attr","device_id","areas","area_id","area_name","area_entities","area_devices","user_name","user_is_admin","user_is_owner","user_agent","".concat('"use strict";'," ").concat(e))(this._scopped.hass,this._scopped.states,this._scopped.is_state.bind(this._scopped),this._scopped.state_attr.bind(this._scopped),this._scopped.is_state_attr.bind(this._scopped),this._scopped.has_value.bind(this._scopped),this._scopped.entities,this._scopped.entity_prop,this._scopped.is_entity_prop.bind(this._scopped),this._scopped.devices,this._scopped.device_attr.bind(this._scopped),this._scopped.is_device_attr.bind(this._scopped),this._scopped.device_id.bind(this._scopped),this._scopped.areas.bind(this._scopped),this._scopped.area_id.bind(this._scopped),this._scopped.area_name.bind(this._scopped),this._scopped.area_entities.bind(this._scopped),this._scopped.area_devices.bind(this._scopped),this._scopped.user_name,this._scopped.user_is_admin,this._scopped.user_is_owner,this._scopped.user_agent)}catch(t){if(this._throwErrors)throw t;return void(this._throwWarnings&&console.warn(t))}},t.prototype.trackTemplate=function(t,e){this._scopped.cleanTracked();var i=this.renderTemplate(t);this._storeTracked(t,e),e(i)},t.prototype.cleanTracked=function(t){t?this._subscriptions.has(t)&&this._subscriptions.delete(t):this._subscriptions.clear()},t}(),c=function(){function t(t,e){var i,n;void 0===e&&(e={}),this._renderer=(i=function(){return t.hass},n=function(t){return!!(t&&t.areas&&t.devices&&t.entities&&t.states&&t.user)},new Promise((function(t,e){var s=0,r=function(){var a=i();n(a)?t(a):++s<100?setTimeout(r,50):e()};r()}))).then((function(){return new o(t,e)})).catch((function(){throw new Error("The provided element doesn't contain a proper or initialised hass object")}))}return t.prototype.getRenderer=function(){return this._renderer},t}();module.exports=c;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "home-assistant-javascript-templates",
3
- "version": "3.1.1",
3
+ "version": "5.0.0",
4
4
  "description": "A JavaScript utility to render Home Assistant JavaScript templates",
5
5
  "keywords": [
6
6
  "home-assistant",
@@ -39,7 +39,6 @@
39
39
  "test:ts": "tsc --noEmit",
40
40
  "test:unit": "jest --verbose",
41
41
  "test:all": "pnpm test:ts && pnpm test:unit",
42
- "preinstall": "npx -y only-allow pnpm",
43
42
  "prepare": "pnpm build",
44
43
  "prepublishOnly": "pnpm test:all",
45
44
  "version": "git add .",
@@ -47,13 +46,14 @@
47
46
  },
48
47
  "devDependencies": {
49
48
  "@rollup/plugin-terser": "^0.4.4",
50
- "@types/jest": "^29.5.12",
51
- "@types/node": "^20.11.30",
49
+ "@types/jest": "^29.5.13",
50
+ "@types/node": "^22.5.5",
52
51
  "jest": "^29.7.0",
53
- "rollup": "^4.13.0",
52
+ "jest-environment-jsdom": "^29.7.0",
53
+ "rollup": "^4.21.3",
54
54
  "rollup-plugin-ts": "^3.4.5",
55
- "ts-jest": "^29.1.2",
56
- "tslib": "^2.6.2",
57
- "typescript": "^5.4.3"
55
+ "ts-jest": "^29.2.5",
56
+ "tslib": "^2.7.0",
57
+ "typescript": "^5.6.2"
58
58
  }
59
59
  }