UNPKG

node-red-contrib-presence-faker

Version:

Simulates home occupancy by intermittently activating and deactivating devices (e.g. lights) at random intervals

506 lines (464 loc) 18.4 kB
<script type="text/javascript"> ;(function () { const calcDuration = (type) => { // type can be 'min' or 'max' let hours = Number($(`#node-input-${type}DurationHours`).val()) let minutes = Number($(`#node-input-${type}DurationMinutes`).val()) let seconds = Number($(`#node-input-${type}DurationSeconds`).val()) return hours * 3600 + minutes * 60 + seconds } RED.nodes.registerType('presence-faker', { category: 'smart home', color: '#FFAAAA', defaults: { name: { value: '' }, onPayload: { value: '', validate: RED.validators.typedInput('payloadType'), }, onPayloadType: { value: 'string' }, onTopic: { value: '', required: false }, offPayload: { value: '', validate: RED.validators.typedInput('payloadType'), }, offPayloadType: { value: 'string' }, offTopic: { value: '', required: false }, windowBegin: { value: '07:30', required: true, validate: RED.validators.regex( /^(0[0-9]|1[0-9]|2[0-3]):[0-5][0-9](:[0-5][0-9])?$/ ), }, windowEnd: { value: '17:30', required: true, validate: RED.validators.regex( /^(0[0-9]|1[0-9]|2[0-3]):[0-5][0-9](:[0-5][0-9])?$/ ), }, minDurationHours: { value: 0, required: true }, minDurationMinutes: { value: 0, required: true }, minDurationSeconds: { value: 0, required: true }, minDuration: { value: 0 }, maxDurationHours: { value: 0, required: true }, maxDurationMinutes: { value: 0, required: true }, maxDurationSeconds: { value: 0, required: true }, maxDuration: { value: 0 }, minCount: { value: 1, required: true, validate: function (v) { if (!/\d+/.exec(v)) { return false } else { return true } }, }, maxCount: { value: 1, required: true, validate: function (v) { if (!/\d+/.exec(v)) { return false } if (calcDuration('min') > calcDuration('max')) { return false } return true }, }, startupBehavior: { value: 'onMsg' }, actionOnDisable: { value: 'none' }, firstBlockType: { value: 'off' }, lastBlockType: { value: 'off' }, isLoggingEnabled: { value: false }, }, inputs: 1, outputs: 1, icon: 'light.png', label: function () { return this.name || 'presence-faker' }, oneditprepare: function () { fetch('/context/node/' + this.id, { method: 'GET' }) .then((response) => response.json()) .then((data) => { if (data.memory.windowBegin || data.memory.windowEnd) { $('#lbl-window').addClass('tooltip') $('#hint-window').html( 'current value:<br />' + (data.memory.windowBegin?.msg || this.windowBegin) + ' - ' + (data.memory.windowEnd?.msg || this.windowEnd) ) } if (data.memory.minCount || data.memory.maxCount) { $('#lbl-onTimes').addClass('tooltip') $('#hint-onTimes').html( 'current value:<br />' + (data.memory.minCount?.msg || this.minCount) + ' - ' + (data.memory.maxCount?.msg || this.maxCount) ) } if (data.memory.minDuration) { $('#lbl-onMinDuration').addClass('tooltip') $('#hint-onMinDuration').html( 'current value:<br />' + (data.memory.minDuration?.msg || this.minDuration) + ' sec' ) } if (data.memory.maxDuration) { $('#lbl-onMaxDuration').addClass('tooltip') $('#hint-onMaxDuration').html( 'current value:<br />' + (data.memory.maxDuration?.msg || this.maxDuration) + ' sec' ) } }) .catch((e) => console.log(e)) //ON Payload if (this.onPayloadType == null) { if (this.onPayload == '') { this.onPayloadType = 'date' } else { this.onPayloadType = 'str' } } else if ( this.onPayloadType === 'string' || this.onPayloadType === 'none' ) { this.onPayloadType = 'str' } $('#node-input-onPayloadType').val(this.onPayloadType) $('#node-input-onPayload').typedInput({ default: 'str', typeField: $('#node-input-onPayloadType'), types: [ //'flow', //'global', 'str', 'num', 'bool', 'json', //'bin', //'date', //'env' ], }) $('#node-input-onPayload').typedInput('type', this.onPayloadType) //OFF Payload if (this.offPayloadType == null) { if (this.offPayload == '') { this.offPayloadType = 'date' } else { this.offPayloadType = 'str' } } else if ( this.offPayloadType === 'string' || this.offPayloadType === 'none' ) { this.offPayloadType = 'str' } $('#node-input-offPayloadType').val(this.offPayloadType) $('#node-input-offPayload').typedInput({ default: 'str', typeField: $('#node-input-offPayloadType'), types: [ //'flow', //'global', 'str', 'num', 'bool', 'json', //'bin', //'date', //'env' ], }) $('#node-input-offPayload').typedInput('type', this.offPayloadType) }, oneditsave: function () { $('#node-input-minDuration').val(calcDuration('min')) $('#node-input-maxDuration').val(calcDuration('max')) }, }) })() </script> <style> .form-row label { width: 120px; } .tooltip { color: #ff5733; position: relative; display: inline-block; border-bottom: 1px dotted; /* If you want dots under the hoverable text */ } /* Tooltip text */ .tooltip .tooltiptext { visibility: hidden; width: 120px; background-color: black; color: #fff; text-align: center; padding: 5px 0; border-radius: 6px; /* Position the tooltip text - see examples below! */ position: absolute; z-index: 1; } /* Show the tooltip text when you mouse over the tooltip container */ .tooltip:hover .tooltiptext { visibility: visible; } </style> <script type="text/x-red" data-template-name="presence-faker"> <div class="form-row"> <label for="node-input-name"><i class="icon-tag"></i> Name</label> <input type="text" id="node-input-name" placeholder="Name"> </div> <div class="form-row"> <label for="node-input-windowBegin"><span id="lbl-window"><i class="fa fa-clock-o"></i> window <span id="hint-window" class="tooltiptext"></span></span></label> begin: <input type="text" id="node-input-windowBegin" placeholder="HH:MM" style="width:75px"> end: <input type="text" id="node-input-windowEnd" placeholder="HH:MM" style="width:75px"> </div> <div class="form-row"> <label for="node-input-minCount"><span id="lbl-onTimes">ON times <span id="hint-onTimes" class="tooltiptext"></span></span></label> min: <input type="text" id="node-input-minCount" placeholder="" style="width:50px"> max: <input type="text" id="node-input-maxCount" placeholder="" style="width:50px"> </div> <div class="form-row"> <label for="node-input-minDurationHours"><span id="lbl-onMinDuration">ON min duration <span id="hint-onMinDuration" class="tooltiptext"></span></span></label> <select style="width:50px; margin-left:0px;" id="node-input-minDurationHours"> <option value="0">00</option> <option value="1">01</option> <option value="2">02</option> <option value="3">03</option> <option value="4">04</option> <option value="5">05</option> <option value="6">06</option> <option value="7">07</option> <option value="8">08</option> <option value="9">09</option> <option value="10">10</option> <option value="11">11</option> <option value="12">12</option> <option value="13">13</option> <option value="14">14</option> <option value="15">15</option> <option value="16">16</option> <option value="17">17</option> <option value="18">18</option> <option value="19">19</option> <option value="20">20</option> <option value="21">21</option> <option value="22">22</option> <option value="23">23</option> </select>&nbsp;hours <select style="width:50px; margin-left:0px;" id="node-input-minDurationMinutes"> <option value="0">00</option> <option value="1">01</option> <option value="2">02</option> <option value="3">03</option> <option value="4">04</option> <option value="5">05</option> <option value="6">06</option> <option value="7">07</option> <option value="8">08</option> <option value="9">09</option> <option value="10">10</option> <option value="15">15</option> <option value="20">20</option> <option value="25">25</option> <option value="30">30</option> <option value="35">35</option> <option value="40">40</option> <option value="45">45</option> <option value="50">50</option> <option value="55">55</option> </select>&nbsp;minutes <select style="width:50px; margin-left:0px;" id="node-input-minDurationSeconds"> <option value="0">00</option> <option value="1">01</option> <option value="2">02</option> <option value="3">03</option> <option value="4">04</option> <option value="5">05</option> <option value="6">06</option> <option value="7">07</option> <option value="8">08</option> <option value="9">09</option> <option value="10">10</option> <option value="15">15</option> <option value="20">20</option> <option value="25">25</option> <option value="30">30</option> <option value="35">35</option> <option value="40">40</option> <option value="45">45</option> <option value="50">50</option> <option value="55">55</option> </select>&nbsp;seconds <input type="hidden" id="node-input-minDuration"> </div> <div class="form-row"> <label for="node-input-maxDurationHours"><span id="lbl-onMaxDuration">ON max duration <span id="hint-onMaxDuration" class="tooltiptext"></span></span></label> <select style="width:50px; margin-left:0px;" id="node-input-maxDurationHours"> <option value="0">00</option> <option value="1">01</option> <option value="2">02</option> <option value="3">03</option> <option value="4">04</option> <option value="5">05</option> <option value="6">06</option> <option value="7">07</option> <option value="8">08</option> <option value="9">09</option> <option value="10">10</option> <option value="11">11</option> <option value="12">12</option> <option value="13">13</option> <option value="14">14</option> <option value="15">15</option> <option value="16">16</option> <option value="17">17</option> <option value="18">18</option> <option value="19">19</option> <option value="20">20</option> <option value="21">21</option> <option value="22">22</option> <option value="23">23</option> </select>&nbsp;hours <select style="width:50px; margin-left:0px;" id="node-input-maxDurationMinutes"> <option value="0">00</option> <option value="1">01</option> <option value="2">02</option> <option value="3">03</option> <option value="4">04</option> <option value="5">05</option> <option value="6">06</option> <option value="7">07</option> <option value="8">08</option> <option value="9">09</option> <option value="10">10</option> <option value="15">15</option> <option value="20">20</option> <option value="25">25</option> <option value="30">30</option> <option value="35">35</option> <option value="40">40</option> <option value="45">45</option> <option value="50">50</option> <option value="55">55</option> </select>&nbsp;minutes <select style="width:50px; margin-left:0px;" id="node-input-maxDurationSeconds"> <option value="0">00</option> <option value="1">01</option> <option value="2">02</option> <option value="3">03</option> <option value="4">04</option> <option value="5">05</option> <option value="6">06</option> <option value="7">07</option> <option value="8">08</option> <option value="9">09</option> <option value="10">10</option> <option value="15">15</option> <option value="20">20</option> <option value="25">25</option> <option value="30">30</option> <option value="35">35</option> <option value="40">40</option> <option value="45">45</option> <option value="50">50</option> <option value="55">55</option> </select>&nbsp;seconds <input type="hidden" id="node-input-maxDuration"> </div> <div class="form-row"> <label for="node-input-onPayload"><i class="fa fa-envelope"></i> <span data-i18n="common.label.payload">ON Payload</span></label> <input type="text" id="node-input-onPayload" style="width:70%"> <input type="hidden" id="node-input-onPayloadType"> </div> <div class="form-row"> <label for="node-input-onTopic"><i class="fa fa-tasks"></i> <span>ON Topic</span></label> <input type="text" id="node-input-onTopic"> </div> <div class="form-row"> <label for="node-input-offPayload"><i class="fa fa-envelope"></i> <span data-i18n="common.label.payload">OFF Payload</span></label> <input type="text" id="node-input-offPayload" style="width:70%"> <input type="hidden" id="node-input-offPayloadType"> </div> <div class="form-row"> <label for="node-input-offTopic"><i class="fa fa-tasks"></i> <span>OFF Topic</span></label> <input type="text" id="node-input-offTopic"> </div> <div class="form-row"> <label for="node-input-firstBlockType"><i class="fa fa-step-backward"></i> <span>first block</span></label> <select id="node-input-firstBlockType" style="width:70%"> <option value="on">ON message</option> <option value="off">OFF message</option> </select> </div> <div class="form-row"> <label for="node-input-lastBlockType"><i class="fa fa-step-forward"></i> <span>last block</span></label> <select id="node-input-lastBlockType" style="width:70%"> <option value="on">ON message</option> <option value="off">OFF message</option> </select> </div> <div class="form-row"> <label for="node-input-startupBehavior"><i class="fa fa-power-off"></i> <span>enable</span></label> <select id="node-input-startupBehavior" style="width:70%"> <option value="onMsg">when TRUE is received as msg.payload</option> <option value="onStartup">when the flow starts up</option> </select> </div> <div class="form-row"> <label for="node-input-actionOnDisable"><i class="fa fa-power-off"></i> <span>when disabled</span></label> <select id="node-input-actionOnDisable" style="width:70%"> <option value="none">do nothing</option> <option value="on">send ON message</option> <option value="off">send OFF message</option> </select> </div> <div class="form-row"> <label for="node-input-isLoggingEnabled"><i class="fa fa-bug"></i> logging</label> <input type="checkbox" id="node-input-isLoggingEnabled" style="width:30px; margin:0px"> log debug messages to console </div> </script> <script type="text/markdown" data-help-name="presence-faker"> This node simulates home occupancy by intermittently activating and deactivating devices (e.g. lights) at random intervals. ### Inputs : payload (string | obj) : `true` to enable or `false` to disable the presence-faker node, or an `object` to configure it. ### Outputs : payload (string | bool | number | obj) : the output of the presence-faker node. ### Details By default, this node remains inactive until it receives a `true` value in the incoming `msg.payload`. When activated, it generates a random schedule of ON/OFF events, which are then transmitted as outgoing messages. You can specify the payload and topic for these messages in the node properties. Additionally, you have the option to configure the node to start up in an enabled state, eliminating the need for an incoming activation message. When the node receives a `false` value in the `msg.payload`, it deactivates itself. You can further customize the behavior in this situation, including the type of message to be sent. Runtime configuration adjustments are possible by passing an optional configuration object as `msg.payload` to the presence-faker node. The following attributes can be included: - `windowBegin`: Starting time (e.g., "00:00") - `windowEnd`: Ending time (e.g., "23:59:59") - `minDuration`: Minimum event duration (in seconds) - `maxDuration`: Maximum event duration (in seconds) - `minCount`: Minimum event count - `maxCount`: Maximum event count The activation and deactivation of the node are typically triggered by your home automation system, such as based on home occupancy status, LUX sensor readings, sunset times, and more. ### Combining presence-faker with suncron You can combine the presence-faker node with the [suncron node](https://www.npmjs.com/package/node-red-contrib-suncron) in order to dynamically set `windowBegin` and/or `windowEnd` based on the position of the sun: - [Example](https://gist.github.com/csuermann/604f846f4b7bc3a518dc6385c16e3a48) </script>