node-red-contrib-iotflows
Version:
A collection of Node-RED nodes for IoTFlows.
599 lines (555 loc) • 25 kB
HTML
<!--
Copyright 2022 IoTFlows Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/x-red" data-template-name="datastream in">
<div class="form-row">
<label for="node-input-broker"><i class="fa fa-globe"></i> <span>Server</span></label>
<input type="text" id="node-input-broker">
</div>
<div class="form-row">
<label for="node-input-topic"><i class="fa fa-tasks"></i> <span>Topic</span></label>
<select id="node-input-topic" onchange="onChangeDataStreamInputTopic()"></select>
<button onclick="fetchDataStreams('Project')" class="btn"><i class="fa fa-refresh"></i></button>
</div>
<div class="form-row">
<label for="node-input-subtopicsubscription"><i class="fa fa-tasks"></i> <span>Subtopic Subscription</span></label>
<select id="node-input-subtopicsubscription" onchange="onChangeDataStreamInputSubtopicSubscription()">
<option value=False>False</option>
<option value=True>True</option>
</select>
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span>Name</span></label>
<input type="text" id="node-input-name">
</div>
</script>
<script type="text/x-red" data-template-name="datastream out">
<div class="form-row">
<label for="node-input-broker"><i class="fa fa-globe"></i> <span>Server</span></label>
<input type="text" id="node-input-broker">
</div>
<div class="form-row">
<label for="node-input-topic"><i class="fa fa-tasks"></i> <span>Topic</span></label>
<select id="node-input-topic" onchange="onChangeDataStreamInputTopic()"></select>
<button onclick="fetchDataStreams('Device')" class="btn"><i class="fa fa-refresh"></i></button>
</div>
<div hidden class="form-row">
<label for="node-input-datastreamid"><i class="fa fa-tag"></i> <span>Data Stream</span></label>
<input type="text" id="node-input-datastreamid" disabled>
</div>
<div class="form-row">
<label for="node-input-datastreamsubtopic"><i class="fa fa-tag"></i> <span>Subtopic</span></label>
<input type="text" id="node-input-datastreamsubtopic">
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span>Name</span></label>
<input type="text" id="node-input-name">
</div>
</script>
<script type="text/x-red" data-template-name="alert channel out">
<div class="form-row">
<label for="node-input-broker"><i class="fa fa-globe"></i> <span>Server</span></label>
<input type="text" id="node-input-broker">
</div>
<div class="form-row">
<label for="node-input-topic"><i class="fa fa-tasks"></i> <span>Topic</span></label>
<select id="node-input-topic" onchange="onChangeAlertChannelInputTopic()"></select>
<button onclick="fetchAlertChannels()" class="btn"><i class="fa fa-refresh"></i></button>
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span>Name</span></label>
<input type="text" id="node-input-name">
</div>
</script>
<script type="text/x-red" data-template-name="action start">
<div class="form-row">
<label for="node-input-broker"><i class="fa fa-globe"></i> <span>Server</span></label>
<input type="text" id="node-input-broker">
</div>
<div class="form-row">
<label for="node-input-topic"><i class="fa fa-tasks"></i> <span>Topic</span></label>
<select id="node-input-topic" onchange="onChangeActionInputTopic()"></select>
<button onclick="fetchActions()" class="btn"><i class="fa fa-refresh"></i></button>
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span>Name</span></label>
<input type="text" id="node-input-name">
</div>
</script>
<script type="text/x-red" data-template-name="action finish">
<div class="form-row">
<label for="node-input-broker"><i class="fa fa-globe"></i> <span>Server</span></label>
<input type="text" id="node-input-broker">
</div>
</script>
<script>
var this_clientid;
function fetchDataStreams(type) {
// Generate the DataStreams List
var xhttpDataStream = new XMLHttpRequest();
xhttpDataStream.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200)
{
var data = JSON.parse(this.responseText);
$('#node-input-topic').empty();
data.map(item => {
$('#node-input-topic').append('<option datastreamid="' + item.data_stream_uuid + '" value="v1/organizations/' + item.organization_uuid + '/projects/' + item.project_uuid + '/devices/' + item.device_uuid + '/data-streams/' + item.data_stream_uuid + '">' + item.project_name + '-' + item.device_name + ' - ' + item.data_stream_name + '</option>');
})
}
};
if(type == "Device")
{
xhttpDataStream.open("GET", "https://api.iotflows.com/v1/device_clients/" + this_clientid + "/device_data_streams", false);
xhttpDataStream.send();
}
else if (type == "Project")
{
xhttpDataStream.open("GET", "https://api.iotflows.com/v1/device_clients/" + this_clientid + "/project_data_streams", false);
xhttpDataStream.send();
}
onChangeDataStreamInputTopic();
}
function onChangeDataStreamInputTopic(){
let selectedTopic = $('#node-input-topic').children("option:selected").html()
if(selectedTopic && selectedTopic !== undefined && selectedTopic !== "undefined")
{
$('#node-input-name').val('Data Stream ' + selectedTopic);
}
else
{
$('#node-input-name').val('Data Stream');
}
$('#node-input-datastreamid').val($('#node-input-topic').children("option:selected").attr('datastreamid'));
}
function onChangeDataStreamInputSubtopicSubscription(){
$('#node-input-subtopicsubscription').val($('#node-input-subtopicsubscription').children("option:selected").attr('value'));
console.log($('#node-input-subtopicsubscription').children("option:selected").attr('value'))
}
</script>
<script>
var this_clientid;
function fetchAlertChannels() {
// Generate the Alert Channels List
var xhttpDataStream = new XMLHttpRequest();
xhttpDataStream.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200)
{
var data = JSON.parse(this.responseText);
$('#node-input-topic').empty();
data.map(item => {
$('#node-input-topic').append('<option value="v1/organizations/' + item.organization_uuid + '/alert-channels/' + item.alert_channel_uuid + '">' + item.alert_channel_name + '</option>');
})
}
};
xhttpDataStream.open("GET", "https://api.iotflows.com/v1/device_clients/" + this_clientid + "/organization_alert_channels", false);
xhttpDataStream.send();
onChangeAlertChannelInputTopic();
}
function onChangeAlertChannelInputTopic(){
let selectedTopic = $('#node-input-topic').children("option:selected").html()
if(selectedTopic && selectedTopic !== undefined && selectedTopic !== "undefined")
{
$('#node-input-name').val('Alert ' + selectedTopic);
}
else
{
$('#node-input-name').val('Alert');
}
}
</script>
<script>
var this_clientid;
function fetchActions() {
// Generate the Actions List
var xhttpDataStream = new XMLHttpRequest();
xhttpDataStream.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200)
{
var data = JSON.parse(this.responseText);
$('#node-input-topic').empty();
data.map(item => {
$('#node-input-topic').append('<option actionid="' + item.action_uuid + '" value="v1/organizations/' + item.organization_uuid + '/projects/' + item.project_uuid + '/devices/' + item.device_uuid + '/actions/' + item.action_uuid + '">' + item.device_name + ' - ' + item.action_name + '</option>');
})
}
};
xhttpDataStream.open("GET", "https://api.iotflows.com/v1/device_clients/" + this_clientid + "/device_actions", false);
xhttpDataStream.send();
onChangeActionInputTopic();
}
function onChangeActionInputTopic(){
let selectedTopic = $('#node-input-topic').children("option:selected").html()
if(selectedTopic && selectedTopic !== undefined && selectedTopic !== "undefined")
{
$('#node-input-name').val('Action ' + selectedTopic);
}
else
{
$('#node-input-name').val('Action');
}
$('#node-input-actionid').val($('#node-input-topic').children("option:selected").attr('actionid'));
}
</script>
<script type="text/javascript">
RED.nodes.registerType('datastream in',{
category: 'iotflows',
defaults: {
name: {value:""},
topic: {value:"",required:true,validate: RED.validators.regex(/^(#$|(\+|[^+#]*)(\/(\+|[^+#]*))*(\/(\+|#|[^+#]*))?$)/)},
subtopicsubscription: {value: false},
qos: {value: "2"},
datatype: {value:"auto",required:true},
broker: {type:"mqtt-iotflows-broker", required:true},
},
color:"#99CCFF",
inputs:0,
outputs:1,
icon: "iotflows.png",
label: function() {
return this.name||this.topic||"data stream";
},
labelStyle: function() {
return this.name?"node_label_italic":"";
},
oneditprepare: function() {
if (this.qos === undefined) {
$("#node-input-qos").val("2");
}
if (this.datatype === undefined) {
$("#node-input-datatype").val("auto");
}
}
});
</script>
<script type="text/javascript">
RED.nodes.registerType('datastream out',{
category: 'iotflows',
defaults: {
name: {value:""},
topic: {value:""},
qos: {value:""},
retain: {value:""},
broker: {type:"mqtt-iotflows-broker", required:true},
// IoTFlows
datastreamid: {value:""},
datastreamsubtopic: {value:""},
},
color:"#99CCFF",
inputs:1,
outputs:0,
icon: "iotflows.png",
align: "right",
label: function() {
return this.name||this.topic||"data stream";
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>
<script type="text/javascript">
RED.nodes.registerType('alert channel out',{
category: 'iotflows',
defaults: {
name: {value:""},
topic: {value:""},
qos: {value:""},
retain: {value:""},
broker: {type:"mqtt-iotflows-broker", required:true},
},
color:"#F1C86F",
inputs:1,
outputs:0,
icon: "iotflows.png",
align: "right",
label: function() {
return this.name||this.topic||"alert channel";
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>
<script type="text/javascript">
RED.nodes.registerType('action start',{
category: 'iotflows',
defaults: {
name: {value:""},
topic: {value:"",required:true,validate: RED.validators.regex(/^(#$|(\+|[^+#]*)(\/(\+|[^+#]*))*(\/(\+|#|[^+#]*))?$)/)},
qos: {value: "2"},
datatype: {value:"auto",required:true},
broker: {type:"mqtt-iotflows-broker", required:true},
},
color:"#BC94D0",
inputs:0,
outputs:1,
icon: "iotflows.png",
label: function() {
return this.name||this.topic||"action";
},
labelStyle: function() {
return this.name?"node_label_italic":"";
},
oneditprepare: function() {
if (this.qos === undefined) {
$("#node-input-qos").val("2");
}
if (this.datatype === undefined) {
$("#node-input-datatype").val("auto");
}
}
});
</script>
<script type="text/javascript">
RED.nodes.registerType('action finish',{
category: 'iotflows',
defaults: {
name: {value:""},
topic: {value:""},
qos: {value:""},
retain: {value:""},
broker: {type:"mqtt-iotflows-broker", required:true},
},
color:"#BC94D0",
inputs:1,
outputs:0,
icon: "iotflows.png",
align: "right",
label: function() {
return "Action Finish"; //this.name||this.topic||"action";
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>
<script type="text/html" data-template-name="mqtt-iotflows-broker">
<!-- <div class="form-row">
<label for="node-config-input-name"><i class="fa fa-tag"></i> <span>Name</span></label>
<input type="text" id="node-config-input-name">
</div> -->
<div class="form-row">
<ul style="min-width: 600px; margin-bottom: 20px;" id="node-config-mqtt-iotflows-broker-tabs"></ul>
</div>
<div id="node-config-mqtt-iotflows-broker-tabs-content" style="min-height:150px;">
<div id="mqtt-iotflows-broker-tab-security" style="display:none">
<div class="form-row">
<label for="node-config-input-clientid"><i class="fa fa-tag"></i> <span>Client ID</span></label>
<input type="text" id="node-config-input-clientid">
</div>
<div class="form-row">
<label for="node-config-input-password"><i class="fa fa-lock"></i> <span>Password</span></label>
<input type="password" id="node-config-input-password">
</div>
</div>
</div>
</script>
<script type="text/javascript">
RED.nodes.registerType('mqtt-iotflows-broker',{
category: 'config',
defaults: {
name: {value:""},
broker: {value:"",required:true},
port: {value:1883,required:false,validate:RED.validators.number(true)},
tls: {type:"tls-config",required: false},
clientid: {value:"", validate: function(v) {
if ($("#node-config-input-clientid").length) {
// Currently editing the node
return $("#node-config-input-cleansession").is(":checked") || (v||"").length > 0;
} else {
return (this.cleansession===undefined || this.cleansession) || (v||"").length > 0;
}
}},
usetls: {value: false},
verifyservercert: { value: false},
compatmode: { value: false},
keepalive: {value:60,validate:RED.validators.number()},
cleansession: {value: true},
birthTopic: {value:""},
birthQos: {value:"0"},
birthRetain: {value:false},
birthPayload: {value:""},
closeTopic: {value:""},
closeQos: {value:"0"},
closeRetain: {value:false},
closePayload: {value:""},
willTopic: {value:""},
willQos: {value:"0"},
willRetain: {value:false},
willPayload: {value:""},
},
credentials: {
clientid: {type:"text"},
password: {type: "password"}
},
label: function() {
// if (this.name) {
// return this.name;
// }
// var b = this.broker;
// if (b === "") { b = "undefined"; }
// var lab = "";
// lab = (this.clientid?this.clientid+"@":"")+b;
// if (b.indexOf("://") === -1){
// if (!this.port){ lab = lab + ":1883"; }
// else { lab = lab + ":" + this.port; }
// }
// return lab;
if(!this.name)
{
var label = "IoTFlows Cloud" // default value
// Generate the Server Name
var xhttpServer = new XMLHttpRequest();
xhttpServer.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200)
{
var data = JSON.parse(this.responseText);
label = data[0].organization_name + " - " + data[0].project_name + " - " + data[0].device_name + " - " + data[0].client_name;
}
};
xhttpServer.open("GET", "https://api.iotflows.com/v1/device_clients/" + this.clientid, false);
xhttpServer.send();
this.name = label;
}
// set the global clientid
this_clientid = this.clientid
return this.name;
},
oneditprepare: function () {
var tabs = RED.tabs.create({
id: "node-config-mqtt-iotflows-broker-tabs",
onchange: function(tab) {
$("#node-config-mqtt-iotflows-broker-tabs-content").children().hide();
$("#" + tab.id).show();
}
});
tabs.addTab({
id: "mqtt-iotflows-broker-tab-security",
label: this._("Credentials")
});
function setUpSection(sectionId, isExpanded) {
var birthMessageSection = $(sectionId);
var paletteHeader = birthMessageSection.find('.red-ui-palette-header');
var twistie = paletteHeader.find('i');
var sectionContent = birthMessageSection.find('.section-content');
function toggleSection(expanded) {
twistie.toggleClass('expanded', expanded);
sectionContent.toggle(expanded);
}
paletteHeader.on("click", function(e) {
e.preventDefault();
var isExpanded = twistie.hasClass('expanded');
toggleSection(!isExpanded);
});
toggleSection(isExpanded);
}
// show first section if none are set so the user gets the idea
var showBirthSection = this.birthTopic !== ""
|| this.willTopic === ""
&& this.birthTopic === ""
&& this.closeTopic == "";
setUpSection('#mqtt-iotflows-broker-section-birth', showBirthSection);
setUpSection('#mqtt-iotflows-broker-section-close', this.closeTopic !== "");
setUpSection('#mqtt-iotflows-broker-section-will', this.willTopic !== "");
setTimeout(function() { tabs.resize(); },0);
if (typeof this.cleansession === 'undefined') {
this.cleansession = true;
$("#node-config-input-cleansession").prop("checked",true);
}
if (typeof this.usetls === 'undefined') {
this.usetls = false;
$("#node-config-input-usetls").prop("checked",false);
}
if (typeof this.compatmode === 'undefined') {
this.compatmode = false;
$("#node-config-input-compatmode").prop('checked', false);
}
if (typeof this.keepalive === 'undefined') {
this.keepalive = 15;
$("#node-config-input-keepalive").val(this.keepalive);
}
if (typeof this.birthQos === 'undefined') {
this.birthQos = "0";
$("#node-config-input-birthQos").val("0");
}
if (typeof this.closeQos === 'undefined') {
this.willQos = "0";
$("#node-config-input-willQos").val("0");
}
if (typeof this.willQos === 'undefined') {
this.willQos = "0";
$("#node-config-input-willQos").val("0");
}
function updateTLSOptions() {
if ($("#node-config-input-usetls").is(':checked')) {
$("#node-config-row-tls").show();
} else {
$("#node-config-row-tls").hide();
}
}
updateTLSOptions();
$("#node-config-input-usetls").on("click",function() {
updateTLSOptions();
});
var node = this;
function updateClientId() {
if ($("#node-config-input-cleansession").is(":checked")) {
$("#node-config-input-clientid").attr("placeholder",node._("mqtt.placeholder.clientid"));
} else {
// $("#node-config-input-clientid").attr("placeholder",node._("mqtt.placeholder.clientid-nonclean"));
$("#node-config-input-clientid").attr("placeholder",node._("Enter \"Internal\" to use device's IoTFlows agent connection if available"));
}
$("#node-config-input-clientid").change();
}
setTimeout(updateClientId,0);
$("#node-config-input-cleansession").on("click",function() {
updateClientId();
});
function updatePortEntry(){
var disabled = $("#node-config-input-port").prop("disabled");
if ($("#node-config-input-broker").val().indexOf("://") === -1){
if (disabled){
$("#node-config-input-port").prop("disabled", false);
}
}
else {
if (!disabled){
$("#node-config-input-port").prop("disabled", true);
}
}
}
$("#node-config-input-broker").on("change", function() {
updatePortEntry();
});
$("#node-config-input-broker").on( "keyup", function() {
updatePortEntry();
});
setTimeout(updatePortEntry,50);
},
oneditsave: function() {
if (!$("#node-config-input-usetls").is(':checked')) {
$("#node-config-input-tls").val("");
}
}
});
</script>