Chapter 10

Current State, Get Entities, and Conditional Logic

After receiving a msg, Current State v3 reads one entity from the HA WebSocket cache. Get Entities v1 scans that cache and builds a collection from state, device, area, floor, and label conditions. Neither node performs a live REST query or retrieves historical data. This chapter defines input overrides, output mapping, empty-result behavior, and result limits.

Why this chapter matters

State queries usually sit between an event and a side effect: an upstream node reports motion, then Current State checks whether the current mode permits an action; alternatively, Get Entities finds a set of candidates that match specified rules. Treating the cache as real-time truth, treating a missing entity as false, or allowing untrusted msg.payload.* properties to rewrite a query can bypass the original safety conditions.

Current State and Get Entities each accept one input message, but their contracts differ. Current State v3 can use Block Input Overrides to lock the configured entity. Get Entities v1 has no equivalent switch: it always evaluates msg.payload.rules and several output-setting overrides. This distinction determines where input must be sanitized and whether HTTP, MQTT, or webhook messages may be sent directly to the node.

Do not run the examples in this chapter: In 05-current-state.json and 06-get-entities.json, every HA query node has "d": true, and the server and entity values are PLACEHOLDER_*. Review them only as text and before import. Do not connect, deploy, or attach them to an Action node.

Cache, history, and waiting are different operations

NodePersisted typeData source or lifecyclePrimary limitation
Current State v3api-current-stateReads one cached state when a msg arrivesA cache miss raises an error; input overrides can be blocked
Get Entities v1ha-get-entitiesScans cached states when a msg arrives and consults registries when requiredNo Block Input Overrides option; the resulting collection may be large
Get Historyapi-get-historyQueries HA historyNo built-in result limit or request timeout; enforce both outside the node
Wait Untilha-wait-untilRetains one incoming msg until a condition is met or a timeout expiresOnly one active slot per node; a later input replaces the earlier one

Current State and Get Entities are appropriate for quick reads from the cache maintained by the integration. Get History is appropriate for past intervals constrained by external policy. Wait Until is appropriate for pausing one message flow with an explicit timeout. Do not use Get History to emulate frequent current-state reads, and do not treat Wait Until as a queue for multiple messages.

The WebSocket connection and state_changed events update the cache. It may be incomplete during startup, stale after a disconnection, or missing an entity that was removed; it may also hold unknown or unavailable as the state. A timestamp in the node output does not prove freshness. Safety-sensitive decisions must route connection failures and missing or indeterminate values through a fail-closed path.

Review two disabled examples safely

  1. Read the files with text tools.

    Start with 05-current-state.json and 06-get-entities.json. Confirm that each contains only a manual Inject node, a disabled HA node, and a Debug node limited to the payload. This step performs no HA operation.

  2. Check versions and placeholders.

    Current State should have the type and version api-current-state/3; Get Entities should have ha-get-entities/1. Their server and entity values must remain complete PLACEHOLDER_* values.

  3. Check the Current State override boundary.

    In the example, blockInputOverrides is true, so the configured entity_id cannot be replaced by msg.payload.entity_id or msg.payload.entityId. Retain this safe default.

  4. Remove Get Entities override fields.

    Define a contract that rejects externally supplied payload.rules, outputType, outputEmptyResults, outputLocationType, outputLocation, and outputResultsCount. Get Entities v1 has no checkbox that can replace this allowlist.

  5. Define empty-result behavior and hard limits.

    Decide whether 0 matches produce an empty array, the number 0, or no message. Then set limits for the number of candidate entities, the fields exposed to Debug, and downstream fan-out. Do not assume that the small size of a test environment is a permanent bound.

  6. Test failure paths first.

    Use a paper review or unit tests to cover cache misses, unknown, unavailable, 0 matches, excess matches, and missing registry metadata. Route output only to an isolated Debug node. Keep HA nodes disabled and side effects disconnected until the deployment-site review is complete.

Neither example is a one-click recipe. Importing a flow only places its JSON in the editor; do not deploy it until the safety review is complete. Real entity IDs, device, area, floor, or label IDs, and complete attributes must not appear in screenshots, issues, or public flows.

Offline three-case exercise: Keep the Current State and Get Entities nodes disabled and disconnected from HA, and keep Action disconnected throughout. Use placeholder data only to compare the following output contracts.

