@haydendonald/node-red-contrib-hass-stuff 1.1.7 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -3
- package/dist/Connections/connectionsConfigNode.js +32 -18
- package/dist/LightControl/lightControlConfigNode.html +16 -1
- package/dist/LightControl/lightControlConfigNode.js +38 -15
- package/dist/LightControl/lightControlNode.html +33 -1
- package/dist/LightControl/lightControlNode.js +0 -4
- package/dist/PIRControl/PIRControlConfigNode.html +68 -0
- package/dist/PIRControl/PIRControlConfigNode.js +123 -0
- package/dist/PIRControl/PIRControlNode.html +32 -0
- package/dist/PIRControl/PIRControlNode.js +18 -0
- package/dist/PIRControl/PIRControlTypes.js +2 -0
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -502,16 +502,15 @@ A node that controls lights with general scenes that adjust throughout the day w
|
|
|
502
502
|
|
|
503
503
|
### Features
|
|
504
504
|
* Adaptive scenes depending on what time of the day it is
|
|
505
|
-
* Adaptive scenes depending on luminance sensor
|
|
505
|
+
* Adaptive scenes depending on luminance sensor
|
|
506
506
|
* Night mode entity to force the adaptive scene to be in "night mode"
|
|
507
507
|
* Ability to turn off entities in night mode
|
|
508
508
|
|
|
509
509
|
### Dependencies
|
|
510
|
-
1. [
|
|
510
|
+
1. [Hue Scene Presets](https://github.com/Hypfer/hass-scene_presets)
|
|
511
511
|
2. [HASS Sun](https://www.home-assistant.io/integrations/sun/)
|
|
512
512
|
|
|
513
513
|
## PIR
|
|
514
|
-
*TODO*
|
|
515
514
|
|
|
516
515
|
A node that simplifies control of a PIR
|
|
517
516
|
|
|
@@ -131,8 +131,10 @@ module.exports = function ConnectionsConfigNode(RED) {
|
|
|
131
131
|
}
|
|
132
132
|
currentState = state;
|
|
133
133
|
//Tell HASS that the button was pressed
|
|
134
|
-
self.sendHASSAPI("http", "post", "/api/states/" + entityId,
|
|
135
|
-
|
|
134
|
+
self.sendHASSAPI("http", "post", "/api/states/" + entityId, (response) => {
|
|
135
|
+
var _a;
|
|
136
|
+
(_a = options.changedCallback) === null || _a === void 0 ? void 0 : _a.call(options, currentState, serviceData, response);
|
|
137
|
+
}, undefined, data(currentState));
|
|
136
138
|
}
|
|
137
139
|
};
|
|
138
140
|
}
|
|
@@ -173,8 +175,10 @@ module.exports = function ConnectionsConfigNode(RED) {
|
|
|
173
175
|
if (domain == "button" && service == "press" && (serviceData === null || serviceData === void 0 ? void 0 : serviceData.entity_id) == entityId) {
|
|
174
176
|
const state = new Date();
|
|
175
177
|
//Tell HASS that the button was pressed
|
|
176
|
-
self.sendHASSAPI("http", "post", "/api/states/" + entityId,
|
|
177
|
-
|
|
178
|
+
self.sendHASSAPI("http", "post", "/api/states/" + entityId, (response) => {
|
|
179
|
+
var _a;
|
|
180
|
+
(_a = options.pressedCallback) === null || _a === void 0 ? void 0 : _a.call(options, state, serviceData, response);
|
|
181
|
+
}, undefined, data(state));
|
|
178
182
|
}
|
|
179
183
|
};
|
|
180
184
|
}
|
|
@@ -218,8 +222,10 @@ module.exports = function ConnectionsConfigNode(RED) {
|
|
|
218
222
|
}
|
|
219
223
|
const state = new Date();
|
|
220
224
|
//Tell HASS that the scene was activated
|
|
221
|
-
self.sendHASSAPI("http", "post", "/api/states/" + entityId,
|
|
222
|
-
|
|
225
|
+
self.sendHASSAPI("http", "post", "/api/states/" + entityId, (response) => {
|
|
226
|
+
var _a;
|
|
227
|
+
(_a = options.activatedCallback) === null || _a === void 0 ? void 0 : _a.call(options, state, serviceData, response);
|
|
228
|
+
}, undefined, data(state));
|
|
223
229
|
}
|
|
224
230
|
};
|
|
225
231
|
}
|
|
@@ -263,16 +269,22 @@ module.exports = function ConnectionsConfigNode(RED) {
|
|
|
263
269
|
return;
|
|
264
270
|
}
|
|
265
271
|
if (service == "select_option") {
|
|
266
|
-
self.sendHASSAPI("http", "post", "/api/states/" + entityId,
|
|
267
|
-
|
|
272
|
+
self.sendHASSAPI("http", "post", "/api/states/" + entityId, (response) => {
|
|
273
|
+
var _a;
|
|
274
|
+
(_a = options.activatedCallback) === null || _a === void 0 ? void 0 : _a.call(options, serviceData.option, serviceData, response);
|
|
275
|
+
}, undefined, data(serviceData.option));
|
|
268
276
|
}
|
|
269
277
|
else if (service == "select_first") {
|
|
270
|
-
self.sendHASSAPI("http", "post", "/api/states/" + entityId,
|
|
271
|
-
|
|
278
|
+
self.sendHASSAPI("http", "post", "/api/states/" + entityId, (response) => {
|
|
279
|
+
var _a;
|
|
280
|
+
(_a = options.activatedCallback) === null || _a === void 0 ? void 0 : _a.call(options, options.options[0] || "unknown", serviceData, response);
|
|
281
|
+
}, undefined, data(options.options[0] || "unknown"));
|
|
272
282
|
}
|
|
273
283
|
else if (service == "select_last") {
|
|
274
|
-
self.sendHASSAPI("http", "post", "/api/states/" + entityId,
|
|
275
|
-
|
|
284
|
+
self.sendHASSAPI("http", "post", "/api/states/" + entityId, (response) => {
|
|
285
|
+
var _a;
|
|
286
|
+
(_a = options.activatedCallback) === null || _a === void 0 ? void 0 : _a.call(options, options.options[options.options.length - 1] || "unknown", serviceData, response);
|
|
287
|
+
}, undefined, data(options.options[options.options.length - 1] || "unknown"));
|
|
276
288
|
}
|
|
277
289
|
else if (service == "select_previous" || service == "select_next") {
|
|
278
290
|
self.getHASSEntityState(entityId, (currentState) => {
|
|
@@ -295,8 +307,10 @@ module.exports = function ConnectionsConfigNode(RED) {
|
|
|
295
307
|
index = options.options.length - 1;
|
|
296
308
|
}
|
|
297
309
|
}
|
|
298
|
-
self.sendHASSAPI("http", "post", "/api/states/" + entityId,
|
|
299
|
-
|
|
310
|
+
self.sendHASSAPI("http", "post", "/api/states/" + entityId, (response) => {
|
|
311
|
+
var _a;
|
|
312
|
+
(_a = options.activatedCallback) === null || _a === void 0 ? void 0 : _a.call(options, options.options[index] || "unknown", serviceData, response);
|
|
313
|
+
}, undefined, data(options.options[index] || "unknown"));
|
|
300
314
|
});
|
|
301
315
|
}
|
|
302
316
|
}
|
|
@@ -334,10 +348,10 @@ module.exports = function ConnectionsConfigNode(RED) {
|
|
|
334
348
|
} : undefined);
|
|
335
349
|
});
|
|
336
350
|
return (state) => {
|
|
337
|
-
self.sendHASSAPI("http", "post", "/api/states/" + entityId,
|
|
338
|
-
|
|
339
|
-
options.changedCallback(state);
|
|
340
|
-
}
|
|
351
|
+
self.sendHASSAPI("http", "post", "/api/states/" + entityId, (response) => {
|
|
352
|
+
var _a;
|
|
353
|
+
(_a = options.changedCallback) === null || _a === void 0 ? void 0 : _a.call(options, state, response);
|
|
354
|
+
}, undefined, data(state));
|
|
341
355
|
};
|
|
342
356
|
};
|
|
343
357
|
self.sendCompanionNotification = function (options) {
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
<p>
|
|
54
54
|
The following settings define what the scenes should do. They follow the syntax of [scene] [brightnessPercent]
|
|
55
55
|
where scene is the scene name, if the same as below values will use the
|
|
56
|
-
<a style="font-weight: bold;" href="https://github.com/
|
|
56
|
+
<a style="font-weight: bold;" href="https://github.com/Hypfer/hass-scene_presets">Hue Scene Presets HACS plugin</a>
|
|
57
57
|
otherwise will send to scene.apply. The brightnessPercent is the brightness percentage to set
|
|
58
58
|
</p>
|
|
59
59
|
<ul>
|
|
@@ -106,4 +106,19 @@
|
|
|
106
106
|
</script>
|
|
107
107
|
|
|
108
108
|
<script type="text/html" data-help-name="light-control-config-node">
|
|
109
|
+
<p>A node that controls lights with general scenes that adjust throughout the day with sleep mode and other features.</p>
|
|
110
|
+
|
|
111
|
+
<h2>Features</h2>
|
|
112
|
+
<ul>
|
|
113
|
+
<li>Adaptive scenes depending on what time of the day it is</li>
|
|
114
|
+
<li>Adaptive scenes depending on luminance sensor</li>
|
|
115
|
+
<li>Night mode entity to force the adaptive scene to be in "night mode"</li>
|
|
116
|
+
<li>Ability to turn off entities in night mode</li>
|
|
117
|
+
</ul>
|
|
118
|
+
|
|
119
|
+
<h2>Dependencies</h2>
|
|
120
|
+
<ul>
|
|
121
|
+
<li><a href="https://github.com/Hypfer/hass-scene_presets" target="_blank">Hue Scene Presets</a></li>
|
|
122
|
+
<li><a href="https://www.home-assistant.io/integrations/sun/" target="_blank">Sun Integration</a></li>
|
|
123
|
+
</ul>
|
|
109
124
|
</script>
|
|
@@ -10,6 +10,7 @@ module.exports = function LightControlConfigNode(RED) {
|
|
|
10
10
|
let nightModeState;
|
|
11
11
|
let currentSceneState;
|
|
12
12
|
let sunState;
|
|
13
|
+
let lastSentScene;
|
|
13
14
|
let entitiesOnDuringNight = [];
|
|
14
15
|
let entitiesOffDuringNight = [];
|
|
15
16
|
let adaptiveInterval;
|
|
@@ -134,7 +135,7 @@ module.exports = function LightControlConfigNode(RED) {
|
|
|
134
135
|
friendlyName: `${self.name} - ${sceneValue.friendlyName}`,
|
|
135
136
|
id: (0, utility_1.getEntityId)("scene", `${self.name}_${sceneKey}`),
|
|
136
137
|
creationCallback: (response) => { },
|
|
137
|
-
activatedCallback: (state, serviceData) => {
|
|
138
|
+
activatedCallback: (state, serviceData, response) => {
|
|
138
139
|
//Set our scene
|
|
139
140
|
connectionsConfigNode.sendHASSAction("select.select_option", { entity_id: [(0, utility_1.getEntityId)("select", `${self.name}_current_scene`)] }, {
|
|
140
141
|
option: sceneValue.friendlyName
|
|
@@ -148,7 +149,7 @@ module.exports = function LightControlConfigNode(RED) {
|
|
|
148
149
|
friendlyName: `${self.name} - Adaptive`,
|
|
149
150
|
id: (0, utility_1.getEntityId)("scene", `${self.name}_adaptive`),
|
|
150
151
|
creationCallback: (response) => { },
|
|
151
|
-
activatedCallback: (state, serviceData) => {
|
|
152
|
+
activatedCallback: (state, serviceData, response) => {
|
|
152
153
|
//Set our scene to adaptive
|
|
153
154
|
connectionsConfigNode.sendHASSAction("select.select_option", { entity_id: [(0, utility_1.getEntityId)("select", `${self.name}_current_scene`)] }, {
|
|
154
155
|
option: "Adaptive"
|
|
@@ -161,7 +162,7 @@ module.exports = function LightControlConfigNode(RED) {
|
|
|
161
162
|
friendlyName: `${self.name} - Turn On`,
|
|
162
163
|
id: (0, utility_1.getEntityId)("scene", `${self.name}_turn_on`),
|
|
163
164
|
creationCallback: (response) => { },
|
|
164
|
-
activatedCallback: (state, serviceData) => {
|
|
165
|
+
activatedCallback: (state, serviceData, response) => {
|
|
165
166
|
runLights(serviceData.transition || 1, true);
|
|
166
167
|
}
|
|
167
168
|
});
|
|
@@ -170,7 +171,7 @@ module.exports = function LightControlConfigNode(RED) {
|
|
|
170
171
|
friendlyName: `${self.name} - Turn Off`,
|
|
171
172
|
id: (0, utility_1.getEntityId)("scene", `${self.name}_turn_off`),
|
|
172
173
|
creationCallback: (response) => { },
|
|
173
|
-
activatedCallback: (state, serviceData) => {
|
|
174
|
+
activatedCallback: (state, serviceData, response) => {
|
|
174
175
|
connectionsConfigNode.sendHASSAction("light.turn_off", { entity_id: [config.groupEntityId] }, {
|
|
175
176
|
transition: serviceData.transition || 1
|
|
176
177
|
});
|
|
@@ -181,7 +182,7 @@ module.exports = function LightControlConfigNode(RED) {
|
|
|
181
182
|
friendlyName: `${self.name} - Toggle`,
|
|
182
183
|
id: (0, utility_1.getEntityId)("scene", `${self.name}_toggle`),
|
|
183
184
|
creationCallback: (response) => { },
|
|
184
|
-
activatedCallback: (state, serviceData) => {
|
|
185
|
+
activatedCallback: (state, serviceData, response) => {
|
|
185
186
|
if (currentState.state == "off") {
|
|
186
187
|
runLights(serviceData.transition || 1, true);
|
|
187
188
|
}
|
|
@@ -201,11 +202,11 @@ module.exports = function LightControlConfigNode(RED) {
|
|
|
201
202
|
"Adaptive"
|
|
202
203
|
],
|
|
203
204
|
defaultState: "Adaptive",
|
|
204
|
-
creationCallback: (state) => {
|
|
205
|
+
creationCallback: (state, response) => {
|
|
205
206
|
currentSceneState = state;
|
|
206
207
|
runLights(300, false);
|
|
207
208
|
},
|
|
208
|
-
activatedCallback: (state, serviceData) => {
|
|
209
|
+
activatedCallback: (state, serviceData, response) => {
|
|
209
210
|
currentSceneState = state;
|
|
210
211
|
}
|
|
211
212
|
});
|
|
@@ -229,11 +230,16 @@ module.exports = function LightControlConfigNode(RED) {
|
|
|
229
230
|
};
|
|
230
231
|
//When we get a message from a node on the NodeRED flows
|
|
231
232
|
this.msgReceived = function (msg, senderIds) { };
|
|
232
|
-
const activateScene = (scene, transitionSec, turnLightsOn, entitiesOn, entitiesOff) => {
|
|
233
|
+
const activateScene = (scene, transitionSec, turnLightsOn, entitiesOn, entitiesOff, forceSend) => {
|
|
233
234
|
//If the lights are off and we are not to turn the lights on don't do anything
|
|
234
235
|
if (turnLightsOn == false && currentState.state == "off") {
|
|
235
236
|
return;
|
|
236
237
|
}
|
|
238
|
+
//Don't send the same scene again
|
|
239
|
+
if (forceSend != true && scene.sceneName == lastSentScene) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
lastSentScene = scene.sceneName;
|
|
237
243
|
//Is not a scene preset scene if we have a entity id
|
|
238
244
|
if (scene.sceneName.includes(".")) {
|
|
239
245
|
//Run via hass scene.turn_on
|
|
@@ -242,6 +248,14 @@ module.exports = function LightControlConfigNode(RED) {
|
|
|
242
248
|
}, {
|
|
243
249
|
transition: transitionSec
|
|
244
250
|
});
|
|
251
|
+
//Send to the node output
|
|
252
|
+
self.sendMsg({
|
|
253
|
+
topic: "sceneSent",
|
|
254
|
+
payload: {
|
|
255
|
+
scene,
|
|
256
|
+
transitionSec
|
|
257
|
+
}
|
|
258
|
+
});
|
|
245
259
|
}
|
|
246
260
|
//Is a scene preset
|
|
247
261
|
else {
|
|
@@ -250,8 +264,7 @@ module.exports = function LightControlConfigNode(RED) {
|
|
|
250
264
|
self.error(`Scene preset not found for ${scene.sceneName}. Please use scene.<presetname> if this is not a scene i know about`);
|
|
251
265
|
return;
|
|
252
266
|
}
|
|
253
|
-
|
|
254
|
-
connectionsConfigNode.sendHASSAction("scene_presets.apply_preset", undefined, {
|
|
267
|
+
const msg = {
|
|
255
268
|
brightness: (scene.brightnessPct / 100.0) * 255,
|
|
256
269
|
transition: transitionSec,
|
|
257
270
|
preset_id: sceneId,
|
|
@@ -260,7 +273,9 @@ module.exports = function LightControlConfigNode(RED) {
|
|
|
260
273
|
targets: {
|
|
261
274
|
entity_id: entitiesOn ? entitiesOn : [config.groupEntityId]
|
|
262
275
|
}
|
|
263
|
-
}
|
|
276
|
+
};
|
|
277
|
+
//Run via adaptive lights
|
|
278
|
+
connectionsConfigNode.sendHASSAction("scene_presets.apply_preset", undefined, msg, undefined);
|
|
264
279
|
//Run any lights off that need to be off
|
|
265
280
|
if (entitiesOff && entitiesOff.length > 0) {
|
|
266
281
|
connectionsConfigNode.sendHASSAction("light.turn_off", {
|
|
@@ -269,6 +284,14 @@ module.exports = function LightControlConfigNode(RED) {
|
|
|
269
284
|
transition: transitionSec
|
|
270
285
|
});
|
|
271
286
|
}
|
|
287
|
+
//Send to the node output
|
|
288
|
+
self.sendMsg({
|
|
289
|
+
topic: "sceneSent",
|
|
290
|
+
payload: {
|
|
291
|
+
scene,
|
|
292
|
+
transitionSec
|
|
293
|
+
}
|
|
294
|
+
});
|
|
272
295
|
}
|
|
273
296
|
};
|
|
274
297
|
const runLights = (transitionSec, turnLightsOn) => {
|
|
@@ -284,7 +307,7 @@ module.exports = function LightControlConfigNode(RED) {
|
|
|
284
307
|
activateScene(scene, transitionSec, turnLightsOn);
|
|
285
308
|
}
|
|
286
309
|
};
|
|
287
|
-
const runAdaptive = (transitionSec, turnLightsOn) => {
|
|
310
|
+
const runAdaptive = (transitionSec, turnLightsOn, forceSend) => {
|
|
288
311
|
let entitiesOn;
|
|
289
312
|
let entitiesOff;
|
|
290
313
|
//Decide what scene to send
|
|
@@ -327,12 +350,12 @@ module.exports = function LightControlConfigNode(RED) {
|
|
|
327
350
|
}
|
|
328
351
|
}
|
|
329
352
|
//Send it
|
|
330
|
-
activateScene(scene, transitionSec, turnLightsOn, entitiesOn, entitiesOff);
|
|
353
|
+
activateScene(scene, transitionSec, turnLightsOn, entitiesOn, entitiesOff, forceSend);
|
|
331
354
|
//Start our interval to update the adaptive scene every minute
|
|
332
355
|
clearTimeout(adaptiveInterval);
|
|
333
356
|
adaptiveInterval = setTimeout(() => {
|
|
334
|
-
runAdaptive(300, false);
|
|
335
|
-
},
|
|
357
|
+
runAdaptive(300, false, true);
|
|
358
|
+
}, 300000);
|
|
336
359
|
};
|
|
337
360
|
};
|
|
338
361
|
RED.nodes.registerType("light-control-config-node", register);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
RED.nodes.registerType("light-control-node", {
|
|
3
3
|
category: "HASS Stuff",
|
|
4
4
|
color: "#ffcc00",
|
|
5
|
-
inputs:
|
|
5
|
+
inputs: 0,
|
|
6
6
|
outputs: 1,
|
|
7
7
|
icon: "bulb.svg",
|
|
8
8
|
paletteLabel: "Light Control",
|
|
@@ -28,4 +28,36 @@
|
|
|
28
28
|
</div>
|
|
29
29
|
</script>
|
|
30
30
|
<script type="text/html" data-help-name="light-control-node">
|
|
31
|
+
<p>A node that controls lights with general scenes that adjust throughout the day with sleep mode and other features.</p>
|
|
32
|
+
|
|
33
|
+
<h2>Features</h2>
|
|
34
|
+
<ul>
|
|
35
|
+
<li>Adaptive scenes depending on what time of the day it is</li>
|
|
36
|
+
<li>Adaptive scenes depending on luminance sensor</li>
|
|
37
|
+
<li>Night mode entity to force the adaptive scene to be in "night mode"</li>
|
|
38
|
+
<li>Ability to turn off entities in night mode</li>
|
|
39
|
+
</ul>
|
|
40
|
+
|
|
41
|
+
<h2>Dependencies</h2>
|
|
42
|
+
<ul>
|
|
43
|
+
<li><a href="https://github.com/Hypfer/hass-scene_presets" target="_blank">Hue Scene Presets</a></li>
|
|
44
|
+
<li><a href="https://www.home-assistant.io/integrations/sun/" target="_blank">Sun Integration</a></li>
|
|
45
|
+
</ul>
|
|
46
|
+
|
|
47
|
+
<h2>Output</h2>
|
|
48
|
+
<p>This node can output the following messages</p>
|
|
49
|
+
<ul>
|
|
50
|
+
<li>Scene Sent - A scene has been sent to the lights</li>
|
|
51
|
+
<pre><code>
|
|
52
|
+
{
|
|
53
|
+
topic: "sceneSent",
|
|
54
|
+
payload: {
|
|
55
|
+
friendlyName: "Concentrate",
|
|
56
|
+
sceneName: "sunFlare",
|
|
57
|
+
brightnessPct: 100
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
</code></pre>
|
|
61
|
+
</ul>
|
|
62
|
+
|
|
31
63
|
</script>
|
|
@@ -9,10 +9,6 @@ module.exports = function LightControlNode(RED) {
|
|
|
9
9
|
lightControlConfigNode.addMsgCallback(this.id, (msg) => {
|
|
10
10
|
this.send(msg);
|
|
11
11
|
});
|
|
12
|
-
//When an input message is received
|
|
13
|
-
this.on("input", (msg, send, done) => {
|
|
14
|
-
lightControlConfigNode.msgReceived(msg, [this.id]);
|
|
15
|
-
});
|
|
16
12
|
}
|
|
17
13
|
RED.nodes.registerType("light-control-node", register);
|
|
18
14
|
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<script type="text/javascript" id="node-PIR-control-config-node">
|
|
2
|
+
RED.nodes.registerType("PIR-control-config-node", {
|
|
3
|
+
category: "config",
|
|
4
|
+
defaults: {
|
|
5
|
+
name: { value: "", required: true },
|
|
6
|
+
connectionsConfigNode: { value: "", type: "connections-config-node", required: true },
|
|
7
|
+
PIROccupancyEntity: { value: "", required: true },
|
|
8
|
+
luminanceEntity: { value: "", required: false },
|
|
9
|
+
minBrightnessLevel: { value: "", required: false },
|
|
10
|
+
maxBrightnessLevel: { value: "", required: false },
|
|
11
|
+
turnOffAfterMs: { value: "0", default: "0", required: true },
|
|
12
|
+
turnOnAfterMs: { value: "0", default: "0", required: true },
|
|
13
|
+
enabledByDefault: { value: true, default: true, required: true }
|
|
14
|
+
},
|
|
15
|
+
label: function () {
|
|
16
|
+
return this.name || "PIR Control Config"
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<script type="text/html" data-template-name="PIR-control-config-node">
|
|
22
|
+
<div class="form-row">
|
|
23
|
+
<label for="node-config-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
24
|
+
<input type="text" id="node-config-input-name" placeholder="Name">
|
|
25
|
+
</div>
|
|
26
|
+
<div class="form-row">
|
|
27
|
+
<label for="node-config-input-connectionsConfigNode">Config</label>
|
|
28
|
+
<input type="text" id="node-config-input-connectionsConfigNode" />
|
|
29
|
+
</div>
|
|
30
|
+
<div class="form-row">
|
|
31
|
+
<p>The entity id of the PIR occupancy sensor</p>
|
|
32
|
+
<label for="node-config-input-PIROccupancyEntity">PIR Occupancy Entity</label>
|
|
33
|
+
<input type="text" id="node-config-input-PIROccupancyEntity" />
|
|
34
|
+
</div>
|
|
35
|
+
<div class="form-row">
|
|
36
|
+
<p>The entity id of the luminance sensor. If not set will disable this feature</p>
|
|
37
|
+
<label for="node-config-input-luminanceEntity">Luminance Entity</label>
|
|
38
|
+
<input type="text" id="node-config-input-luminanceEntity" />
|
|
39
|
+
</div>
|
|
40
|
+
<div class="form-row">
|
|
41
|
+
<p>The minimum brightness level of the luminance sensor to trigger the PIR. If not set will disable this feature</p>
|
|
42
|
+
<label for="node-config-input-minBrightnessLevel">Min Brightness Level</label>
|
|
43
|
+
<input type="number" id="node-config-input-minBrightnessLevel" />
|
|
44
|
+
</div>
|
|
45
|
+
<div class="form-row">
|
|
46
|
+
<p>The maximum brightness level of the luminance sensor to trigger the PIR. If not set will disable this feature</p>
|
|
47
|
+
<label for="node-config-input-maxBrightnessLevel">Max Brightness Level</label>
|
|
48
|
+
<input type="number" id="node-config-input-maxBrightnessLevel" />
|
|
49
|
+
</div>
|
|
50
|
+
<div class="form-row">
|
|
51
|
+
<p>The minimum time in milliseconds of no detection before triggering the "not detected" output</p>
|
|
52
|
+
<label for="node-config-input-turnOffAfterMs">Turn Off After (ms)</label>
|
|
53
|
+
<input type="number" id="node-config-input-turnOffAfterMs" />
|
|
54
|
+
</div>
|
|
55
|
+
<div class="form-row">
|
|
56
|
+
<p>The minimum time in milliseconds of detection before triggering the "detected" output</p>
|
|
57
|
+
<label for="node-config-input-turnOnAfterMs">Turn On After (ms)</label>
|
|
58
|
+
<input type="number" id="node-config-input-turnOnAfterMs" />
|
|
59
|
+
</div>
|
|
60
|
+
<div class="form-row">
|
|
61
|
+
<p>The default state of the enabled boolean entity when it's added to Home Assistant</p>
|
|
62
|
+
<label for="node-config-input-enabledByDefault">Enabled By Default</label>
|
|
63
|
+
<input type="checkbox" id="node-config-input-enabledByDefault" />
|
|
64
|
+
</div>
|
|
65
|
+
</script>
|
|
66
|
+
|
|
67
|
+
<script type="text/html" data-help-name="PIR-control-config-node">
|
|
68
|
+
</script>
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const baseConfigNode_1 = require("../baseConfigNode");
|
|
3
|
+
const utility_1 = require("../utility");
|
|
4
|
+
module.exports = function PIRControlConfigNode(RED) {
|
|
5
|
+
const register = function (config) {
|
|
6
|
+
const self = this;
|
|
7
|
+
let PIROccupancyState;
|
|
8
|
+
let luminanceState;
|
|
9
|
+
let enabledState;
|
|
10
|
+
let detectedTimeout;
|
|
11
|
+
let notDetectedTimeout;
|
|
12
|
+
RED.nodes.createNode(this, config);
|
|
13
|
+
(0, baseConfigNode_1.assignBaseConfigNode)(this);
|
|
14
|
+
const connectionsConfigNode = RED.nodes.getNode(config.connectionsConfigNode);
|
|
15
|
+
let errored = false;
|
|
16
|
+
if (!config.connectionsConfigNode || !connectionsConfigNode) {
|
|
17
|
+
this.error("Connections Config Node is required");
|
|
18
|
+
errored = true;
|
|
19
|
+
}
|
|
20
|
+
if (!config.PIROccupancyEntity) {
|
|
21
|
+
this.error("PIR occupancy entity is required");
|
|
22
|
+
errored = true;
|
|
23
|
+
}
|
|
24
|
+
if (!config.enabledByDefault) {
|
|
25
|
+
this.error("Enabled by default is required");
|
|
26
|
+
errored = true;
|
|
27
|
+
}
|
|
28
|
+
if (errored) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
//When HASS is ready
|
|
32
|
+
connectionsConfigNode.hassEventReadyCallbacks[this.id] = function (msg) {
|
|
33
|
+
//Get the current state of the occupancy sensor
|
|
34
|
+
connectionsConfigNode.getHASSEntityState(config.PIROccupancyEntity, (payload, data) => {
|
|
35
|
+
PIROccupancyState = data.data;
|
|
36
|
+
});
|
|
37
|
+
//If there is a luminance entity get it's state
|
|
38
|
+
if (config.luminanceEntity && config.luminanceEntity != "") {
|
|
39
|
+
connectionsConfigNode.getHASSEntityState(config.luminanceEntity, (payload, data) => {
|
|
40
|
+
luminanceState = data.data;
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
//Add the enabled input boolean
|
|
44
|
+
connectionsConfigNode.addHASSInputBoolean({
|
|
45
|
+
friendlyName: `${self.name} - Enabled`,
|
|
46
|
+
id: (0, utility_1.getEntityId)("input_boolean", `${self.name}_enabled`),
|
|
47
|
+
defaultState: config.enabledByDefault ? "on" : "off",
|
|
48
|
+
creationCallback: (state, response) => {
|
|
49
|
+
enabledState = state;
|
|
50
|
+
},
|
|
51
|
+
changedCallback: (state, serviceData) => {
|
|
52
|
+
enabledState = state;
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
//When a state change happens in home assistant
|
|
57
|
+
connectionsConfigNode.hassEventStateChangeCallbacks[this.id] = function (entityId, oldState, newState) {
|
|
58
|
+
switch (entityId) {
|
|
59
|
+
case config.PIROccupancyEntity: {
|
|
60
|
+
PIROccupancyState = newState;
|
|
61
|
+
handle();
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
case config.luminanceEntity: {
|
|
65
|
+
luminanceState = newState;
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
function handle() {
|
|
71
|
+
//Don't do anything if the PIR is disabled
|
|
72
|
+
if (enabledState == "off") {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
//Don't do anything if the luminance is not enough
|
|
76
|
+
if (config.minBrightnessLevel && parseInt(luminanceState.state) < parseInt(config.minBrightnessLevel)) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
//Don't do anything if the luminance is too much
|
|
80
|
+
if (config.maxBrightnessLevel && parseInt(luminanceState.state) > parseInt(config.maxBrightnessLevel)) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const msg = {
|
|
84
|
+
payload: {
|
|
85
|
+
PIROccupancyState,
|
|
86
|
+
enabledState,
|
|
87
|
+
luminanceState
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
//Send the detected event
|
|
91
|
+
if (PIROccupancyState.state == "on") {
|
|
92
|
+
clearTimeout(notDetectedTimeout);
|
|
93
|
+
if (config.turnOnAfterMs) {
|
|
94
|
+
detectedTimeout = setTimeout(() => {
|
|
95
|
+
sendDetectedMsg(msg);
|
|
96
|
+
}, parseInt(config.turnOnAfterMs));
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
sendDetectedMsg(msg);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
//Send the not detected event
|
|
103
|
+
if (PIROccupancyState.state == "off") {
|
|
104
|
+
clearTimeout(detectedTimeout);
|
|
105
|
+
if (config.turnOffAfterMs) {
|
|
106
|
+
notDetectedTimeout = setTimeout(() => {
|
|
107
|
+
sendNotDetectedMsg(msg);
|
|
108
|
+
}, parseInt(config.turnOffAfterMs));
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
sendNotDetectedMsg(msg);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function sendDetectedMsg(msg) {
|
|
116
|
+
self.sendMsg([msg]);
|
|
117
|
+
}
|
|
118
|
+
function sendNotDetectedMsg(msg) {
|
|
119
|
+
self.sendMsg([undefined, msg]);
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
RED.nodes.registerType("PIR-control-config-node", register);
|
|
123
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<script type="text/javascript" id="node-PIR-control-node">
|
|
2
|
+
RED.nodes.registerType("PIR-control-node", {
|
|
3
|
+
category: "HASS Stuff",
|
|
4
|
+
color: "#ffcc00",
|
|
5
|
+
inputs: 0,
|
|
6
|
+
outputs: 2,
|
|
7
|
+
outputLabels: ["detected", "not detected"],
|
|
8
|
+
icon: "alert.svg",
|
|
9
|
+
paletteLabel: "PIR Control",
|
|
10
|
+
defaults: {
|
|
11
|
+
name: { value: "" },
|
|
12
|
+
PIRControlConfigNode: { value: '', type: "PIR-control-config-node", required: true }
|
|
13
|
+
},
|
|
14
|
+
label: function () {
|
|
15
|
+
return this.name || "PIR Control"
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
<script type="text/html" data-template-name="PIR-control-node">
|
|
22
|
+
<div class="form-row">
|
|
23
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
24
|
+
<input type="text" id="node-input-name" placeholder="Name">
|
|
25
|
+
</div>
|
|
26
|
+
<div class="form-row">
|
|
27
|
+
<label for="node-input-PIRControlConfigNode">Config</label>
|
|
28
|
+
<input type="text" id="node-input-PIRControlConfigNode" />
|
|
29
|
+
</div>
|
|
30
|
+
</script>
|
|
31
|
+
<script type="text/html" data-help-name="PIR-control-node">
|
|
32
|
+
</script>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const baseNode_1 = require("../baseNode");
|
|
3
|
+
module.exports = function PIRControlNode(RED) {
|
|
4
|
+
function register(config) {
|
|
5
|
+
RED.nodes.createNode(this, config);
|
|
6
|
+
(0, baseNode_1.assignBaseNode)(this);
|
|
7
|
+
const PIRControlConfigNode = RED.nodes.getNode(config.PIRControlConfigNode);
|
|
8
|
+
//When a config node wants to send a message to the output
|
|
9
|
+
PIRControlConfigNode.addMsgCallback(this.id, (msg) => {
|
|
10
|
+
this.send(msg);
|
|
11
|
+
});
|
|
12
|
+
//When an input message is received
|
|
13
|
+
this.on("input", (msg, send, done) => {
|
|
14
|
+
PIRControlConfigNode.msgReceived(msg, [this.id]);
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
RED.nodes.registerType("PIR-control-node", register);
|
|
18
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@haydendonald/node-red-contrib-hass-stuff",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "A collection of stuff I use on my Node Red + Home Assistant server. This could be of use for others, i don't know..",
|
|
5
5
|
"devDependencies": {
|
|
6
6
|
"@types/node": "^18.14.0",
|
|
@@ -28,7 +28,9 @@
|
|
|
28
28
|
"ConnectionsNode": "dist/Connections/connectionsNode.js",
|
|
29
29
|
"LightControlConfigNode": "dist/LightControl/lightControlConfigNode.js",
|
|
30
30
|
"LightControlNode": "dist/LightControl/lightControlNode.js",
|
|
31
|
-
"EVChargingPriceNode": "dist/EVChargingPrice/EVChargingPriceNode.js"
|
|
31
|
+
"EVChargingPriceNode": "dist/EVChargingPrice/EVChargingPriceNode.js",
|
|
32
|
+
"PIRControlConfigNode": "dist/PIRControl/PIRControlConfigNode.js",
|
|
33
|
+
"PIRControlNode": "dist/PIRControl/PIRControlNode.js"
|
|
32
34
|
}
|
|
33
35
|
},
|
|
34
36
|
"publishConfig": {
|