Skip to content

Commit 7508f10

Browse files
committed
Add a new method state_translated and improve is_state method
1 parent f737a5a commit 7508f10

7 files changed

Lines changed: 98 additions & 19 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelog
22

3+
## [5.9.0] - 2025-07-13
4+
5+
- Add a new method `state_translated` to return the translated state value of an entity
6+
- Improve the `is_state` function to accept also an array as a value to compare
7+
38
## [5.8.0] - 2025-06-24
49

510
- Improve the `states` function with an optional object with options (`with_unit` and `rounded`)

README.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,12 +180,22 @@ states.sensor // returns an object containing all the entities states of the 'se
180180
>[!TIP]
181181
>Avoid using `states['sensor.slaapkamer_temperatuur'].state` or `states.sensor.slaapkamer_temperatuur.state`. Use `states('sensor.slaapkamer_temperatuur')` instead, 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['sensor.slaapkamer_temperatuur']?.state` or `states.sensor?.slaapkamer_temperatuur?.state`.
182182
183+
#### state_translated
184+
185+
Method to return a translated state of an entity using a language that is currently configured in the general settings. If the entity id doesn‘t exist it returns `undefined`.
186+
187+
```javascript
188+
states('device_tracker.paulus'); // not_home
189+
state_translated('device_tracker.paulus') // Away
190+
```
191+
183192
#### is_state
184193

185-
Method to check if the state of an entity is equal to a certain value. It returns a `boolean`. If the entity id doesn‘t exist it returns `false`.
194+
Method to check if the state of an entity is equal to a certain value or it is contained inside a list of values. It returns a `boolean`. If the entity id doesn‘t exist it returns `false`.
186195

187196
```javascript
188-
is_state('device_tracker.paulus', 'not_home')
197+
is_state('device_tracker.paulus', 'not_home') // check for a single value
198+
is_state('device_tracker.paulus', ['not_home', 'work']) // check for a list of values
189199
```
190200

191201
#### state_attr

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ class HomeAssistantJavaScriptTemplatesRenderer {
195195
const templateFunction = new Function(
196196
'hass',
197197
'states',
198+
'state_translated',
198199
'is_state',
199200
'state_attr',
200201
'is_state_attr',
@@ -225,6 +226,7 @@ class HomeAssistantJavaScriptTemplatesRenderer {
225226
return templateFunction(
226227
this._scopped.hass,
227228
this._scopped.states,
229+
this._scopped.state_translated.bind(this._scopped),
228230
this._scopped.is_state.bind(this._scopped),
229231
this._scopped.state_attr.bind(this._scopped),
230232
this._scopped.is_state_attr.bind(this._scopped),

src/types/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export interface Hass {
4545
states: Record<string, State>;
4646
user: User;
4747
language: string;
48+
formatEntityState: (state: State) => string | undefined;
4849
}
4950

5051
export interface HomeAssistant extends HTMLElement {
@@ -109,7 +110,8 @@ export interface Scopped {
109110
hass: Hass;
110111
// states
111112
states: ProxiedStates;
112-
is_state: (entityId: string, value: string) => boolean;
113+
state_translated: (entityId: string) => string;
114+
is_state: (entityId: string, value: string | string[]) => boolean;
113115
state_attr: (entityId: string, attr: string) => unknown | undefined;
114116
is_state_attr: (entityId: string, attr: string, value: unknown) => boolean;
115117
has_value: (entityId: string) => boolean;

src/utilities/index.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,8 +161,20 @@ export function createScoppedFunctions(
161161
}
162162
}
163163
),
164-
is_state(entityId: string, value: string): boolean {
164+
state_translated(entityId: string): string | undefined {
165165
trackEntity(entityId);
166+
if (ha.hass.states[entityId]) {
167+
return ha.hass.formatEntityState(ha.hass.states[entityId]);
168+
}
169+
return undefined;
170+
},
171+
is_state(entityId: string, value: string | string[]): boolean {
172+
trackEntity(entityId);
173+
if (Array.isArray(value)) {
174+
return value.some((valueItem: string): boolean => {
175+
return ha.hass.states[entityId]?.state === valueItem;
176+
});
177+
}
166178
return ha.hass.states[entityId]?.state === value;
167179
},
168180
state_attr(entityId: string, attr: string): unknown {

tests/03 - basic-templates.test.ts

Lines changed: 61 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ describe('Basic templates tests', () => {
1717
});
1818

1919
afterEach(() => {
20+
jest.resetAllMocks();
2021
consoleWarnMock.mockRestore();
2122
});
2223

@@ -194,25 +195,71 @@ describe('Basic templates tests', () => {
194195

195196
});
196197

197-
describe('is_state', () => {
198+
describe('state_translated', () => {
198199

199-
it('is_state should return true if the value coincides', () => {
200-
expect(
201-
compiler.renderTemplate('is_state("sensor.slaapkamer_luchtvochtigheid", "45")')
202-
).toBe(true);
200+
describe.each(
201+
Object.keys(HASS.states)
202+
)('for entity id %s', (entityId: string) => {
203+
it('should send the proper state to the formatEntityState function', () => {
204+
compiler.renderTemplate(`state_translated("${entityId}")`);
205+
expect(HASS.formatEntityState).toHaveBeenNthCalledWith(1, HASS.states[entityId]);
206+
});
203207
});
204208

205-
it('is_state should return false if the value doesn\'t coincide', () => {
206-
expect(
207-
compiler.renderTemplate('is_state("sensor.slaapkamer_temperatuur", "10")')
208-
).toBe(false);
209+
it('should return undefined if the entity id doesn\'t exist', () => {
210+
const result = compiler.renderTemplate('state_translated("sensor.non_existent")');
211+
expect(HASS.formatEntityState).not.toHaveBeenCalled();
212+
expect(result).toBeUndefined();
209213
});
210214

211-
it('is_state should return false if the entity id doesn\'t exist', () => {
212-
expect(
213-
compiler.renderTemplate('is_state("sensor.non_existent", "10")')
214-
).toBe(false);
215-
expect(consoleWarnMock).toHaveBeenCalledWith('Entity sensor.non_existent used in a JavaScript template doesn\'t exist');
215+
});
216+
217+
describe('is_state', () => {
218+
219+
describe('value as string', () => {
220+
221+
it('is_state should return true if the value coincides', () => {
222+
expect(
223+
compiler.renderTemplate('is_state("sensor.slaapkamer_luchtvochtigheid", "45")')
224+
).toBe(true);
225+
});
226+
227+
it('is_state should return false if the value doesn\'t coincide', () => {
228+
expect(
229+
compiler.renderTemplate('is_state("sensor.slaapkamer_temperatuur", "10")')
230+
).toBe(false);
231+
});
232+
233+
it('is_state should return false if the entity id doesn\'t exist', () => {
234+
expect(
235+
compiler.renderTemplate('is_state("sensor.non_existent", "45")')
236+
).toBe(false);
237+
expect(consoleWarnMock).toHaveBeenCalledWith('Entity sensor.non_existent used in a JavaScript template doesn\'t exist');
238+
});
239+
240+
});
241+
242+
describe('value as array', () => {
243+
244+
it('is_state should return true if the value is contained inside the array', () => {
245+
expect(
246+
compiler.renderTemplate('is_state("sensor.slaapkamer_luchtvochtigheid", ["15", "45", "56"])')
247+
).toBe(true);
248+
});
249+
250+
it('is_state should return false if the value isn\'t contained inside the array', () => {
251+
expect(
252+
compiler.renderTemplate('is_state("sensor.slaapkamer_temperatuur", ["5", "10", "28", "100"])')
253+
).toBe(false);
254+
});
255+
256+
it('is_state should return false if the entity id doesn\'t exist', () => {
257+
expect(
258+
compiler.renderTemplate('is_state("sensor.non_existent", ["17", "45", "2"])')
259+
).toBe(false);
260+
expect(consoleWarnMock).toHaveBeenCalledWith('Entity sensor.non_existent used in a JavaScript template doesn\'t exist');
261+
});
262+
216263
});
217264

218265
});

tests/constants.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,8 @@ export const HASS: Hass = {
157157
is_owner: false,
158158
name: 'ElChiniNet'
159159
},
160-
language: 'en'
160+
language: 'en',
161+
formatEntityState: jest.fn()
161162
};
162163

163164
export const HOME_ASSISTANT_ELEMENT = {

0 commit comments

Comments
 (0)