Test scenarioPaper inputExpected output or no action
Current State cache hitPLACEHOLDER_ENTITY_ID maps to PLACEHOLDER_STATEA limited Debug node receives msg.payload="PLACEHOLDER_STATE" for inspection only; Action remains disconnected
Current State cache missPLACEHOLDER_ENTITY_ID is absent from the cacheAn InputError is thrown and Catch routes to NO_ACTION_CACHE_MISS; there is no normal output
Get Entities returns 0 or exceeds the approved limitFixed rules return 0, or PLACEHOLDER_RESULT_COUNT > PLACEHOLDER_APPROVED_LIMITCount may output 0; the count gate rejects an excess result. Neither case sends array or split output to an Action node

Current State v3: one entity, comparison, and mapping

In v3, InputService defines entityId as the only overridable input. It checks msg.payload.entity_id first, also accepts the camel-case msg.payload.entityId, and otherwise uses the configured entity_id. The entity ID is also rendered through Mustache. When Block Input Overrides is true, InputService disables message overrides and the configured value takes precedence. When it is false, any upstream source that can write either payload property can make the node query a different entity.

The controller reads the cache through WebSocket getState(entityId). If the entity is absent, it throws a “not found” InputError; it does not send a normal message with state=false. When the entity is found, the controller adds timeSinceChangedMs. If a non-string state type is selected, it preserves original_state before conversion. This object comes from the cache; downstream nodes must not mutate its nested attributes indiscriminately.

Fieldv3 behaviorSafety recommendation
Entity IDThe configured value may contain Mustache; two payload keys can override it when overrides are not blockedDocument it with a complete placeholder; keep Block Input Overrides enabled
If StateCompares the current entity.state; when configured, true and false use separate outputsCompare strings explicitly and route unknown and unavailable separately
ForTests whether timeSinceChangedMs is greater than the specified durationTreat it as a cache-timestamp calculation, not a waiting timer or proof of history
State TypeDefaults to string; a non-string conversion preserves original_stateAvoid the common trap in which a nonempty string converts to Boolean true
Output PropertiesDefaults to the state string in payload and the complete entity in dataMap only allowlisted fields; do not assume that payload always has the default meaning

Without If State, the node normally has only its standard output. With If State, or with the HA Boolean type, a true condition uses the first output and a false condition uses the second; the second output is not an error channel. The For setting applies only after the condition itself matches and only with the comparators is, is not, includes, and does not include. Its duration may come from a number, JSONata, msg, flow context, or global context; a negative duration raises an error.

The time test is entity.timeSinceChangedMs > forDurationMs: strictly greater than, not greater than or equal to. It is calculated from last_changed; it neither waits for the duration to elapse nor queries the recorder. To wait for a condition, evaluate Wait Until and set a timeout. To establish what happened over a past interval, use a constrained Get History query.

Get Entities v1: rules, overrides, and result modes

Each time v1 receives a msg, it obtains the cached states and evaluates its rules for each entity. A state condition can read a property of the state object. Device, area, floor, and label conditions require the entity registry and related registry metadata. An area may come directly from the entity or from its device; a floor comes from the area; and a label condition checks labels on the entity, device, and area. A condition does not match when its required metadata is missing.

Multiple rules are evaluated individually, and one failed rule excludes the entity; the rules therefore have AND semantics. At runtime, the node sorts rules into label, state, device, area, and floor order before evaluating them, but no side effect should depend on that order. Rules must be pure comparisons. Except for the JSONata case, a missing state property does not match.

outputTypeOutput shapeBehavior with 0 resultsLimit to enforce
arrayWrites a HassEntity array to the specified msg, flow, or global locationSends an empty array only when outputEmptyResults=true; otherwise sends no messageNo built-in maximum result count; enforce a limit before downstream processing
countWrites a number to the specified locationSends 0Contains no entity details, making it suitable for an initial threshold check
randomReturns one entity when the limit is 1, or an array when the limit is greater than 1Sends no messageoutputResultsCount controls only Random; validate a message override as an integer ≥1
splitSends one msg per entity, with that entity in payloadSends no messageFans out; estimate the count and downstream capacity first

