thingzi-logic-timers
Version:
Easy to use time based nodes with clean design
392 lines (358 loc) • 17.7 kB
HTML
<script type="text/javascript">
RED.nodes.registerType('thingzi-activity', {
category: 'function',
color: '#9ccff5',
defaults: {
name: {value: ''},
lat: {value: '', validate: function(v){
return latLonValidator(this,v) ;
}},
lon: {value: '', validate: function(v){
return latLonValidator(this,v) ;
}},
// Activity timer duration in seconds
timerduration: {value: 60, required: true, validate: function(v){
return v >= 0;
}},
// Activity trigger
triggervaluetype: {value: 'str'},
triggervalue: {value: 'ON'},
resetvaluetype: {value: 'str'},
resetvalue: {value: null},
extendtimer: {value: true},
// ON config params
ontype: {value: 'tod'},
ontimesun: {value: 'dusk'},
ontimetod: {value: null},
ontimetodtype: {value: 'str'},
onoffset: {value: null, validate: function(v){
return (v >= -180 && v <= 180);
}},
onrandomoffset: {value: null},
// OFF config params
offtype: {value: 'tod'},
offtimesun: {value: 'dawn'},
offtimetod: {value: null},
offtimetodtype: {value: 'str'},
offoffset: {value: null, validate: function(v){
return (v >= -180 && v <= 180);
}},
offrandomoffset: {value: null},
// Days of the week
mon: {value: true},
tue: {value: true},
wed: {value: true},
thu: {value: true},
fri: {value: true},
sat: {value: true},
sun: {value: true},
// Limit activity range
limitactivity: {value: false},
// Pass on unchecked days
passunchecked: {value: false},
},
inputs: 1,
outputs: 1,
outputLabels: ["activity"],
icon: "activity.png",
label: function () {
return this.name ?? "activity";
},
paletteLabel: "activity",
oneditprepare: function () {
var updateGeo = function() {
if ($('#node-input-limitactivity').prop("checked") && ($('#node-input-ontype').val() === 'sun' || $('#node-input-offtype').val() === 'sun')) {
$('.thingzi-geo').show();
} else {
$('.thingzi-geo').hide();
}
}
var updateLimit = function() {
if ($('#node-input-limitactivity').prop("checked")) {
$('.thingzi-limitactivity').show()
} else {
$('.thingzi-limitactivity').hide()
}
}
// TRIGGER
$("#node-input-triggervaluetype").val(this.triggervaluetype ?? 'str');
$("#node-input-triggervalue").typedInput({
default: 'str',
typeField: $("#node-input-triggervaluetype"),
types:['str','num','bool']
});
// RESET
$("#node-input-resetvaluetype").val(this.resetvaluetype ?? 'str');
$("#node-input-resetvalue").typedInput({
default: 'str',
typeField: $("#node-input-resetvaluetype"),
types:['str','num','bool']
});
// START
var startType = $('#node-input-ontype');
var startTimeTod = $('#node-input-ontimetod');
var startTimeTodType = $('#node-input-ontimetodtype');
if (!startType.val()) {
startType.val('tod');
}
if (!startTimeTodType.val()) {
startTimeTodType.val('str');
}
startType.on('change', function(data) {
$('.thingzi-starttype').hide();
switch (startType.val()) {
case "sun": $('.thingzi-starttype-sun').show(); break;
case "tod": $('.thingzi-starttype-tod').show(); break;
}
});
startTimeTod.typedInput({
default: 'str',
typeField: startTimeTodType,
types:['str','flow','global']});
startTimeTodType.val(this.ontimetodtype);
// END
var endType = $('#node-input-offtype');
var endTimeTod = $('#node-input-offtimetod');
var endTimeTodType = $('#node-input-offtimetodtype');
if (!endType.val()) {
endType.val('tod');
}
if (!endTimeTodType.val()) {
endTimeTodType.val('str');
}
endType.on('change', function(data) {
$('.thingzi-endtype').hide();
switch (endType.val()) {
case "sun": $('.thingzi-endtype-sun').show(); break;
case "tod": $('.thingzi-endtype-tod').show(); break;
}
});
endTimeTod.typedInput({
default: 'str',
typeField: endTimeTodType,
types:['str','flow','global']});
endTimeTodType.val(this.offtimetodtype);
// Get lat/long from API
if (!$("#node-input-lat").val().trim() && !$("#node-input-lon").val().trim()) {
$.getJSON('thingzi/timer/location', function( data, status, xhr ) {
$("#node-input-lat").val(Number(data.latitude.toFixed(5)));
$("#node-input-lon").val(Number(data.longitude.toFixed(5)));
});
}
// Show/hide Geo fields
updateGeo();
$('.thingzi-geo-check').on('change', function(data) {
updateGeo();
});
// Show/hide time range
updateLimit();
$('#node-input-limitactivity').on('change', function(data) {
updateLimit();
updateGeo();
});
}
});
var latLonValidator = function(node, v){
if (node.ontype === 'sun' || node.offtype === 'sun') {
return (node.lat.length > 0 && !isNaN(node.lat) && node.lon.length > 0 && !isNaN(node.lon));
} else {
return true;
}
}
</script>
<script type="text/html" data-template-name="thingzi-activity">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="name">
</div>
<div class="form-row">
<label for="node-input-timerduration">Timer (secs)</label>
<input type="number" id="node-input-timerduration">
</div>
<div class="form-row">
<label title="if ticked and triggers occur while the timer is running, the timer is silently restarted." style="margin-left: 100px; width: 70%">
<input type="checkbox" id="node-input-extendtimer" style="width: 20px; margin: 0">
<span>Extend timer on new events</span>
</label>
</div>
<div class="form-row">
<label title="value passed in <code>payload</code> which triggers the activity" for="node-input-triggervalue">Trigger Value</label>
<input type="text" id="node-input-triggervalue" style="width:70%">
<input type="hidden" id="node-input-triggervaluetype">
</div>
<div class="form-row">
<label title="value passed in <code>payload</code> which will reset the trigger" for="node-input-resetvalue">Reset Value</label>
<input type="text" id="node-input-resetvalue" style="width:70%" placeholder="Leave empty for no reset">
<input type="hidden" id="node-input-resetvaluetype">
</div>
<div class="form-row">
<label title="days to check, by default not ticked days will fail the check" for="node-input-lat"><i class="fa fa-calendar">
</i> Schedule
</label>
<table style="display: inline-block; position: relative; top: 5px; left: -5px;">
<tr>
<td><input type="checkbox" id="node-input-mon" placeholder="" style="width: 20px; margin: 0">Mon</td>
<td><input type="checkbox" id="node-input-tue" placeholder="" style="width: 20px; margin: 0">Tue</td>
<td><input type="checkbox" id="node-input-wed" placeholder="" style="width: 20px; margin: 0">Wed</td>
<td><input type="checkbox" id="node-input-thu" placeholder="" style="width: 20px; margin: 0">Thu</td>
<td><input type="checkbox" id="node-input-fri" placeholder="" style="width: 20px; margin: 0">Fri</td>
<td><input type="checkbox" id="node-input-sat" placeholder="" style="width: 20px; margin: 0">Sat</td>
<td><input type="checkbox" id="node-input-sun" placeholder="" style="width: 20px; margin: 0">Sun</td>
</tr>
</table>
</div>
<div class="form-row">
<label title="if ticked, triggers can be limited to a specific time range e.g. night time" style="margin-left: 100px; width: 70%">
<input type="checkbox" id="node-input-limitactivity" style="width: 20px; margin: 0">
<span>Limit time range</span>
</label>
</div>
<div class="form-row thingzi-limitactivity">
<label title="if ticked, when a day is unchecked the test will always pass" style="margin-left: 100px; width: 70%">
<input type="checkbox" id="node-input-passunchecked" style="width: 20px; margin: 0">
<span>Ignore time range on unchecked days</span>
</label>
</div>
<div class="thingzi-limitactivity">
<hr>
<h4>Start</h4>
<div class="form-row">
<label for="node-input-ontype">Type</label>
<select id="node-input-ontype" class="thingzi-geo-check">
<option value="tod">Time of Day</option>
<option value="sun">Sun Event</option>
</select>
</div>
<div class="form-row thingzi-starttype thingzi-starttype-sun">
<label for="node-input-ontimesun">Sun Event</label>
<select id="node-input-ontimesun">
<option value="solarNoon">Solar Noon</option>
<option value="goldenHourEnd">Golden Hour End</option>
<option value="goldenHour">Golden Hour</option>
<option value="sunriseEnd">Sunrise End</option>
<option value="sunsetStart">Sunset Start</option>
<option value="sunrise">Sunrise</option>
<option value="sunset">Sunset</option>
<option value="dawn">Dawn</option>
<option value="dusk">Dusk</option>
<option value="nauticalDawn">Nautical Dawn</option>
<option value="nauticalDusk">Nautical Dusk</option>
<option value="nightEnd">Night End</option>
<option value="night">Night</option>
<option value="nadir">Nadir</option>
</select>
</div>
<div class="form-row thingzi-starttype thingzi-starttype-tod">
<label for="node-input-ontimetod" title="Simple time string e.g. HH::mm. Can also value from flow or global context">
Time (HH:mm)
</label>
<input type="text" id="node-input-ontimetod" style="width: 70%"/>
<input type="hidden" id="node-input-ontimetodtype">
</div>
<div class="form-row thingzi-starttype thingzi-starttype-sun thingzi-starttype-tod">
<label for="node-input-onoffset">Offset (mins)</label>
<input type="number" id="node-input-onoffset" placeholder="0">
</div>
<div class="form-row thingzi-starttype thingzi-starttype-sun thingzi-starttype-tod">
<label style="margin-left: 100px; width: 70%">
<input type="checkbox" id="node-input-onrandomoffset" placeholder="" style="width: 20px; margin: 0">
Randomise time within offset
</label>
</div>
</div>
<div class="thingzi-limitactivity">
<hr>
<h4>End</h4>
<div class="form-row">
<label for="node-input-offtype">Type</label>
<select id="node-input-offtype" class="thingzi-geo-check">
<option value="tod">Time of Day</option>
<option value="sun">Sun Event</option>
</select>
</div>
<div class="form-row thingzi-endtype thingzi-endtype-sun">
<label for="node-input-offtimesun">Sun Event</label>
<select id="node-input-offtimesun">
<option value="solarNoon">Solar Noon</option>
<option value="goldenHourEnd">Golden Hour End</option>
<option value="goldenHour">Golden Hour</option>
<option value="sunriseEnd">Sunrise End</option>
<option value="sunsetStart">Sunset Start</option>
<option value="sunrise">Sunrise</option>
<option value="sunset">Sunset</option>
<option value="dawn">Dawn</option>
<option value="dusk">Dusk</option>
<option value="nauticalDawn">Nautical Dawn</option>
<option value="nauticalDusk">Nautical Dusk</option>
<option value="nightEnd">Night End</option>
<option value="night">Night</option>
<option value="nadir">Nadir</option>
</select>
</div>
<div class="form-row thingzi-endtype thingzi-endtype-tod">
<label for="node-input-offtimetod" title="Simple time string e.g. HH::mm. Can also value from flow or global context" >
Time (HH:mm)
</label>
<input type="text" id="node-input-offtimetod" style="width: 70%" placeholder="HH:mm"/>
<input type="hidden" id="node-input-offtimetodtype">
</div>
<div class="form-row thingzi-endtype thingzi-endtype-sun thingzi-endtype-tod">
<label for="node-input-offoffset">Offset (mins)</label>
<input type="number" id="node-input-offoffset" placeholder="0">
</div>
<div class="form-row thingzi-endtype thingzi-endtype-sun thingzi-endtype-tod">
<label style="margin-left: 100px; width: 70%">
<input type="checkbox" id="node-input-offrandomoffset" placeholder="" style="width: 20px; margin: 0">
Randomise time within offset
</label>
</div>
</div>
<div class="thingzi-geo">
<hr>
<h4>Sun Events</h4>
<div class="form-row">
<label for="node-input-lat"><i class="fa fa-globe"></i> Latitude</label>
<input type="text" id="node-input-lat" placeholder="51.025">
</div>
<div class="form-row">
<label for="node-input-lon"><i class="fa fa-globe"></i> Longitude</label>
<input type="text" id="node-input-lon" placeholder="-1.4">
</div>
</div>
</script>
<script type="text/html" data-help-name="thingzi-activity">
<p>Handle an activity event and start a timer with optional extension for new events. Useful for motion events and lighting.</p>
<h3>Inputs</h3>
<dl class="message-properties">
<dt class="optional">payload <span class="property-type">string</span></dt>
<dd> Message that triggers the activity. Must match the value set in the node properties.</dd>
<dt class="optional">disable <span class="property-type">string</span></dt>
<dd> Disable the activity, even when in progress, can be <code>ON</code> or <code>OFF</code>. Useful if you need to override behaviour e.g. via a switch</dd>
<dt class="optional">reset <span class="property-type">string</span></dt>
<dd> Reset any active timer. Message content is ignored.</dd>
<dt class="optional">starttime (optional) <span class="property-type">string</span></dt>
<dd> Simple time string to use instead of the start time e.g. HH:mm. This forces "time of day" mode for the start time</dd>
<dt class="optional">endtime (optional) <span class="property-type">string</span></dt>
<dd> Simple time string to use instead of the off time e.g. HH:mm. This forces "time of day" mode for the end time</dd>
</dl>
<h3>Outputs</h3>
<ol class="node-ports">
<li>Activty
<dl class="message-properties">
<dt>payload <span class="property-type">string</span></dt>
<dd> Activity state <code>ON</code> or <code>OFF</code></dd>
</dl>
</li>
</ol>
<h3>Details</h3>
<p>Ideal for usage where motion sensors drive lights or similar. Listens for a trigger
message to begin the activity. When the trigger is received ON is sent and the timer starts.
When the timer ends OFF is sent.</p>
<p>Optional: If "Extend timer on new events" is enabled and triggers occur while
the timer is running, the timer is silently extended.</p>
<p>Time ranges can optionally be used to limit activity within a specified range. e.g. "only
start activity when dark". When using sun events the node will try to use your lat/long position
using the IP address of your ISP which is usually close enough. You can override this if you wish.</p>
<p>Times entered as text must be in 24hr format <code>HH:mm</code> (or military time in US). e.g. 18:45 or 09:50.
Note that seconds are also supported e.g. 11:30:45</p>
</script>