Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/helpers/mustache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ function parseContext(key: string) {

class CustomContext extends Context {
readonly #nodeContext: NodeContext;
readonly #entities: HassEntities;
readonly #entities: Readonly<HassEntities>;

constructor(
view: any,
parentContext: CustomContext | undefined,
nodeContext: NodeContext,
entities: HassEntities,
entities: Readonly<HassEntities>,
) {
super(view, parentContext);
this.#nodeContext = nodeContext;
Expand Down Expand Up @@ -93,7 +93,7 @@ export function renderTemplate(
str: string,
message: NodeMessage,
nodeContext: NodeContext,
entities: HassEntities,
entities: Readonly<HassEntities>,
altTags = false,
): string {
if (
Expand All @@ -115,7 +115,7 @@ export function renderTemplate(
export function generateRenderTemplate(
message: NodeMessage,
context: NodeContext,
states: HassEntities,
states: Readonly<HassEntities>,
) {
return (template: string, altTags = false) =>
renderTemplate(template, message, context, states, altTags);
Expand Down
9 changes: 9 additions & 0 deletions src/homeAssistant/Websocket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -716,6 +716,15 @@ export default class Websocket {
return cloneDeep(this.states);
}

/**
* Return the current state map for internal read-only consumers.
* Callers must not modify the returned object. Use getStates() when an
* independent snapshot is required.
*/
getStatesReadOnly(): Readonly<HassEntities> {
return this.states;
}

getState(entityId: string): HassEntity | undefined {
return cloneDeep(this.states[entityId]);
}
Expand Down
4 changes: 2 additions & 2 deletions src/nodes/action/ActionController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export default class ActionController extends InputOutputController<
throw new NoConnectionError();
}

const states = this.homeAssistant.websocket.getStates();
const states = this.homeAssistant.websocket.getStatesReadOnly();

const render = generateRenderTemplate(
message,
Expand Down Expand Up @@ -245,7 +245,7 @@ export default class ActionController extends InputOutputController<
const render = generateRenderTemplate(
message,
this.node.context(),
this.homeAssistant.websocket.getStates(),
this.homeAssistant.websocket.getStatesReadOnly(),
);

const map: Record<string, string> = {
Expand Down
2 changes: 1 addition & 1 deletion src/nodes/api/ApiController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export default class ApiController extends InputOutputController<
const renderTemplate = generateRenderTemplate(
message,
this.node.context(),
this.homeAssistant.websocket.getStates(),
this.homeAssistant.websocket.getStatesReadOnly(),
);

let data;
Expand Down
2 changes: 1 addition & 1 deletion src/nodes/current-state/CurrentStateController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export default class CurrentStateController extends InputOutputController<
parsedMessage.entityId.value,
message,
this.node.context(),
this.#homeAssistant.websocket.getStates(),
this.#homeAssistant.websocket.getStatesReadOnly(),
);

const entity = this.#homeAssistant.websocket.getState(
Expand Down
4 changes: 2 additions & 2 deletions src/nodes/fire-event/FireEventController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export default class FireEventController extends InputOutputController<
parsedMessage.event.value,
message,
this.node.context(),
this.homeAssistant.websocket.getStates(),
this.homeAssistant.websocket.getStatesReadOnly(),
);

let eventData: unknown;
Expand All @@ -49,7 +49,7 @@ export default class FireEventController extends InputOutputController<
: parsedMessage.data.value,
message,
this.node.context(),
this.homeAssistant.websocket.getStates(),
this.homeAssistant.websocket.getStatesReadOnly(),
);
try {
eventData = JSON.parse(dataString);
Expand Down
2 changes: 1 addition & 1 deletion src/nodes/get-entities/GetEntitiesController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ export default class GetEntitiesController extends SendSplitController {
): Promise<HassEntity[]> {
const currentTime = Date.now();
const filteredEntities: HassEntity[] = [];
const states = this.#homeAssistant.websocket.getStates();
const states = this.#homeAssistant.websocket.getStatesReadOnly();
const sortedConditions = sortConditions(conditions);

const stateKeys = Object.keys(states);
Expand Down
4 changes: 2 additions & 2 deletions src/nodes/get-history/GetHistoryController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,12 @@ export default class GetHistoryController extends SendSplitController {
parsedMessage.entityId.value,
message,
this.node.context(),
this.homeAssistant.websocket.getStates(),
this.homeAssistant.websocket.getStatesReadOnly(),
);
}
if (parsedMessage.entityIdType.value === EntityFilterType.Regex) {
const entities = Object.keys(
this.homeAssistant.websocket.getStates(),
this.homeAssistant.websocket.getStatesReadOnly(),
);
const regex = new RegExp(entityId);
entityId = entities
Expand Down
2 changes: 1 addition & 1 deletion src/nodes/tag/issue-check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default function issueCheck(config: TagNodeProperties): Issue[] {
if (!ha) {
return issues;
}
const states = ha.websocket.getStates();
const states = ha.websocket.getStatesReadOnly();
const tagStates = Object.values(states).filter((state) =>
state.entity_id.startsWith('tag.'),
);
Expand Down
2 changes: 1 addition & 1 deletion src/nodes/trigger-state/TriggerStateController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ export default class TriggerStateController extends ExposeAsController {
output.messageValue,
eventMessage.event as NodeMessage,
this.node.context(),
this.homeAssistant.websocket.getStates(),
this.homeAssistant.websocket.getStatesReadOnly(),
);
}

Expand Down
2 changes: 1 addition & 1 deletion src/nodes/trigger-state/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import HomeAssistant from '../../homeAssistant/HomeAssistant';
import { HassStateChangedEvent } from '../../types/home-assistant';

export function createStateChangeEvents(homeAssistant: HomeAssistant) {
const entities = homeAssistant.websocket.getStates();
const entities = homeAssistant.websocket.getStatesReadOnly();

const events: HassStateChangedEvent[] = [];
for (const entityId in entities) {
Expand Down
2 changes: 1 addition & 1 deletion src/nodes/wait-until/WaitUntilController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ export default class WaitUntil extends InputOutputController<
e,
message,
this.node.context(),
this.#homeAssistant.websocket.getStates(),
this.#homeAssistant.websocket.getStatesReadOnly(),
),
);
}
Expand Down
2 changes: 1 addition & 1 deletion src/nodes/zone/ZoneController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export default class Zone extends ExposeAsController {
}

#getZones() {
const entities = this.homeAssistant.websocket.getStates();
const entities = this.homeAssistant.websocket.getStatesReadOnly();
const zones: HassEntity[] = [];
for (const entityId in entities) {
if (this.node.config.zones.includes(entityId)) {
Expand Down
33 changes: 33 additions & 0 deletions test/unit/homeAssistant/Websocket.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { HassEntities } from 'home-assistant-js-websocket';
import { describe, expect, it } from 'vitest';

import Websocket from '../../../src/homeAssistant/Websocket';

describe('Websocket state access', function () {
it('keeps getStates as an independent snapshot', function () {
const websocket = Object.create(Websocket.prototype) as Websocket;
websocket.states = {
'sensor.example': {
entity_id: 'sensor.example',
state: 'on',
},
} as HassEntities;

const snapshot = websocket.getStates();
snapshot['sensor.example'].state = 'off';

expect(websocket.getState('sensor.example')?.state).toBe('on');
});

it('provides the current map to explicitly read-only internal consumers', function () {
const websocket = Object.create(Websocket.prototype) as Websocket;
websocket.states = {
'sensor.example': {
entity_id: 'sensor.example',
state: 'on',
},
} as HassEntities;

expect(websocket.getStatesReadOnly()).toBe(websocket.states);
});
});