node-red-contrib-things
Advanced tools
Comparing version
{ | ||
"name": "node-red-contrib-things", | ||
"version": "2.0.0", | ||
"version": "2.0.1", | ||
"description": "A set of Node-RED nodes that uses an agnostic state management system to keep IOT device states and provide a uniform control system.", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -1,7 +0,5 @@ | ||
let { systemBus, stateBus, pushUnique } = require('./shared') | ||
let { stateBus, pushUnique } = require('./shared') | ||
module.exports = function (RED) { | ||
function Node(config) { | ||
RED.nodes.createNode(this, config) | ||
@@ -25,3 +23,2 @@ | ||
config.things.forEach((newThing, i) => { | ||
if (!newThing.name) { | ||
@@ -33,3 +30,2 @@ node.error(`Thing name missing during setup (${config.thingType}:${i})`) | ||
if (config.thingType == 'Group') { | ||
let { name, things } = newThing | ||
@@ -41,8 +37,9 @@ | ||
things, | ||
status: () => { } // Placeholder; TODO: Somehow combine status of all things in group | ||
// TODO: Can have more features for groups | ||
status: () => {}, // Placeholder; TODO: Somehow combine status of all things in group | ||
state: {}, // Placeholder (without it, can cause crash) | ||
props: {} // Placeholder (without it, can cause crash) | ||
} | ||
} else { | ||
// config.thingType != 'Group' | ||
} else { // config.thingType != 'Group' | ||
let { name, id } = newThing | ||
@@ -86,36 +83,38 @@ | ||
// For each proxied thing | ||
Object.entries(JSON.parse(newThing.proxy)).forEach(([proxyThingName, { state: stateMap }]) => { | ||
if (!stateMap) return | ||
// | ||
// Link states with getter | ||
Object.entries(stateMap).forEach(([from, to]) => { | ||
delete thing.state[from] // Clear it first if it exists | ||
Object.defineProperty(thing.state, from, { | ||
get: () => { | ||
// This check for the thing is mostly just in case it attempts to | ||
// use this state to update the status before the child has been setup | ||
const THINGS = global.get('things') | ||
if (config.debug) | ||
node.warn( | ||
`Calling getter for '${name}'.state.${from} -- Will return '${THINGS[proxyThingName] && THINGS[proxyThingName].state[to] | ||
}'` | ||
) | ||
return THINGS[proxyThingName] && THINGS[proxyThingName].state[to] | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
Object.entries(JSON.parse(newThing.proxy)).forEach( | ||
([proxyThingName, { state: stateMap }]) => { | ||
if (!stateMap) return | ||
// | ||
// Link states with getter | ||
Object.entries(stateMap).forEach(([from, to]) => { | ||
delete thing.state[from] // Clear it first if it exists | ||
Object.defineProperty(thing.state, from, { | ||
get: () => { | ||
// This check for the thing is mostly just in case it attempts to | ||
// use this state to update the status before the child has been setup | ||
const THINGS = global.get('things') | ||
if (config.debug) | ||
node.warn( | ||
`Calling getter for '${name}'.state.${from} -- Will return '${ | ||
THINGS[proxyThingName] && THINGS[proxyThingName].state[to] | ||
}'` | ||
) | ||
return THINGS[proxyThingName] && THINGS[proxyThingName].state[to] | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}) | ||
}) | ||
}) | ||
// Find proxied thing (i.e. the child) | ||
let proxyThing = THINGS[proxyThingName] | ||
if (proxyThing) { | ||
// If it is already setup, note parent in proxied thing | ||
if (config.debug) node.warn(`Adding parent ${name} to proxy child ${proxyThing.name}`) | ||
pushUnique(proxyThing.parents, name) | ||
// Find proxied thing (i.e. the child) | ||
let proxyThing = THINGS[proxyThingName] | ||
if (proxyThing) { | ||
// If it is already setup, note parent in proxied thing | ||
if (config.debug) | ||
node.warn(`Adding parent ${name} to proxy child ${proxyThing.name}`) | ||
pushUnique(proxyThing.parents, name) | ||
} | ||
} | ||
}) | ||
) | ||
} // End if proxies are specified | ||
} // End if config.thingType != 'Group' | ||
@@ -127,5 +126,11 @@ | ||
stateBus.emit(newThing.name) | ||
}) | ||
// Sort things first (only for ease of access in UI) | ||
const ABC_THINGS = {} | ||
Object.keys(THINGS) | ||
.sort() | ||
.forEach(key => (ABC_THINGS[key] = THINGS[key])) | ||
global.set('things', ABC_THINGS) | ||
node.status({ | ||
@@ -138,9 +143,9 @@ shape: 'dot', | ||
setTimeout(() => { | ||
// Make list of all active things of this type | ||
let allThingsThisType = [] | ||
RED.nodes.eachNode(otherConfig => { | ||
if (RED.nodes.getNode(otherConfig.id) | ||
&& otherConfig.type == 'Thing Setup' | ||
&& otherConfig.thingType == config.thingType | ||
if ( | ||
RED.nodes.getNode(otherConfig.id) && | ||
otherConfig.type == 'Thing Setup' && | ||
otherConfig.thingType == config.thingType | ||
) | ||
@@ -151,10 +156,9 @@ allThingsThisType.push(...otherConfig.things.map(t => t.name)) | ||
// Garbage collect inactive things of this type | ||
const THINGS = global.get('things') | ||
Object.values(THINGS) | ||
.filter(t => t.type == config.thingType && !allThingsThisType.includes(t.name)) | ||
.forEach(t => delete THINGS[t.name]) | ||
}, 0) | ||
} | ||
RED.nodes.registerType('Thing Setup', Node) | ||
} |
let EventEmitter = require('events') | ||
let systemBus = new EventEmitter() | ||
let stateBus = new EventEmitter() | ||
@@ -15,5 +14,7 @@ let commandBus = new EventEmitter() | ||
return `${d.getMonth() + 1}/${d.getDate()} ${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}` | ||
return `${d.getMonth() + 1}/${d.getDate()} ${p(d.getHours())}:${p(d.getMinutes())}:${p( | ||
d.getSeconds() | ||
)}` | ||
} | ||
module.exports = { systemBus, stateBus, commandBus, pushUnique, now } | ||
module.exports = { stateBus, commandBus, pushUnique, now } |
let { stateBus } = require('./shared') | ||
module.exports = function (RED) { | ||
function Node(config) { | ||
@@ -27,3 +26,3 @@ RED.nodes.createNode(this, config) | ||
let things = GLOBAL.get('things') | ||
return things ? things[this.name] : { state: {} } // Placeholder if called before setup in single mode | ||
return things ? things[this.name] : { state: {} } // Placeholder if called before setup in single mode | ||
} | ||
@@ -34,3 +33,5 @@ getState() { | ||
pathOrWhole(flag, path) { | ||
return flag == 'path' ? RED.util.getObjectProperty(this.thing.state, path) : this.thing.state | ||
return flag == 'path' | ||
? RED.util.getObjectProperty(this.thing.state, path) | ||
: this.thing.state | ||
} | ||
@@ -75,11 +76,8 @@ callback() { | ||
if (!config.multiMode) { | ||
// Create watcher and register | ||
watchers = [new ThingWatcher(config.name)] | ||
registerThingListeners() | ||
} else { | ||
// Instant timeout causes this to run async (after all setup) | ||
setTimeout(() => { | ||
// Generate list of things that match conditions | ||
@@ -91,3 +89,4 @@ watchers = Object.values(GLOBAL.get('things')) | ||
let test = value => value instanceof RegExp ? value.test(thingValue) : thingValue == value | ||
let test = value => | ||
value instanceof RegExp ? value.test(thingValue) : thingValue == value | ||
@@ -114,5 +113,4 @@ return config.multiKey == 'parents' ? thingValue.some(test) : test(compareValue) | ||
}) | ||
} | ||
RED.nodes.registerType('Thing Trigger', Node) | ||
} |
161987
0.28%1490
1.29%