node-red-contrib-home-assistant-websocket
Version:
Node-RED integration with Home Assistant through websocket and REST API
255 lines (254 loc) • 14.3 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 _TriggerStateController_instances, _TriggerStateController_comparatorService, _TriggerStateController_state, _TriggerStateController_transformState, _TriggerStateController_getConstraintComparatorResults, _TriggerStateController_getConstraintTargetData, _TriggerStateController_getCustomOutputsComparatorResults, _TriggerStateController_getDefaultMessageOutputs, _TriggerStateController_getOutputMessage;
Object.defineProperty(exports, "__esModule", { value: true });
const lodash_1 = require("lodash");
const selectn_1 = __importDefault(require("selectn"));
const ExposeAsMixin_1 = __importDefault(require("../../common/controllers/ExposeAsMixin"));
const InputOutputController_1 = __importDefault(require("../../common/controllers/InputOutputController"));
const ConfigError_1 = __importDefault(require("../../common/errors/ConfigError"));
const TransformState_1 = require("../../common/TransformState");
const assert_1 = require("../../helpers/assert");
const mustache_1 = require("../../helpers/mustache");
const utils_1 = require("../../helpers/utils");
const homeAssistant_1 = require("../../homeAssistant");
const const_1 = require("./const");
const ExposeAsController = (0, ExposeAsMixin_1.default)((InputOutputController_1.default));
class TriggerStateController extends ExposeAsController {
constructor(props) {
super(props);
_TriggerStateController_instances.add(this);
_TriggerStateController_comparatorService.set(this, void 0);
_TriggerStateController_state.set(this, void 0);
_TriggerStateController_transformState.set(this, void 0);
__classPrivateFieldSet(this, _TriggerStateController_comparatorService, props.comparatorService, "f");
__classPrivateFieldSet(this, _TriggerStateController_state, props.state, "f");
__classPrivateFieldSet(this, _TriggerStateController_transformState, props.transformState, "f");
}
get isEnabled() {
var _a, _b;
if (this.exposeAsConfigNode) {
return super.isEnabled;
}
return (_b = (_a = __classPrivateFieldGet(this, _TriggerStateController_state, "f")) === null || _a === void 0 ? void 0 : _a.isEnabled()) !== null && _b !== void 0 ? _b : true;
}
onInputEnabled(message) {
var _a;
const enable = message.payload === const_1.ENABLE;
(_a = __classPrivateFieldGet(this, _TriggerStateController_state, "f")) === null || _a === void 0 ? void 0 : _a.setEnabled(enable);
this.enableExposeAs(enable);
return true;
}
onInputTesting(message) {
const { entity_id: entityId, new_state: newState, old_state: oldState, } = message.payload;
if (entityId && newState && oldState) {
const evt = {
event_type: homeAssistant_1.HaEvent.StateChanged,
entity_id: entityId,
event: message.payload,
};
this.onEntityStateChanged(evt);
}
return true;
}
async onEntityStateChanged(evt) {
if (this.isEnabled === false ||
!this.homeAssistant.isHomeAssistantRunning) {
return;
}
const eventMessage = (0, lodash_1.cloneDeep)(evt);
if (!eventMessage.event.new_state) {
return;
}
// Check if the entity_id is in the list of entities to watch
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)(eventMessage.entity_id, id, type));
});
// If the entity_id is not in the list of entities to watch, return
if (!valid) {
return;
}
// Convert and save original state if needed
if (eventMessage.event.old_state &&
this.node.config.stateType !== TransformState_1.TransformType.String) {
eventMessage.event.old_state.original_state = eventMessage.event
.old_state.state;
eventMessage.event.old_state.state = __classPrivateFieldGet(this, _TriggerStateController_transformState, "f").transform(this.node.config.stateType, eventMessage.event.old_state.state);
}
if (eventMessage.event.new_state &&
this.node.config.stateType !== TransformState_1.TransformType.String) {
eventMessage.event.new_state.original_state = eventMessage.event
.new_state.state;
eventMessage.event.new_state.state = __classPrivateFieldGet(this, _TriggerStateController_transformState, "f").transform(this.node.config.stateType, eventMessage.event.new_state.state);
}
if (eventMessage.event.new_state) {
eventMessage.event.new_state.timeSinceChangedMs =
Date.now() -
new Date(eventMessage.event.new_state.last_changed).getTime();
}
const constraintComparatorResults = await __classPrivateFieldGet(this, _TriggerStateController_instances, "m", _TriggerStateController_getConstraintComparatorResults).call(this, this.node.config.constraints, eventMessage);
let outputs = __classPrivateFieldGet(this, _TriggerStateController_instances, "m", _TriggerStateController_getDefaultMessageOutputs).call(this, constraintComparatorResults, eventMessage);
const stateString = eventMessage.event.new_state.state.toString();
// If a constraint comparator failed we're done, also if no custom outputs to look at
if (constraintComparatorResults.failed.length ||
!this.node.config.customOutputs.length) {
if (constraintComparatorResults.failed.length) {
this.status.setFailed(stateString);
}
else {
this.status.setSuccess(stateString);
}
this.debugToClient(`done processing sending messages:`);
this.debugToClient(outputs);
this.node.send(outputs);
return;
}
const customOutputsComparatorResults = await __classPrivateFieldGet(this, _TriggerStateController_instances, "m", _TriggerStateController_getCustomOutputsComparatorResults).call(this, this.node.config.customOutputs, eventMessage);
const customOutputMessages = customOutputsComparatorResults.map((r) => r.message);
outputs = outputs.concat(customOutputMessages);
this.debugToClient(`done processing sending messages: ${outputs}`);
this.status.setSuccess(stateString);
this.node.send(outputs);
}
}
_TriggerStateController_comparatorService = new WeakMap(), _TriggerStateController_state = new WeakMap(), _TriggerStateController_transformState = new WeakMap(), _TriggerStateController_instances = new WeakSet(), _TriggerStateController_getConstraintComparatorResults = async function _TriggerStateController_getConstraintComparatorResults(constraints, eventMessage) {
const comparatorResults = [];
// Check constraints
for (const constraint of constraints) {
const { comparatorType, comparatorValue, comparatorValueDatatype, propertyValue, } = constraint;
const constraintTarget = __classPrivateFieldGet(this, _TriggerStateController_instances, "m", _TriggerStateController_getConstraintTargetData).call(this, constraint, eventMessage.event);
const actualValue = (0, selectn_1.default)(constraint.propertyValue, constraintTarget.state);
const comparatorResult = await __classPrivateFieldGet(this, _TriggerStateController_comparatorService, "f").getComparatorResult(comparatorType, comparatorValue, actualValue, comparatorValueDatatype, {
entity: eventMessage.event.new_state,
prevEntity: eventMessage.event.old_state,
});
if (comparatorResult === false) {
this.debugToClient(`constraint comparator: failed entity "${constraintTarget.entityId}" property "${propertyValue}" with value ${actualValue} failed "${comparatorType}" check against (${comparatorValueDatatype}) ${comparatorValue}`);
}
comparatorResults.push({
constraint,
constraintTarget,
actualValue,
comparatorResult,
});
}
const failedComparators = comparatorResults.filter((res) => !res.comparatorResult);
return {
all: comparatorResults,
failed: failedComparators,
};
}, _TriggerStateController_getConstraintTargetData = function _TriggerStateController_getConstraintTargetData(constraint, triggerEvent) {
const isTargetThisEntity = constraint.targetType === const_1.TargetType.ThisEntity;
const entityId = isTargetThisEntity
? triggerEvent.entity_id
: constraint.targetValue;
const entity = isTargetThisEntity
? triggerEvent
: this.homeAssistant.websocket.getState(entityId);
if (entity === null) {
throw new ConfigError_1.default([
'trigger-state.error.entity_id_not_found',
{ entity_id: entityId },
]);
}
const targetData = {
entityId,
state: entity,
};
if (!isTargetThisEntity &&
constraint.propertyType === const_1.PropertyType.CurrentState) {
targetData.state = {
entity_id: entityId,
old_state: null,
new_state: this.homeAssistant.websocket.getState(entityId),
};
}
return targetData;
}, _TriggerStateController_getCustomOutputsComparatorResults = async function _TriggerStateController_getCustomOutputsComparatorResults(outputs, eventMessage) {
return outputs.reduce(async (acc, output) => {
const result = {
output,
comparatorMatched: true,
actualValue: null,
message: null,
};
if (output.comparatorPropertyType !==
const_1.ComparatorPropertyType.Always) {
result.actualValue = (0, selectn_1.default)(output.comparatorPropertyValue, eventMessage.event);
result.comparatorMatched =
await __classPrivateFieldGet(this, _TriggerStateController_comparatorService, "f").getComparatorResult(output.comparatorType, output.comparatorValue, result.actualValue, output.comparatorValueDataType, {
entity: eventMessage.event.new_state,
prevEntity: eventMessage.event.old_state,
});
}
result.message = await __classPrivateFieldGet(this, _TriggerStateController_instances, "m", _TriggerStateController_getOutputMessage).call(this, result, eventMessage);
return [...(await acc), result];
}, Promise.resolve([]));
}, _TriggerStateController_getDefaultMessageOutputs = function _TriggerStateController_getDefaultMessageOutputs(comparatorResults, eventMessage) {
var _a;
const { entity_id: entityId, event } = eventMessage;
const msg = {
topic: entityId,
payload: (_a = event.new_state) === null || _a === void 0 ? void 0 : _a.state,
data: eventMessage,
};
let outputs;
if (comparatorResults.failed.length) {
this.debugToClient('constraint comparator: one or more comparators failed to match constraints, message will send on the failed output');
msg.failedComparators = comparatorResults.failed;
outputs = [null, msg];
}
else {
outputs = [msg, null];
}
return outputs;
}, _TriggerStateController_getOutputMessage = async function _TriggerStateController_getOutputMessage({ output, comparatorMatched, actualValue, }, eventMessage) {
var _a;
// If comparator did not match
if (!comparatorMatched) {
this.debugToClient(`output comparator failed: property "${output.comparatorPropertyValue}" with value ${actualValue} failed "${output.comparatorType}" check against (${output.comparatorValueDataType}) ${output.comparatorValue}`);
return null;
}
let message = {
topic: eventMessage.entity_id,
payload: (_a = eventMessage.event.new_state) === null || _a === void 0 ? void 0 : _a.state,
data: eventMessage,
};
if (output.messageType === const_1.MessageType.Custom ||
output.messageType === const_1.MessageType.Payload) {
let payload = output.messageValue;
// Render Template Variables
if ((0, utils_1.containsMustache)(output.messageValue)) {
payload = (0, mustache_1.renderTemplate)(output.messageValue, eventMessage.event, this.node.context(), this.homeAssistant.websocket.getStates());
}
payload = await this.typedInputService.getValue(payload, output.messageValueType, {
eventData: eventMessage,
});
if (output.messageType === const_1.MessageType.Custom) {
if (!(0, assert_1.isRecord)(payload)) {
throw new ConfigError_1.default('trigger-state.error.custom_output_message_needs_to_be_object');
}
message = payload;
}
else {
message = {
payload,
};
}
}
return message;
};
exports.default = TriggerStateController;