diff --git a/lib/mustache-context.js b/lib/mustache-context.js
new file mode 100644
index 0000000000..8a0be738ce
--- /dev/null
+++ b/lib/mustache-context.js
@@ -0,0 +1,79 @@
+/*
+ * Modified from https://github.com/node-red/node-red/blob/master/nodes/core/core/80-template.js
+ */
+
+const mustache = require('mustache');
+const selectn = require('selectn');
+
+function parseContext(key) {
+ var match = /^(flow|global)(\[(\w+)\])?\.(.+)/.exec(key);
+ if (match) {
+ var parts = {};
+ parts.type = match[1];
+ parts.store = match[3] === '' ? 'default' : match[3];
+ parts.field = match[4];
+ return parts;
+ }
+ return undefined;
+}
+
+/**
+ * Custom Mustache Context capable to collect message property and node
+ * flow and global context
+ */
+
+function NodeContext(msg, parent, nodeContext, serverName) {
+ this.msgContext = new mustache.Context(msg, parent);
+ this.nodeContext = nodeContext;
+ this.serverName = serverName;
+}
+
+NodeContext.prototype = new mustache.Context();
+
+NodeContext.prototype.lookup = function(name) {
+ // try message first:
+ try {
+ const value = this.msgContext.lookup(name);
+ if (value !== undefined) {
+ return value;
+ }
+
+ // try flow/global context:
+ const context = parseContext(name);
+ if (context) {
+ const type = context.type;
+ const store = context.store;
+ const field = context.field;
+ const target = this.nodeContext[type];
+ if (target) {
+ return target.get(field, store);
+ }
+ }
+
+ // try state entities
+ const match = /^states\.(\w+\.\w+)(?:\.(.+))?/.exec(name);
+ if (match) {
+ const gHomeAssistant = this.nodeContext.global.get('homeassistant');
+ const states = gHomeAssistant[this.serverName].states;
+ const entityId = match[1];
+ const path = match[2] || 'state';
+
+ return selectn(path, states[entityId]) || '';
+ }
+
+ return '';
+ } catch (err) {
+ throw err;
+ }
+};
+
+NodeContext.prototype.push = function push(view) {
+ return new NodeContext(
+ view,
+ this.nodeContext,
+ this.msgContext,
+ this.serverName
+ );
+};
+
+module.exports = NodeContext;
diff --git a/nodes/call-service/call-service.html b/nodes/call-service/call-service.html
index 8cb19fda3d..f5b3387b83 100644
--- a/nodes/call-service/call-service.html
+++ b/nodes/call-service/call-service.html
@@ -30,7 +30,6 @@
}
}
},
- render_data: { value: false },
mergecontext: { value: null },
output_location: { value: "payload" },
output_location_type: { value: "msg" }
@@ -362,11 +361,6 @@
-
-
- Render templates in Data
-
-
Merge Context
@@ -408,9 +402,9 @@
Config
Entity Id string
A comma delimited list of entity ids
data JSON
- When using templates the top level is a property of the message object: msg.payload
would be {{payload}}
- Render Templates boolean
- When checked will attempt to render templates within the data element including entity ids
+ JSON object to pass along.
+ Templates
+ You can use templates in the Entity Id
and data
fields. When using templates the top level is a property of the message object: msg.payload
would be {{payload}}
. You can also access the flow, global and states
contexts {{flow.foobar}}
{{global.something}}
. For the states
context you can use the {{states.domain.entity_id}}
to just get the state or drill further down like {{states.light.kitchen.attributes.friendly_name}}
. {{states.light.kitchen}}
and {{states.light.kitchen.state}}
are equivalent.
Inputs
diff --git a/nodes/call-service/call-service.js b/nodes/call-service/call-service.js
index ddc0007c2a..fd6cbb99a2 100644
--- a/nodes/call-service/call-service.js
+++ b/nodes/call-service/call-service.js
@@ -1,6 +1,7 @@
/* eslint-disable camelcase */
const BaseNode = require('../../lib/base-node');
const mustache = require('mustache');
+const Context = require('../../lib/mustache-context');
module.exports = function(RED) {
const nodeOptions = {
@@ -9,7 +10,6 @@ module.exports = function(RED) {
service_domain: {},
service: {},
data: {},
- render_data: {},
mergecontext: {},
name: {},
server: { isNode: true },
@@ -78,11 +78,17 @@ module.exports = function(RED) {
);
}
- if (this.nodeConfig.render_data) {
- apiData = JSON.parse(
- mustache.render(JSON.stringify(apiData), message)
- );
- }
+ apiData = JSON.parse(
+ mustache.render(
+ JSON.stringify(apiData),
+ new Context(
+ message,
+ null,
+ this.node.context(),
+ this.utils.toCamelCase(this.nodeConfig.server.name)
+ )
+ )
+ );
this.debug(
`Calling Service: ${apiDomain}:${apiService} -- ${JSON.stringify(
@@ -158,7 +164,7 @@ module.exports = function(RED) {
payloadData = payloadData || {};
configData = configData || {};
- // Cacluate payload to send end priority ends up being 'Config, Global Ctx, Flow Ctx, Payload' with right most winning
+ // Calculate payload to send end priority ends up being 'Config, Global Ctx, Flow Ctx, Payload' with right most winning
if (this.nodeConfig.mergecontext) {
const ctx = this.node.context();
let flowVal = ctx.flow.get(this.nodeConfig.mergecontext);
diff --git a/nodes/fire-event/fire-event.html b/nodes/fire-event/fire-event.html
index 364f9e0425..be91f27803 100644
--- a/nodes/fire-event/fire-event.html
+++ b/nodes/fire-event/fire-event.html
@@ -11,17 +11,9 @@
return this.name || `Event: ${this.event}`;
},
defaults: {
- name: {
- value: ""
- },
- server: {
- value: "",
- type: "server",
- required: true
- },
- event: {
- value: ""
- },
+ name: { value: "" },
+ server: { value: "", type: "server", required: true },
+ event: { value: "" },
data: {
value: "",
validate: function(v) {
@@ -37,10 +29,7 @@
}
}
},
- render_data: { value: false },
- mergecontext: {
- value: null
- }
+ mergecontext: { value: null }
},
oneditprepare: function() {
const NODE = this;
@@ -91,11 +80,6 @@
-
-
- Render templates in Data
-
-
Merge Context
@@ -103,16 +87,26 @@
diff --git a/nodes/fire-event/fire-event.js b/nodes/fire-event/fire-event.js
index 297714bb4f..ce461f9c40 100644
--- a/nodes/fire-event/fire-event.js
+++ b/nodes/fire-event/fire-event.js
@@ -1,5 +1,6 @@
const BaseNode = require('../../lib/base-node');
const mustache = require('mustache');
+const Context = require('../../lib/mustache-context');
module.exports = function(RED) {
const nodeOptions = {
@@ -10,9 +11,7 @@ module.exports = function(RED) {
render_data: {},
mergecontext: {},
name: {},
- server: {
- isNode: true
- }
+ server: { isNode: true }
}
};
@@ -52,11 +51,17 @@ module.exports = function(RED) {
this.debug(`Fire Event: ${eventType} -- ${JSON.stringify({})}`);
- if (this.nodeConfig.render_data) {
- eventData = JSON.parse(
- mustache.render(JSON.stringify(eventData), message)
- );
- }
+ eventData = JSON.parse(
+ mustache.render(
+ JSON.stringify(eventData),
+ new Context(
+ message,
+ null,
+ this.node.context(),
+ this.utils.toCamelCase(this.nodeConfig.server.name)
+ )
+ )
+ );
message.payload = {
event: eventType,
@@ -103,7 +108,7 @@ module.exports = function(RED) {
payloadData = payloadData || {};
configData = configData || {};
- // Cacluate payload to send end priority ends up being 'Config, Global Ctx, Flow Ctx, Payload' with right most winning
+ // Calculate payload to send end priority ends up being 'Config, Global Ctx, Flow Ctx, Payload' with right most winning
if (this.nodeConfig.mergecontext) {
const ctx = this.node.context();
let flowVal = ctx.flow.get(this.nodeConfig.mergecontext);