Examples
June 21, 2026 · View on GitHub
Scripts run independently but can share state and communicate through a few well-defined mechanisms.
she.global — shared plain object
she.global is a plain object available to every running script. Use it to pass values or pre-computed results without going through MQTT.
// script-a.js — producer
she.global.lastMotion = {};
she.mqtt.sub('home/motion/#', { change: true }, (topic, val) => {
if (val) she.global.lastMotion[topic] = Date.now();
});
// script-b.js — consumer
she.api.get('/last-motion', () => she.global.lastMotion || {});
Because scripts load in filesystem order, guard against she.global.x not existing yet with ?. or a default:
const ts = she.global.lastMotion?.['home/motion/hall'] ?? null;
Library script — shared utilities
Create a script in a lib/ subdirectory (mark the folder as a Library in the file tree so she doesn't auto-execute it). Require it like any other Node.js module.
// scripts/lib/utils.js
module.exports = {
toPercent(val, min, max) {
return Math.round(((val - min) / (max - min)) * 100);
},
formatTemp(val) {
return `${parseFloat(val).toFixed(1)} °C`;
},
};
// scripts/heating.js
const utils = require('./lib/utils');
she.mqtt.sub('home/sensor/living/temp', { change: true }, (topic, val) => {
she.info('living room:', utils.formatTemp(val));
});
Cross-script event bus using Node.js EventEmitter
she.global can hold any object — including a Node.js EventEmitter. This gives you a lightweight in-process event bus without MQTT overhead or broker dependency.
Put the setup in a script that loads first (e.g. 00-init.js):
// scripts/00-init.js
const { EventEmitter } = require('events');
she.global.bus = new EventEmitter();
Then any script can publish and subscribe:
// scripts/alarm.js — emit an event
she.mqtt.sub('home/sensor/basement/flood', { change: true }, (topic, val) => {
if (val) she.global.bus?.emit('alert', { type: 'flood', zone: 'basement', ts: Date.now() });
});
// scripts/notify.js — react to any alert
she.global.bus?.on('alert', async ({ type, zone }) => {
she.warn('alert:', type, 'in', zone);
// send push notification, publish MQTT, etc.
});
Using ?. makes it safe even if 00-init.js hasn't run yet, or if you temporarily disable it.
For more advanced use (wildcard event names, once(), namespaced events) you can replace EventEmitter with EventEmitter2 — but read the code you introduce; see Security → Third-party npm packages.
Shared configuration object
Keep configuration in one place and reference it from multiple scripts.
// scripts/lib/config.js
module.exports = {
rooms: ['living', 'kitchen', 'hall', 'bedroom'],
defaultTemp: 21,
awayTemp: 17,
pushoverToken: '', // fill in, or load from sheDB
pushoverUser: '',
};
// scripts/heating.js
const cfg = require('./lib/config');
she.mqtt.sub('home/presence', { change: true }, (topic, val) => {
const target = val ? cfg.defaultTemp : cfg.awayTemp;
cfg.rooms.forEach(r => she.mqtt.pub(`home/thermostat/${r}/setpoint`, target));
});
Initialise shared state from MQTT retained values at startup
When a script needs to read current values before any message arrives, use { retain: true } to fire immediately for all retained topics.
// scripts/00-init.js — build she.global.state from retained MQTT values
she.global.state = {};
she.mqtt.sub('home/#', { retain: true }, (topic, val) => {
she.global.state[topic] = val;
});
Other scripts can then read she.global.state['home/presence'] synchronously without waiting for the next MQTT message.
Coordination: only one script acts on a shared resource
Use she.global as a lightweight mutex flag when two scripts might otherwise both react to the same event.
// scripts/scene.js
she.global.sceneActive = false;
she.mqtt.sub('home/scene/activate', { change: true }, async (topic, val) => {
if (she.global.sceneActive) {
she.warn('scene already running, ignoring:', val);
return;
}
she.global.sceneActive = true;
try {
she.mqtt.pub('home/light/living', 180);
await new Promise(r => setTimeout(r, 2000));
she.mqtt.pub('home/light/hall', 100);
} finally {
she.global.sceneActive = false;
}
});