Array, Count, and Random can write their result to a specified location in msg, flow context, or global context. Split does not expose an output-location setting; it always places each entity in msg.payload. Flow or global context preserves data across messages and may retain complete attributes. Unless retention and authorized readers are explicitly defined, use msg and reduce the fields immediately.

Random provides sampling, not secure allocation, fair rotation, or cryptographic randomness. Split fans out, so limit the candidate count before selecting this mode. If a Join node follows, also bound the sequences it may retain and set a timeout.

Version 0.80.3 detail: Random shuffles the results and then takes the requested number. Split deletes the original message’s _msgid, creates msg.parts with a new sequence id, count, and per-message index, and then clones each message. A downstream Join node should identify the sequence by msg.parts.

Exact resource boundaries for Get History and Wait Until

Get History 0.80.3 reads six overrides from msg.payload: startDate, endDate, entityId, entityIdType, relativeTime, and flatten. It has no Block Input Overrides option. Before an untrusted input reaches the node, delete all six properties or rebuild a clean payload from an allowlist and a date schema. The node also has no built-in result-count limit, and its underlying Axios client sets no request timeout. Short time windows, single-entity restrictions, response-size budgets, proxy or caller timeouts, and cancellation policies must all be enforced outside the node; none is a node guarantee.

Each Wait Until controller has one active slot containing one message and configuration. A later input cancels the current timer and replaces the stored message; this is not a pending-message queue. timeout=0 creates no timer at all, so the node may wait until the condition is met, it receives a reset, another input replaces the message, or the node stops. A safe configuration uses a finite positive timeout and separate outputs for success and timeout. A timeout means only that the condition was not observed before the deadline; it must not directly trigger a side effect.

Input overrides, output mapping, and data minimization

Current State’s Block Input Overrides option is an explicit safety boundary: when enabled, a message cannot change the entity ID. Get Entities has no equivalent option. Its InputService always treats the following message properties as candidate settings, and an upstream payload with a valid type can replace the value configured in the editor.

Message propertyWhat it can changeHandling for untrusted input
msg.payload.rulesThe complete set of filtering rulesDelete it; only a trusted Change node should create the fixed allowlisted rules
msg.payload.outputTypearray/count/random/splitDelete it to prevent a change to Split that amplifies messages
msg.payload.outputEmptyResultsWhether an empty array is emittedDelete it to prevent changes to control flow
msg.payload.outputLocationTypemsg/flow/globalDelete it to prevent writes across scopes
msg.payload.outputLocationThe destination property or pathDelete it to prevent overwriting other message or context properties
msg.payload.outputResultsCountThe Random result limit; the runtime schema checks only that it is a numberDelete it; if it is explicitly permitted, first validate it as an integer ≥1

Validating and then forwarding the original payload is still insufficient because the dangerous override keys remain present. Instead, create a new msg, or delete each override property before a trusted node writes fixed rules. Inputs from HTTP, MQTT, webhooks, and similar sources require both source authentication and schema validation. Even a schema-valid input must not be allowed to specify an arbitrary property path or global location.

Output mapping requires a data contract. The Current State example writes the state string to payload and the entity object to data; the Get Entities example writes an array to payload. If downstream processing needs only the state and entity_id, do not pass all attributes. Configure Debug for a specific property and disable it after testing. A complete entity may contain location, person, device, media, or diagnostic information.

As a safe read-only threshold, one Get Entities query can use Count to confirm that the candidate count is within the expected range before a second constrained query retrieves details. The cache may change between scans, however, so this approach does not provide transactional consistency. When business logic requires atomic semantics, implement them in a more appropriate HA action or automation design rather than inferring them from two Node-RED reads.

Missing entities, unknown or unavailable states, and result limits

When a Current State entity is absent from the cache, the node raises an error. Capture it with Catch and observe the node with Status; record only a sanitized entity placeholder, the node name, and the error category, then terminate the control flow. Do not automatically convert the error to false and pass it to an Action node. By contrast, unknown and unavailable are normal string values on an entity that was found. Route each explicitly through a side-effect-free branch in If State or Switch.

Get Entities may return 0 matches because its rules are too strict, a registry has not loaded, metadata is missing, or the cache is empty. For Array, outputEmptyResults determines whether the node emits one message containing an empty array or emits no message. Count emits 0. Random and Split emit no message for 0 matches. If downstream logic uses Timeout or Complete nodes, define which contract applies; silence is not success.

