@@ -272,6 +272,28 @@ almost certainly not useful / reliable as a condition.
|
|||||||
|
|
||||||
See [Home Assistant conditions documentation](https://www.home-assistant.io/dashboards/conditional/#state).
|
See [Home Assistant conditions documentation](https://www.home-assistant.io/dashboards/conditional/#state).
|
||||||
|
|
||||||
|
## `template`
|
||||||
|
|
||||||
|
Matches based on a template.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
conditions:
|
||||||
|
- condition: template
|
||||||
|
# [...]
|
||||||
|
```
|
||||||
|
|
||||||
|
| Parameter | Description |
|
||||||
|
| ---------------- | ---------------------------------------------------------------------------------- |
|
||||||
|
| `condition` | Must be `template`. |
|
||||||
|
| `value_template` | The Home Assistant template to check, e.g. `{{ states('switch.office') == 'on' }}` |
|
||||||
|
|
||||||
|
See [Home Assistant conditions documentation](https://www.home-assistant.io/docs/scripts/conditions/#template-condition).
|
||||||
|
|
||||||
|
?> The Advanced Camera Card uses
|
||||||
|
[ha-nunjucks](https://github.com/Nerwyn/ha-nunjucks) to process templates.
|
||||||
|
Consult its documentation for the wide variety of different template values
|
||||||
|
supported.
|
||||||
|
|
||||||
## `triggered`
|
## `triggered`
|
||||||
|
|
||||||
Matches based on whether the selected camera has been triggered.
|
Matches based on whether the selected camera has been triggered.
|
||||||
|
|||||||
@@ -47,8 +47,7 @@ export class HASSManager {
|
|||||||
this._hass = hass;
|
this._hass = hass;
|
||||||
|
|
||||||
this._api.getConditionStateManager().setState({
|
this._api.getConditionStateManager().setState({
|
||||||
state: this._hass.states,
|
hass: this._hass,
|
||||||
user: this._hass.user,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Theme may depend on HASS.
|
// Theme may depend on HASS.
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { TemplateRenderer } from '../card-controller/templates';
|
||||||
import { getConfigValue } from '../config/management';
|
import { getConfigValue } from '../config/management';
|
||||||
import { AdvancedCameraCardCondition } from '../config/schema/conditions/types';
|
import { AdvancedCameraCardCondition } from '../config/schema/conditions/types';
|
||||||
import { isCompanionApp } from '../utils/companion';
|
import { isCompanionApp } from '../utils/companion';
|
||||||
@@ -23,6 +24,7 @@ export class ConditionsManager implements ConditionsManagerReadonlyInterface {
|
|||||||
protected _listeners: ConditionsListener[] = [];
|
protected _listeners: ConditionsListener[] = [];
|
||||||
protected _mediaQueries: MediaQueryList[] = [];
|
protected _mediaQueries: MediaQueryList[] = [];
|
||||||
protected _evaluation: ConditionsEvaluationResult = { result: false };
|
protected _evaluation: ConditionsEvaluationResult = { result: false };
|
||||||
|
protected _templateRenderer: TemplateRenderer = new TemplateRenderer();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
conditions: AdvancedCameraCardCondition[],
|
conditions: AdvancedCameraCardCondition[],
|
||||||
@@ -127,8 +129,8 @@ export class ConditionsManager implements ConditionsManagerReadonlyInterface {
|
|||||||
switch (condition.condition) {
|
switch (condition.condition) {
|
||||||
case undefined:
|
case undefined:
|
||||||
case 'state': {
|
case 'state': {
|
||||||
const fromState = oldState?.state?.[condition.entity]?.state;
|
const fromState = oldState?.hass?.states?.[condition.entity]?.state;
|
||||||
const toState = newState?.state?.[condition.entity]?.state;
|
const toState = newState?.hass?.states?.[condition.entity]?.state;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
result:
|
result:
|
||||||
@@ -207,17 +209,18 @@ export class ConditionsManager implements ConditionsManagerReadonlyInterface {
|
|||||||
case 'numeric_state':
|
case 'numeric_state':
|
||||||
return {
|
return {
|
||||||
result:
|
result:
|
||||||
!!newState?.state &&
|
!!newState?.hass?.states &&
|
||||||
condition.entity in newState.state &&
|
condition.entity in newState.hass?.states &&
|
||||||
newState.state[condition.entity].state !== undefined &&
|
newState.hass.states[condition.entity].state !== undefined &&
|
||||||
(condition.above === undefined ||
|
(condition.above === undefined ||
|
||||||
Number(newState.state[condition.entity].state) > condition.above) &&
|
Number(newState.hass.states[condition.entity].state) > condition.above) &&
|
||||||
(condition.below === undefined ||
|
(condition.below === undefined ||
|
||||||
Number(newState.state[condition.entity].state) < condition.below),
|
Number(newState.hass.states[condition.entity].state) < condition.below),
|
||||||
};
|
};
|
||||||
case 'user':
|
case 'user':
|
||||||
return {
|
return {
|
||||||
result: !!newState?.user && condition.users.includes(newState.user.id),
|
result:
|
||||||
|
!!newState?.hass?.user && condition.users.includes(newState.hass.user.id),
|
||||||
};
|
};
|
||||||
case 'media_loaded':
|
case 'media_loaded':
|
||||||
return {
|
return {
|
||||||
@@ -341,6 +344,16 @@ export class ConditionsManager implements ConditionsManagerReadonlyInterface {
|
|||||||
).result,
|
).result,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
case 'template':
|
||||||
|
return {
|
||||||
|
result:
|
||||||
|
!!newState?.hass &&
|
||||||
|
this._templateRenderer.renderRecursively(
|
||||||
|
newState.hass,
|
||||||
|
condition.value_template,
|
||||||
|
{ conditionState: newState },
|
||||||
|
) === true,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { HassEntities } from 'home-assistant-js-websocket';
|
|
||||||
import { KeysState, MicrophoneState } from '../card-controller/types';
|
import { KeysState, MicrophoneState } from '../card-controller/types';
|
||||||
import { ViewDisplayMode } from '../config/schema/common/display';
|
import { ViewDisplayMode } from '../config/schema/common/display';
|
||||||
import { AdvancedCameraCardConfig } from '../config/schema/types';
|
import { AdvancedCameraCardConfig } from '../config/schema/types';
|
||||||
import { CurrentUser } from '../ha/types';
|
import { HomeAssistant } from '../ha/types';
|
||||||
import { MediaLoadedInfo } from '../types';
|
import { MediaLoadedInfo } from '../types';
|
||||||
|
|
||||||
export interface ConditionState {
|
export interface ConditionState {
|
||||||
@@ -16,9 +15,8 @@ export interface ConditionState {
|
|||||||
keys?: KeysState;
|
keys?: KeysState;
|
||||||
mediaLoadedInfo?: MediaLoadedInfo | null;
|
mediaLoadedInfo?: MediaLoadedInfo | null;
|
||||||
microphone?: MicrophoneState;
|
microphone?: MicrophoneState;
|
||||||
state?: HassEntities;
|
hass?: HomeAssistant;
|
||||||
triggered?: Set<string>;
|
triggered?: Set<string>;
|
||||||
user?: CurrentUser;
|
|
||||||
userAgent?: string;
|
userAgent?: string;
|
||||||
view?: string;
|
view?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
// https://www.home-assistant.io/docs/scripts/conditions/#template-condition
|
||||||
|
export const templateConditionSchema = z.object({
|
||||||
|
condition: z.literal('template'),
|
||||||
|
value_template: z.string(),
|
||||||
|
});
|
||||||
@@ -15,6 +15,7 @@ import { viewConditionSchema } from './custom/view';
|
|||||||
import { numericStateConditionSchema } from './stock/numeric';
|
import { numericStateConditionSchema } from './stock/numeric';
|
||||||
import { screenConditionSchema } from './stock/screen';
|
import { screenConditionSchema } from './stock/screen';
|
||||||
import { stateConditionSchema } from './stock/state';
|
import { stateConditionSchema } from './stock/state';
|
||||||
|
import { templateConditionSchema } from './stock/template';
|
||||||
import { usersConditionSchema } from './stock/users';
|
import { usersConditionSchema } from './stock/users';
|
||||||
|
|
||||||
// https://www.home-assistant.io/docs/scripts/conditions/#or-condition
|
// https://www.home-assistant.io/docs/scripts/conditions/#or-condition
|
||||||
@@ -65,6 +66,7 @@ export const advancedCameraCardConditionSchema = z.union([
|
|||||||
orConditionSchema,
|
orConditionSchema,
|
||||||
andConditionSchema,
|
andConditionSchema,
|
||||||
notConditionSchema,
|
notConditionSchema,
|
||||||
|
templateConditionSchema,
|
||||||
|
|
||||||
// Custom conditions:
|
// Custom conditions:
|
||||||
cameraConditionSchema,
|
cameraConditionSchema,
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import {
|
|||||||
createHASS,
|
createHASS,
|
||||||
createStateEntity,
|
createStateEntity,
|
||||||
createStore,
|
createStore,
|
||||||
createUser,
|
|
||||||
createView,
|
createView,
|
||||||
} from '../../test-utils';
|
} from '../../test-utils';
|
||||||
|
|
||||||
@@ -50,17 +49,13 @@ describe('HASSManager', () => {
|
|||||||
it('should set condition manager state', () => {
|
it('should set condition manager state', () => {
|
||||||
const api = createCardAPI();
|
const api = createCardAPI();
|
||||||
const manager = new HASSManager(api);
|
const manager = new HASSManager(api);
|
||||||
|
const hass = createHASS();
|
||||||
const states = { 'switch.foo': createStateEntity() };
|
|
||||||
const user = createUser({ id: 'user_1' });
|
|
||||||
const hass = createHASS(states, user);
|
|
||||||
|
|
||||||
manager.setHASS(hass);
|
manager.setHASS(hass);
|
||||||
|
|
||||||
expect(api.getConditionStateManager().setState).toBeCalledWith(
|
expect(api.getConditionStateManager().setState).toBeCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
state: states,
|
hass: hass,
|
||||||
user: user,
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
|
import { HassEntities } from 'home-assistant-js-websocket';
|
||||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||||
import { MicrophoneState } from '../../src/card-controller/types';
|
import { MicrophoneState } from '../../src/card-controller/types';
|
||||||
import { ConditionsManager } from '../../src/conditions/conditions-manager';
|
import { ConditionsManager } from '../../src/conditions/conditions-manager';
|
||||||
import { ConditionStateManager } from '../../src/conditions/state-manager';
|
import { ConditionStateManager } from '../../src/conditions/state-manager';
|
||||||
|
import { HomeAssistant } from '../../src/ha/types';
|
||||||
import {
|
import {
|
||||||
createConfig,
|
createConfig,
|
||||||
|
createHASS,
|
||||||
createMediaLoadedInfo,
|
createMediaLoadedInfo,
|
||||||
createStateEntity,
|
createStateEntity,
|
||||||
createUser,
|
createUser,
|
||||||
@@ -193,7 +196,9 @@ describe('ConditionsManager', () => {
|
|||||||
manager.addListener(listener);
|
manager.addListener(listener);
|
||||||
|
|
||||||
stateManager.setState({
|
stateManager.setState({
|
||||||
state: { 'binary_sensor.foo': createStateEntity({ state: 'on' }) },
|
hass: createHASS({
|
||||||
|
'binary_sensor.foo': createStateEntity({ state: 'on' }),
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
expect(listener).toBeCalledWith({
|
expect(listener).toBeCalledWith({
|
||||||
result: true,
|
result: true,
|
||||||
@@ -207,7 +212,9 @@ describe('ConditionsManager', () => {
|
|||||||
expect(listener).toBeCalledTimes(1);
|
expect(listener).toBeCalledTimes(1);
|
||||||
|
|
||||||
stateManager.setState({
|
stateManager.setState({
|
||||||
state: { 'binary_sensor.foo': createStateEntity({ state: 'off' }) },
|
hass: createHASS({
|
||||||
|
'binary_sensor.foo': createStateEntity({ state: 'off' }),
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
expect(listener).toBeCalledWith({
|
expect(listener).toBeCalledWith({
|
||||||
result: true,
|
result: true,
|
||||||
@@ -238,11 +245,13 @@ describe('ConditionsManager', () => {
|
|||||||
|
|
||||||
expect(manager.getEvaluation().result).toBeFalsy();
|
expect(manager.getEvaluation().result).toBeFalsy();
|
||||||
stateManager.setState({
|
stateManager.setState({
|
||||||
state: { 'binary_sensor.foo': createStateEntity() },
|
hass: createHASS({ 'binary_sensor.foo': createStateEntity() }),
|
||||||
});
|
});
|
||||||
expect(manager.getEvaluation().result).toBeTruthy();
|
expect(manager.getEvaluation().result).toBeTruthy();
|
||||||
stateManager.setState({
|
stateManager.setState({
|
||||||
state: { 'binary_sensor.foo': createStateEntity({ state: 'off' }) },
|
hass: createHASS({
|
||||||
|
'binary_sensor.foo': createStateEntity({ state: 'off' }),
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
expect(manager.getEvaluation().result).toBeFalsy();
|
expect(manager.getEvaluation().result).toBeFalsy();
|
||||||
});
|
});
|
||||||
@@ -262,15 +271,19 @@ describe('ConditionsManager', () => {
|
|||||||
|
|
||||||
expect(manager.getEvaluation().result).toBeFalsy();
|
expect(manager.getEvaluation().result).toBeFalsy();
|
||||||
stateManager.setState({
|
stateManager.setState({
|
||||||
state: { 'binary_sensor.foo': createStateEntity() },
|
hass: createHASS({ 'binary_sensor.foo': createStateEntity() }),
|
||||||
});
|
});
|
||||||
expect(manager.getEvaluation().result).toBeTruthy();
|
expect(manager.getEvaluation().result).toBeTruthy();
|
||||||
stateManager.setState({
|
stateManager.setState({
|
||||||
state: { 'binary_sensor.foo': createStateEntity({ state: 'active' }) },
|
hass: createHASS({
|
||||||
|
'binary_sensor.foo': createStateEntity({ state: 'active' }),
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
expect(manager.getEvaluation().result).toBeTruthy();
|
expect(manager.getEvaluation().result).toBeTruthy();
|
||||||
stateManager.setState({
|
stateManager.setState({
|
||||||
state: { 'binary_sensor.foo': createStateEntity({ state: 'off' }) },
|
hass: createHASS({
|
||||||
|
'binary_sensor.foo': createStateEntity({ state: 'off' }),
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
expect(manager.getEvaluation().result).toBeFalsy();
|
expect(manager.getEvaluation().result).toBeFalsy();
|
||||||
});
|
});
|
||||||
@@ -292,11 +305,13 @@ describe('ConditionsManager', () => {
|
|||||||
|
|
||||||
expect(manager.getEvaluation().result).toBeFalsy();
|
expect(manager.getEvaluation().result).toBeFalsy();
|
||||||
stateManager.setState({
|
stateManager.setState({
|
||||||
state: { 'binary_sensor.foo': createStateEntity() },
|
hass: createHASS({ 'binary_sensor.foo': createStateEntity() }),
|
||||||
});
|
});
|
||||||
expect(manager.getEvaluation().result).toBeFalsy();
|
expect(manager.getEvaluation().result).toBeFalsy();
|
||||||
stateManager.setState({
|
stateManager.setState({
|
||||||
state: { 'binary_sensor.foo': createStateEntity({ state: 'off' }) },
|
hass: createHASS({
|
||||||
|
'binary_sensor.foo': createStateEntity({ state: 'off' }),
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
expect(manager.getEvaluation().result).toBeTruthy();
|
expect(manager.getEvaluation().result).toBeTruthy();
|
||||||
});
|
});
|
||||||
@@ -315,15 +330,21 @@ describe('ConditionsManager', () => {
|
|||||||
stateManager,
|
stateManager,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(manager.getEvaluation().result).toBeFalsy();
|
|
||||||
stateManager.setState({ state: { 'binary_sensor.foo': createStateEntity() } });
|
|
||||||
expect(manager.getEvaluation().result).toBeFalsy();
|
expect(manager.getEvaluation().result).toBeFalsy();
|
||||||
stateManager.setState({
|
stateManager.setState({
|
||||||
state: { 'binary_sensor.foo': createStateEntity({ state: 'active' }) },
|
hass: createHASS({ 'binary_sensor.foo': createStateEntity() }),
|
||||||
});
|
});
|
||||||
expect(manager.getEvaluation().result).toBeFalsy();
|
expect(manager.getEvaluation().result).toBeFalsy();
|
||||||
stateManager.setState({
|
stateManager.setState({
|
||||||
state: { 'binary_sensor.foo': createStateEntity({ state: 'off' }) },
|
hass: createHASS({
|
||||||
|
'binary_sensor.foo': createStateEntity({ state: 'active' }),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
expect(manager.getEvaluation().result).toBeFalsy();
|
||||||
|
stateManager.setState({
|
||||||
|
hass: createHASS({
|
||||||
|
'binary_sensor.foo': createStateEntity({ state: 'off' }),
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
expect(manager.getEvaluation().result).toBeTruthy();
|
expect(manager.getEvaluation().result).toBeTruthy();
|
||||||
});
|
});
|
||||||
@@ -341,10 +362,14 @@ describe('ConditionsManager', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
expect(manager.getEvaluation().result).toBeFalsy();
|
expect(manager.getEvaluation().result).toBeFalsy();
|
||||||
stateManager.setState({ state: { 'binary_sensor.foo': createStateEntity() } });
|
stateManager.setState({
|
||||||
|
hass: createHASS({ 'binary_sensor.foo': createStateEntity() }),
|
||||||
|
});
|
||||||
expect(manager.getEvaluation().result).toBeTruthy();
|
expect(manager.getEvaluation().result).toBeTruthy();
|
||||||
stateManager.setState({
|
stateManager.setState({
|
||||||
state: { 'binary_sensor.foo': createStateEntity({ state: 'off' }) },
|
hass: createHASS({
|
||||||
|
'binary_sensor.foo': createStateEntity({ state: 'off' }),
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
expect(manager.getEvaluation().result).toBeFalsy();
|
expect(manager.getEvaluation().result).toBeFalsy();
|
||||||
});
|
});
|
||||||
@@ -363,10 +388,10 @@ describe('ConditionsManager', () => {
|
|||||||
manager.addListener(listener);
|
manager.addListener(listener);
|
||||||
|
|
||||||
stateManager.setState({
|
stateManager.setState({
|
||||||
state: {
|
hass: createHASS({
|
||||||
'switch.one': createStateEntity({ state: 'on' }),
|
'switch.one': createStateEntity({ state: 'on' }),
|
||||||
'switch.two': createStateEntity({ state: 'off' }),
|
'switch.two': createStateEntity({ state: 'off' }),
|
||||||
},
|
}),
|
||||||
});
|
});
|
||||||
expect(listener).toHaveBeenLastCalledWith({
|
expect(listener).toHaveBeenLastCalledWith({
|
||||||
result: true,
|
result: true,
|
||||||
@@ -380,10 +405,10 @@ describe('ConditionsManager', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
stateManager.setState({
|
stateManager.setState({
|
||||||
state: {
|
hass: createHASS({
|
||||||
'switch.one': createStateEntity({ state: 'off' }),
|
'switch.one': createStateEntity({ state: 'off' }),
|
||||||
'switch.two': createStateEntity({ state: 'on' }),
|
'switch.two': createStateEntity({ state: 'on' }),
|
||||||
},
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(listener).toHaveBeenLastCalledWith({
|
expect(listener).toHaveBeenLastCalledWith({
|
||||||
@@ -411,10 +436,11 @@ describe('ConditionsManager', () => {
|
|||||||
const listener = vi.fn();
|
const listener = vi.fn();
|
||||||
manager.addListener(listener);
|
manager.addListener(listener);
|
||||||
|
|
||||||
|
const hass = createHASS({
|
||||||
|
'switch.one': createStateEntity({ state: 'on' }),
|
||||||
|
});
|
||||||
stateManager.setState({
|
stateManager.setState({
|
||||||
state: {
|
hass,
|
||||||
'switch.one': createStateEntity({ state: 'on' }),
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
expect(listener).toBeCalledTimes(1);
|
expect(listener).toBeCalledTimes(1);
|
||||||
expect(manager.getEvaluation()).toEqual({
|
expect(manager.getEvaluation()).toEqual({
|
||||||
@@ -428,11 +454,7 @@ describe('ConditionsManager', () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
stateManager.setState({
|
stateManager.setState({ hass });
|
||||||
state: {
|
|
||||||
'switch.one': createStateEntity({ state: 'on' }),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
expect(listener).toBeCalledTimes(1);
|
expect(listener).toBeCalledTimes(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -453,11 +475,11 @@ describe('ConditionsManager', () => {
|
|||||||
|
|
||||||
expect(manager.getEvaluation().result).toBeFalsy();
|
expect(manager.getEvaluation().result).toBeFalsy();
|
||||||
stateManager.setState({
|
stateManager.setState({
|
||||||
state: { 'sensor.foo': createStateEntity({ state: '11' }) },
|
hass: createHASS({ 'sensor.foo': createStateEntity({ state: '11' }) }),
|
||||||
});
|
});
|
||||||
expect(manager.getEvaluation().result).toBeTruthy();
|
expect(manager.getEvaluation().result).toBeTruthy();
|
||||||
stateManager.setState({
|
stateManager.setState({
|
||||||
state: { 'binary_sensor.foo': createStateEntity({ state: '9' }) },
|
hass: createHASS({ 'binary_sensor.foo': createStateEntity({ state: '9' }) }),
|
||||||
});
|
});
|
||||||
expect(manager.getEvaluation().result).toBeFalsy();
|
expect(manager.getEvaluation().result).toBeFalsy();
|
||||||
});
|
});
|
||||||
@@ -477,16 +499,74 @@ describe('ConditionsManager', () => {
|
|||||||
|
|
||||||
expect(manager.getEvaluation().result).toBeFalsy();
|
expect(manager.getEvaluation().result).toBeFalsy();
|
||||||
stateManager.setState({
|
stateManager.setState({
|
||||||
state: { 'sensor.foo': createStateEntity({ state: '11' }) },
|
hass: createHASS({ 'sensor.foo': createStateEntity({ state: '11' }) }),
|
||||||
});
|
});
|
||||||
expect(manager.getEvaluation().result).toBeFalsy();
|
expect(manager.getEvaluation().result).toBeFalsy();
|
||||||
stateManager.setState({
|
stateManager.setState({
|
||||||
state: { 'sensor.foo': createStateEntity({ state: '9' }) },
|
hass: createHASS({ 'sensor.foo': createStateEntity({ state: '9' }) }),
|
||||||
});
|
});
|
||||||
expect(manager.getEvaluation().result).toBeTruthy();
|
expect(manager.getEvaluation().result).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('with template condition', () => {
|
||||||
|
const createHASSForTemplateCondition = (states: HassEntities): HomeAssistant => {
|
||||||
|
const hass = createHASS(states);
|
||||||
|
vi.mocked(hass.connection.sendMessagePromise).mockResolvedValue([]);
|
||||||
|
return hass;
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should evaluate true when template evalutes to true', () => {
|
||||||
|
const stateManager = new ConditionStateManager();
|
||||||
|
const manager = new ConditionsManager(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
condition: 'template' as const,
|
||||||
|
value_template: '{{ is_state("sensor.foo", "on") }}',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
stateManager,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(manager.getEvaluation().result).toBeFalsy();
|
||||||
|
|
||||||
|
stateManager.setState({
|
||||||
|
hass: createHASSForTemplateCondition({
|
||||||
|
'sensor.foo': createStateEntity({ state: 'on' }),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
expect(manager.getEvaluation().result).toBeTruthy();
|
||||||
|
|
||||||
|
stateManager.setState({
|
||||||
|
hass: createHASSForTemplateCondition({
|
||||||
|
'sensor.foo': createStateEntity({ state: 'off' }),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
expect(manager.getEvaluation().result).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should evaluate false when template evalutes to non-boolean', () => {
|
||||||
|
const stateManager = new ConditionStateManager();
|
||||||
|
const manager = new ConditionsManager(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
condition: 'template' as const,
|
||||||
|
// This does not result in a boolean.
|
||||||
|
value_template: '{{ hass.states["light.office"].state }}',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
stateManager,
|
||||||
|
);
|
||||||
|
|
||||||
|
stateManager.setState({
|
||||||
|
hass: createHASSForTemplateCondition({
|
||||||
|
'light.office': createStateEntity({ state: 'on' }),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
expect(manager.getEvaluation().result).toBeFalsy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should not call listeners for HA state changes without relevant condition', () => {
|
it('should not call listeners for HA state changes without relevant condition', () => {
|
||||||
const stateManager = new ConditionStateManager();
|
const stateManager = new ConditionStateManager();
|
||||||
const manager = new ConditionsManager(
|
const manager = new ConditionsManager(
|
||||||
@@ -503,7 +583,7 @@ describe('ConditionsManager', () => {
|
|||||||
manager.addListener(listener);
|
manager.addListener(listener);
|
||||||
|
|
||||||
stateManager.setState({
|
stateManager.setState({
|
||||||
state: { 'sensor.foo': createStateEntity({ state: '11' }) },
|
hass: createHASS({ 'sensor.foo': createStateEntity({ state: '11' }) }),
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(listener).not.toBeCalled();
|
expect(listener).not.toBeCalled();
|
||||||
@@ -524,11 +604,11 @@ describe('ConditionsManager', () => {
|
|||||||
|
|
||||||
expect(manager.getEvaluation().result).toBeFalsy();
|
expect(manager.getEvaluation().result).toBeFalsy();
|
||||||
stateManager.setState({
|
stateManager.setState({
|
||||||
user: createUser({ id: 'user_1' }),
|
hass: createHASS({}, createUser({ id: 'user_1' })),
|
||||||
});
|
});
|
||||||
expect(manager.getEvaluation().result).toBeTruthy();
|
expect(manager.getEvaluation().result).toBeTruthy();
|
||||||
stateManager.setState({
|
stateManager.setState({
|
||||||
user: createUser({ id: 'user_WRONG' }),
|
hass: createHASS({}, createUser({ id: 'user_WRONG' })),
|
||||||
});
|
});
|
||||||
expect(manager.getEvaluation().result).toBeFalsy();
|
expect(manager.getEvaluation().result).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||||
import { ConditionStateManager } from '../../src/conditions/state-manager';
|
import { ConditionStateManager } from '../../src/conditions/state-manager';
|
||||||
import { createStateEntity } from '../test-utils';
|
import { createHASS, createStateEntity } from '../test-utils';
|
||||||
|
|
||||||
describe('ConditionStateManager', () => {
|
describe('ConditionStateManager', () => {
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -46,9 +46,9 @@ describe('ConditionStateManager', () => {
|
|||||||
expect(listener).toBeCalledTimes(1);
|
expect(listener).toBeCalledTimes(1);
|
||||||
|
|
||||||
manager.setState({
|
manager.setState({
|
||||||
state: {
|
hass: createHASS({
|
||||||
'binary_sensor.foo': createStateEntity(),
|
'binary_sensor.foo': createStateEntity(),
|
||||||
},
|
}),
|
||||||
});
|
});
|
||||||
expect(listener).toBeCalledTimes(2);
|
expect(listener).toBeCalledTimes(2);
|
||||||
|
|
||||||
@@ -56,24 +56,24 @@ describe('ConditionStateManager', () => {
|
|||||||
expect(listener).toBeCalledTimes(2);
|
expect(listener).toBeCalledTimes(2);
|
||||||
|
|
||||||
manager.setState({
|
manager.setState({
|
||||||
state: {
|
hass: createHASS({
|
||||||
'binary_sensor.foo': createStateEntity(),
|
'binary_sensor.foo': createStateEntity(),
|
||||||
},
|
}),
|
||||||
});
|
});
|
||||||
expect(listener).toBeCalledTimes(2);
|
|
||||||
|
|
||||||
manager.setState({ fullscreen: false });
|
|
||||||
expect(listener).toBeCalledTimes(3);
|
expect(listener).toBeCalledTimes(3);
|
||||||
|
|
||||||
manager.setState({ fullscreen: false });
|
manager.setState({ fullscreen: false });
|
||||||
expect(listener).toBeCalledTimes(3);
|
expect(listener).toBeCalledTimes(4);
|
||||||
|
|
||||||
|
manager.setState({ fullscreen: false });
|
||||||
|
expect(listener).toBeCalledTimes(4);
|
||||||
|
|
||||||
manager.setState({
|
manager.setState({
|
||||||
state: {
|
hass: createHASS({
|
||||||
'binary_sensor.foo': createStateEntity({ state: 'off' }),
|
'binary_sensor.foo': createStateEntity({ state: 'off' }),
|
||||||
},
|
}),
|
||||||
});
|
});
|
||||||
expect(listener).toBeCalledTimes(4);
|
expect(listener).toBeCalledTimes(5);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ export const createHASS = (states?: HassEntities, user?: CurrentUser): HomeAssis
|
|||||||
hass.user = user;
|
hass.user = user;
|
||||||
}
|
}
|
||||||
hass.connection.subscribeMessage = vi.fn();
|
hass.connection.subscribeMessage = vi.fn();
|
||||||
|
hass.connection.sendMessagePromise = vi.fn();
|
||||||
return hass;
|
return hass;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user