node-red-contrib-home-assistant-websocket
Version:
Node-RED integration with Home Assistant through websocket and REST API
187 lines (186 loc) • 10.5 kB
JavaScript
"use strict";
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var _EventsStateController_instances, _EventsStateController_comparatorService, _EventsStateController_topics, _EventsStateController_transformState, _EventsStateController_getTimerValue, _EventsStateController_isEventValid;
Object.defineProperty(exports, "__esModule", { value: true });
const lodash_1 = require("lodash");
const ExposeAsMixin_1 = __importDefault(require("../../common/controllers/ExposeAsMixin"));
const OutputController_1 = __importDefault(require("../../common/controllers/OutputController"));
const ConfigError_1 = __importDefault(require("../../common/errors/ConfigError"));
const TransformState_1 = require("../../common/TransformState");
const utils_1 = require("../../helpers/utils");
var State;
(function (State) {
State["Unknown"] = "unknown";
State["Unavailable"] = "unavailable";
})(State || (State = {}));
const ExposeAsController = (0, ExposeAsMixin_1.default)((OutputController_1.default));
class EventsStateController extends ExposeAsController {
constructor(props) {
super(props);
_EventsStateController_instances.add(this);
_EventsStateController_comparatorService.set(this, void 0);
_EventsStateController_topics.set(this, {});
_EventsStateController_transformState.set(this, void 0);
__classPrivateFieldSet(this, _EventsStateController_comparatorService, props.comparatorService, "f");
__classPrivateFieldSet(this, _EventsStateController_transformState, props.transformState, "f");
}
async onHaEventsStateChanged(evt, runAll = false) {
if (this.isEnabled === false ||
!this.homeAssistant.isHomeAssistantRunning ||
!__classPrivateFieldGet(this, _EventsStateController_instances, "m", _EventsStateController_isEventValid).call(this, evt)) {
return;
}
const config = this.node.config;
const eventMessage = (0, lodash_1.cloneDeep)(evt);
const entityId = eventMessage.entity_id;
const oldEntity = eventMessage.event.old_state;
const newEntity = eventMessage.event.new_state;
// Convert and save original state if needed
if (oldEntity && this.node.config.stateType !== TransformState_1.TransformType.String) {
oldEntity.original_state = oldEntity.state;
oldEntity.state = __classPrivateFieldGet(this, _EventsStateController_transformState, "f").transform(this.node.config.stateType, oldEntity.state);
}
if (newEntity && this.node.config.stateType !== TransformState_1.TransformType.String) {
newEntity.original_state = newEntity.state;
newEntity.state = __classPrivateFieldGet(this, _EventsStateController_transformState, "f").transform(this.node.config.stateType, newEntity.state);
}
const oldState = oldEntity ? oldEntity.state : undefined;
const newState = newEntity ? newEntity.state : undefined;
// Output only on state change
if (runAll === false &&
config.outputOnlyOnStateChange === true &&
oldState === newState) {
return;
}
// Get if state condition
const isIfState = await __classPrivateFieldGet(this, _EventsStateController_comparatorService, "f").getComparatorResult(config.ifStateOperator, config.ifState, newState, config.ifStateType, {
entity: newEntity !== null && newEntity !== void 0 ? newEntity : undefined,
prevEntity: oldEntity !== null && oldEntity !== void 0 ? oldEntity : undefined,
});
// Track multiple entity ids
__classPrivateFieldGet(this, _EventsStateController_topics, "f")[entityId] = __classPrivateFieldGet(this, _EventsStateController_topics, "f")[entityId] || { active: false };
const timer = await __classPrivateFieldGet(this, _EventsStateController_instances, "m", _EventsStateController_getTimerValue).call(this);
const validTimer = timer > 0;
if (validTimer) {
if (
// If the ifState is not used and prev and current state are the same return because timer should already be running
oldState === newState ||
// Don't run timers for on connection updates
runAll ||
// Timer already active and ifState is still true turn don't update
(config.ifState && isIfState && __classPrivateFieldGet(this, _EventsStateController_topics, "f")[entityId].active)) {
return;
}
if (config.ifState && !isIfState) {
__classPrivateFieldGet(this, _EventsStateController_topics, "f")[entityId].active = false;
}
}
if (!validTimer || (config.ifState && !isIfState)) {
await this.output(eventMessage, isIfState);
return;
}
const statusText = (0, utils_1.getWaitStatusText)(timer, this.node.config.forUnits);
const timeout = (0, utils_1.getTimeInMilliseconds)(timer, this.node.config.forUnits);
this.status.setText(statusText);
clearTimeout(__classPrivateFieldGet(this, _EventsStateController_topics, "f")[entityId].timeoutId);
__classPrivateFieldGet(this, _EventsStateController_topics, "f")[entityId] = {
active: true,
timeoutId: setTimeout(this.output.bind(this, eventMessage, isIfState), timeout),
};
}
async output(eventMessage, condition) {
var _a, _b;
const config = this.node.config;
const message = {};
await this.setCustomOutputs(config.outputProperties, message, {
config,
entity: eventMessage.event.new_state,
entityState: (_a = eventMessage.event.new_state) === null || _a === void 0 ? void 0 : _a.state,
eventData: eventMessage.event,
prevEntity: eventMessage.event.old_state,
triggerId: eventMessage.entity_id,
});
if (eventMessage.event.new_state) {
eventMessage.event.new_state.timeSinceChangedMs =
Date.now() -
new Date(eventMessage.event.new_state.last_changed).getTime();
}
const statusMessage = `${(_b = eventMessage.event.new_state) === null || _b === void 0 ? void 0 : _b.state}`;
clearTimeout(__classPrivateFieldGet(this, _EventsStateController_topics, "f")[eventMessage.entity_id].timeoutId);
if (config.ifState && !condition) {
this.status.setFailed(statusMessage);
this.node.send([null, message]);
return;
}
this.status.setSuccess(statusMessage);
this.node.send([message, null]);
}
onDeploy() {
const entities = this.homeAssistant.websocket.getStates();
this.onStatesLoaded(entities);
}
onStatesLoaded(entities) {
if (!this.isEnabled)
return;
for (const entityId in entities) {
const eventMessage = {
event_type: 'state_changed',
entity_id: entityId,
event: {
entity_id: entityId,
old_state: entities[entityId],
new_state: entities[entityId],
},
};
this.onHaEventsStateChanged(eventMessage, true);
}
}
}
_EventsStateController_comparatorService = new WeakMap(), _EventsStateController_topics = new WeakMap(), _EventsStateController_transformState = new WeakMap(), _EventsStateController_instances = new WeakSet(), _EventsStateController_getTimerValue = async function _EventsStateController_getTimerValue() {
if (this.node.config.for === '')
return 0;
const timer = await this.typedInputService.getValue(this.node.config.for, this.node.config.forType);
if (isNaN(timer) || timer < 0) {
throw new ConfigError_1.default([
'server-state-changed.error.invalid_for',
{ for: timer, type: this.node.config.forType },
]);
}
return Number(timer);
}, _EventsStateController_isEventValid = function _EventsStateController_isEventValid(evt) {
var _a, _b, _c, _d;
const oldState = (_b = (_a = evt.event) === null || _a === void 0 ? void 0 : _a.old_state) === null || _b === void 0 ? void 0 : _b.state;
const newState = (_d = (_c = evt.event) === null || _c === void 0 ? void 0 : _c.new_state) === null || _d === void 0 ? void 0 : _d.state;
const valid = Object.entries(this.node.config.entities).some(([type, ids]) => {
return ids === null || ids === void 0 ? void 0 : ids.some((id) => (0, utils_1.shouldIncludeEvent)(evt.entity_id, id, type));
});
if (!valid) {
return false;
}
if ((this.node.config.ignorePrevStateNull && !evt.event.old_state) ||
(this.node.config.ignorePrevStateUnknown &&
oldState === State.Unknown) ||
(this.node.config.ignorePrevStateUnavailable &&
oldState === State.Unavailable) ||
(this.node.config.ignoreCurrentStateUnknown &&
newState === State.Unknown) ||
(this.node.config.ignoreCurrentStateUnavailable &&
newState === State.Unavailable)) {
return false;
}
return true;
};
exports.default = EventsStateController;