Get Entities v1 has no general maximum result count for Array or Split. outputResultsCount applies only to Random, and a value supplied by a message is checked only for the number type. Upstream validation must also reject fractional, 0, and negative values so that the value is an integer ≥1. If a selector may cover many entities, narrow its rules first, then use a downstream count gate to reject an excess result. Do not split first and throttle afterward. For flow or global output, set a separate retention period and size budget.

For Get History, the entity allowlist, short time window, acceptable result count, and request timeout are external policies, not built-in 0.80.3 constraints. This example library has no dedicated Get History flow. Only Wait Until has a field-safe example, 08-wait-until.json (disabled node, placeholders, 10-second timeout, and two outputs). It demonstrates a finite wait in one active slot; it is not a replacement for this chapter’s two cache-query examples.

Fail-closed decision table: A cache miss, unknown, unavailable, an excess result count, missing registry metadata, or an HA disconnection must enter a side-effect-free branch. Only when the data exists, its type is correct, its age is acceptable, the candidate count is within the limit, and the condition is unambiguously true may the message continue to the next pure-logic node. Whether an Action is permissible still requires the independent review described in Chapter 11.

Troubleshooting

  • Current State reports “not found”: Do not fabricate a false-valued message. Check that a placeholder was not mistaken for a production ID, that the cache is ready, whether the entity was removed or renamed, and whether Block Input Overrides is rejecting an expected override. Keep the side-effect branch disabled.
  • The query returns a different entity from the configured one: Check whether Block Input Overrides is disabled and whether upstream input contains payload.entity_id or payload.entityId. Re-enable the block, remove the message property, and repeat the paper contract test.
  • Get Entities rules are correct in the editor but differ at runtime: Check the upstream payload for all six categories of override. v1 has no block switch; use a Change node to create a new message rather than preserving an external payload.
  • Downstream receives no message for 0 results: Check outputType. Array requires outputEmptyResults=true to emit an empty array; Count emits 0; Random and Split emit no message. Do not mistake silence for node failure.
  • Split produces too many messages: Immediately disconnect downstream side effects. Use Count to estimate the result first, narrow the rules, and set a hard limit. outputResultsCount does not apply to Split.
  • A For comparison narrowly fails: Current State compares timeSinceChangedMs as strictly greater than the duration and derives it from last_changed. Check the clock, whether the state actually changed, and the duration type and unit. Do not confuse this test with recorder history.
  • A device, area, floor, or label condition omits an entity: Its registry entry or associated metadata may be absent. Check for missing data in this order: state, entity registry, device, area, floor, and label. Do not work around the omission by broadening the rule to a global state match.

Pinned sources

If the continuously updated official documentation differs from the pinned 0.80.3 commit, the pinned commit defines this chapter’s exact semantics. Safe downloads: 05-current-state.json and 06-get-entities.json. Both use placeholders and keep their HA nodes disabled.

FAQ

Does Current State make an immediate request to the HA API?
No. v3 reads one state from the cache maintained by the HA WebSocket integration. Handle disconnections, startup, and cache misses separately; a cached object is not evidence of a real-time API round trip.
Does Block Input Overrides protect Get Entities?
No. Current State and Action provide this option, but Get Entities v1 does not. Upstream processing must delete or validate all six categories of msg.payload.* override.
Does outputResultsCount limit results in every mode?
No. The controller uses it only in Random mode. Array can still return the complete collection, Split can still fan out one message per entity, and Count returns only the number of matches. If message overrides are permitted, validate the value as an integer ≥1; validating only its number type does not establish a safe limit.
Does Current State send false from its second output when an entity is missing?
No. A cache miss throws an InputError. The second output represents a false If State condition, not an error channel. Use Catch and Status to distinguish the cases.
How does Split identify messages in the same sequence?
v1 creates msg.parts.id, count, and index, then clones one message per entity with that entity as its payload. Downstream logic must still limit the sequence count and bound Join’s wait with a timeout.
Can a complete entity array be stored in global context for every flow?
The output location technically supports global context, but it is not a safe default. Doing so retains attributes for longer and creates stale copies. Prefer placing the minimum required fields in msg; use context only after defining retention and authorized readers.