config data to local storage, notifications
config data to local storage, notifications

   
var iotCC = { var iotCC = {
mqttDefaultConfig: { mqttDefaultConfig: {
keepalive: 10, keepalive: 10,
protocolId: 'MQTT', protocolId: 'MQTT',
protocolVersion: 4, protocolVersion: 4,
clean: true, clean: true,
reconnectPeriod: 1000, reconnectPeriod: 1000,
connectTimeout: 30 * 1000, connectTimeout: 30 * 1000,
will: { will: {
topic: '/lwt', topic: '/lwt',
payload: 'Connection Closed abnormally..!', payload: 'Connection Closed abnormally..!',
qos: 2, qos: 2,
retain: true retain: true
}, },
rejectUnauthorized: false, rejectUnauthorized: false,
secure: false, secure: false,
simulateDevices: false, simulateDevices: false,
debug: false, debug: false,
}, },
mqttConfig: {}, mqttConfig: {},
mqttClient: null, mqttClient: null,
templates : [], templates : [],
setConfig: function(mqttConfig) {  
this.mqttConfig = Object.assign(this.mqttDefaultConfig, mqttConfig);  
},  
getConfig: function() {  
return this.mqttConfig;  
},  
init: function(mqttConfig) { init: function(mqttConfig) {
if ((typeof(window['mqttConfig']) == 'undefined')) { if (typeof jQuery == 'undefined') {
console.log ('mqttConfig is not defined in config.js. see config.sample.js'); this.showNotification('jQuery', 'IoT Control Center requires jQuery', 'dashboard', 'danger');
  logger.log ('IoT Control center requires jQuery');
return; return;
} else if (typeof jQuery == 'undefined') { }
console.log ('IoT Control center requires jQuery'); var config = this.getConfig();
  if (typeof config == 'undefined') {
  this.showNotification('MQTT connection data', 'Please set the MQTT connection data in Settings', 'dashboard', 'danger');
  logger.log ('MQTT conection data is not set');
return; return;
} }
this.setConfig(mqttConfig); // TODO: check for localStorage object
  this.mqttConfig = Object.assign(this.mqttDefaultConfig, config);
   
  this.showNotification('Connecting to MQTT server', 'Trying to connect to ' + this.mqttConfig.host + ':' + this.mqttConfig.port, 'dashboard', 'info');
   
this.mqttClient = mqtt.connect('ws' + (this.mqttConfig.secure==true?'s':'') + '://' + this.mqttConfig.host + ':' + this.mqttConfig.port, this.mqttConfig); this.mqttClient = mqtt.connect('ws' + (this.mqttConfig.secure==true?'s':'') + '://' + this.mqttConfig.host + ':' + this.mqttConfig.port, this.mqttConfig);
   
this.mqttClient.on('error', function (err) { this.mqttClient.on('error', function (err) {
console.log(err); logger.log('Error' + err);
iotCC.mqttClient.end(); iotCC.mqttClient.end();
}); });
   
this.mqttClient.on('connect', function () { this.mqttClient.on('connect', function () {
if (iotCC.mqttConfig.debug) console.log('client connected:' + iotCC.mqttConfig.clientId); if (iotCC.mqttConfig.debug) logger.log('client connected:' + iotCC.mqttConfig.clientId);
  iotCC.showNotification('Connected to MQTT server' + iotCC.mqttConfig.host + ':' + iotCC.mqttConfig.port, 'Waiting to receive data from devices.', 'dashboard', 'info');
}); });
   
this.mqttClient.subscribe('/iotcc/+/+/config', {qos: 1}); this.mqttClient.subscribe('/iotcc/+/+/config', {qos: 1});
this.mqttClient.subscribe('/iotcc/+/+/data', {qos: 1}); this.mqttClient.subscribe('/iotcc/+/+/data', {qos: 1});
this.mqttClient.subscribe('/iotcc/+/device', {qos: 1}); this.mqttClient.subscribe('/iotcc/+/device', {qos: 1});
   
if (typeof(window['iotCCInitEvents']) != 'undefined') { if (typeof(window['iotCCInitEvents']) != 'undefined') {
for(var i in iotCCInitEvents) { for(var i in iotCCInitEvents) {
try { try {
window[iotCCInitEvents[i]](this); window[iotCCInitEvents[i]](this);
} catch (err) { } catch (err) {
console.log (iotCCInitEvents[i] + 'is not defined'); logger.log (iotCCInitEvents[i] + 'is not defined');
console.log (err); logger.log (err);
} }
} }
} }
   
if (this.mqttConfig.simulateDevices) { if (this.mqttConfig.simulateDevices) {
this.simulateDevices(); this.simulateDevices();
} }
   
this.mqttClient.on('message', function (topic, message, packet) { this.mqttClient.on('message', function (topic, message, packet) {
try { try {
var json = JSON.parse(message.toString()); var json = JSON.parse(message.toString());
} catch(err){ } catch(err){
console.log ('There was a problem decoding the JSON message:\n\t' + message.toString()); logger.log ('There was a problem decoding the JSON message:\n\t' + message.toString());
console.log (err); logger.log (err);
return; return;
} }
var topicPath = topic.split('/'), var topicPath = topic.split('/'),
widgetId = iotCC.formatTopic(topic), widgetId = iotCC.formatTopic(topic),
widget, html; widget, html;
  $('.notification-dashboard').addClass('hide');
if (iotCC.mqttConfig.debug) { if (iotCC.mqttConfig.debug) {
console.log('Received Topic:= ' + topic + '\n\tMessage:= ' + message.toString()); logger.log('Received Topic:= ' + topic + '\n\tMessage:= ' + message.toString());
} }
   
if (topicPath[4] == 'config') { if (topicPath[4] == 'config') {
var page = {'pageId': json.pageId, 'pageName': json.pageName, 'icon': json.icon}; var page = {'pageId': json.pageId, 'pageName': json.pageName, 'icon': json.icon};
iotCC.addPage(page); iotCC.addPage(page);
if (json.widget == 'toggle') { if (json.widget == 'toggle') {
if ($('input[name="' + widgetId + '"]').exists() == false) { if ($('input[name="' + widgetId + '"]').exists() == false) {
html = '<label class="switch switch--material">'; html = '<label class="switch switch--material">';
html += '<input type="checkbox" name="' + widgetId + '" data-widget="toggle" data-status="' + (json.checked==true?'1':'0') + '" class="switch__input switch--material__input" ' + (json.checked==true?'checked="checked"':'') + '>'; html += '<input type="checkbox" name="' + widgetId + '" data-widget="toggle" data-status="' + (json.checked==true?'1':'0') + '" class="switch__input switch--material__input" ' + (json.checked==true?'checked="checked"':'') + '>';
html += '<div class="switch__toggle switch--material__toggle">'; html += '<div class="switch__toggle switch--material__toggle">';
html += '<div class="switch__handle switch--material__handle">'; html += '<div class="switch__handle switch--material__handle">';
html += '</div>'; html += '</div>';
html += '</div>'; html += '</div>';
html += '</label>'; html += '</label>';
json.content = html; json.content = html;
json.widgetId = widgetId; json.widgetId = widgetId;
json.callback = function() { json.callback = function() {
if ($(this).prop('checked') == true) { if ($(this).prop('checked') == true) {
$(this).data('status', 1); $(this).data('status', 1);
iotCC.mqttClient.publish(json.topic + '/data', '{"status":1}', {qos: 1, retained: false}); iotCC.mqttClient.publish(json.topic + '/data', '{"status":1}', {qos: 1, retained: false});
} else { } else {
$(this).data('status', 0); $(this).data('status', 0);
iotCC.mqttClient.publish(json.topic + '/data', '{"status":0}', {qos: 1, retained: false}); iotCC.mqttClient.publish(json.topic + '/data', '{"status":0}', {qos: 1, retained: false});
} }
}; };
iotCC.addWidget(json); iotCC.addWidget(json);
} }
} else if (json.widget == 'radios') { } else if (json.widget == 'radios') {
html = ''; html = '';
$(json.options).each(function(k, v) { $(json.options).each(function(k, v) {
html += '<label class="radio-button radio-button--material"><input type="radio" name="' + widgetId + '" data-widget="radios" data-status="' + v.status + '" class="radio-button__input radio-button--material__input" name="r" ' + (v.checked==true?'checked="checked"':'') + '><div class="radio-button__checkmark radio-button--material__checkmark"></div>'+ v.label +'</label>'; html += '<label class="radio-button radio-button--material"><input type="radio" name="' + widgetId + '" data-widget="radios" data-status="' + v.status + '" class="radio-button__input radio-button--material__input" name="r" ' + (v.checked==true?'checked="checked"':'') + '><div class="radio-button__checkmark radio-button--material__checkmark"></div>'+ v.label +'</label>';
}); });
if ($('input[name="' + widgetId + '"]').exists() == false) { if ($('input[name="' + widgetId + '"]').exists() == false) {
json.content = html; json.content = html;
json.widgetId = widgetId; json.widgetId = widgetId;
json.callback = function() { json.callback = function() {
iotCC.mqttClient.publish(json.topic + '/data', '{"status":"' + $(this).data('status') + '"}', {qos: 1, retained: false}); iotCC.mqttClient.publish(json.topic + '/data', '{"status":"' + $(this).data('status') + '"}', {qos: 1, retained: false});
}; };
iotCC.addWidget(json); iotCC.addWidget(json);
} }
} }
} else if (topicPath[4] == 'data') { } else if (topicPath[4] == 'data') {
widget = $('*[name="' + widgetId + '"]').first().data('widget'); widget = $('*[name="' + widgetId + '"]').first().data('widget');
if (widget == 'toggle') { if (widget == 'toggle') {
if ($('input[name="' + widgetId + '"]').exists() == true) { if ($('input[name="' + widgetId + '"]').exists() == true) {
if (json.status == 1) { if (json.status == 1) {
$('input[name="' + widgetId + '"]').data('status', 1).prop('checked', true); $('input[name="' + widgetId + '"]').data('status', 1).prop('checked', true);
} else { } else {
$('input[name="' + widgetId + '"]').data('status', 0).prop('checked', false); $('input[name="' + widgetId + '"]').data('status', 0).prop('checked', false);
} }
} }
} else if (widget == 'radios') { } else if (widget == 'radios') {
if ($('input[name="' + widgetId + '"]').filter('[data-status="' + json.status + '"]').exists() == true) { if ($('input[name="' + widgetId + '"]').filter('[data-status="' + json.status + '"]').exists() == true) {
$('input[name="' + widgetId + '"]').filter('[data-status="' + json.status + '"]').prop('checked', true); $('input[name="' + widgetId + '"]').filter('[data-status="' + json.status + '"]').prop('checked', true);
} }
} }
} else if (topicPath[3] == 'device') { } else if (topicPath[3] == 'device') {
$(json.pages).each(function(k, page) { $(json.pages).each(function(k, page) {
iotCC.addPage(page); iotCC.addPage(page);
}); });
} }
if (typeof(window['iotCCMessageEvents']) != 'undefined') { if (typeof(window['iotCCMessageEvents']) != 'undefined') {
for(var i in iotCCMessageEvents) { for(var i in iotCCMessageEvents) {
try { try {
window[iotCCMessageEvents[i]](this, topic, topicPath, json); window[iotCCMessageEvents[i]](this, topic, topicPath, json);
} catch (err) { } catch (err) {
console.log (iotCCMessageEvents[i] + 'is not defined'); logger.log (iotCCMessageEvents[i] + 'is not defined');
console.log (err); logger.log (err);
} }
} }
} }
}); });
   
iotCC.mqttClient.on('close', function () { this.mqttClient.on('close', function () {
console.log(mqttConfig.clientId + ' disconnected'); logger.log(iotCC.mqttConfig.clientId + ' disconnected');
}); });
}, },
formatTopic: function(topic) { formatTopic: function(topic) {
return topic.replace(/\//gi, '_').replace(/:/gi, '_').replace('_data', '').replace('_config', ''); return topic.replace(/\//gi, '_').replace(/:/gi, '_').replace('_data', '').replace('_config', '');
}, },
addWidget: function(json) { addWidget: function(json) {
if (this.templates[json.template] != undefined) { if (this.templates[json.template] != undefined) {
iotCC.addHtml(json, this.templates[json.template]); iotCC.addHtml(json, this.templates[json.template]);
} else { } else {
// TODO : fetch custom templates over HTTP // TODO : fetch custom templates over HTTP
$.get('assets/template/' + json.template + '.html', function(html) { $.get('assets/template/' + json.template + '.html', function(html) {
iotCC.templates[json.template] = html; iotCC.templates[json.template] = html;
iotCC.addHtml(json, html); iotCC.addHtml(json, html);
}); });
} }
}, },
addHtml: function(json, html) { addHtml: function(json, html) {
for (var key in json) { for (var key in json) {
html = html.replace('{' + key + '}', json[key]); html = html.replace('{' + key + '}', json[key]);
} }
html = html.replace(/{(\w*)}/g, ''); html = html.replace(/{(\w*)}/g, '');
var section = $('section.content').filter('[data-section="dashboard"]'); var section = $('section.content').filter('[data-section="dashboard"]');
// TODO: add after sort ? // TODO: add after sort ?
/*var widgets = $(section).find('div[data-page="' + json.pageId + '"]').find('div.widgetcontainer').length; /*var widgets = $(section).find('div[data-page="' + json.pageId + '"]').find('div.widgetcontainer').length;
if (widgets > 0 && widgets%2 == 0) { if (widgets > 0 && widgets%2 == 0) {
$(section).find('div[data-page="' + json.pageId + '"]').append('<div class="clearfix visible-sm-block"></div>'); $(section).find('div[data-page="' + json.pageId + '"]').append('<div class="clearfix visible-sm-block"></div>');
}*/ }*/
$(section).find('div[data-page="' + json.pageId + '"]').append(html); $(section).find('div[data-page="' + json.pageId + '"]').append(html);
if (json.callback != undefined) { if (json.callback != undefined) {
$('input[name="' + json.widgetId + '"]').click(json.callback); $('input[name="' + json.widgetId + '"]').click(json.callback);
} }
$(section).find('div[data-page="' + json.pageId + '"]').find('div.widgetcontainer').sort(function(a,b) { $(section).find('div[data-page="' + json.pageId + '"]').find('div.widgetcontainer').sort(function(a,b) {
return $(a).data('order') > $(b).data('order'); return $(a).data('order') > $(b).data('order');
}).appendTo('div[data-page="' + json.pageId + '"]'); }).appendTo('div[data-page="' + json.pageId + '"]');
}, },
addPage: function(page) { addPage: function(page) {
  $('label').filter('[data-pagination="0"]').parent().removeClass('hide');
if ($('div').filter('[data-page="' + page.pageId + '"]').exists() == false) { if ($('div').filter('[data-page="' + page.pageId + '"]').exists() == false) {
if ($('label').filter('[data-pagination="0"]').find('input').prop('checked') == true) { if ($('label').filter('[data-pagination="0"]').find('input').prop('checked') == true) {
var html = '<div class="row" data-page="' + page.pageId + '"></div>'; var html = '<div class="row page" data-page="' + page.pageId + '"></div>';
} else { } else {
var html = '<div class="row hide" data-page="' + page.pageId + '"></div>'; var html = '<div class="row page hide" data-page="' + page.pageId + '"></div>';
} }
$('section.content').filter('[data-section="dashboard"]').append(html); $('section.content').filter('[data-section="dashboard"]').append(html);
} }
if ($('div.pagination').find('label').filter('[data-pagination="' + page.pageId + '"]').exists() == false) { if ($('div.pagination').find('label').filter('[data-pagination="' + page.pageId + '"]').exists() == false) {
var html = '<label class="tab-bar__item tab-bar--material__item" data-pagination="' + page.pageId + '">'; var html = '<label class="tab-bar__item tab-bar--material__item" data-pagination="' + page.pageId + '">';
html += '<input type="radio" name="tab-bar-material-a">'; html += '<input type="radio" name="tab-bar-material-a">';
html += '<button class="tab-bar__button tab-bar--material__button">'; html += '<button class="tab-bar__button tab-bar--material__button">';
html += '<i class="tab-bar__icon tab-bar--material__icon ' + page.icon + '"></i>'; html += '<i class="tab-bar__icon tab-bar--material__icon ' + page.icon + '"></i>';
html += '<div class="tab-bar__label tab-bar--material__label">' + page.pageName + '</div>'; html += '<div class="tab-bar__label tab-bar--material__label">' + page.pageName + '</div>';
html += '</button>'; html += '</button>';
html += '</label>'; html += '</label>';
$('.pagination').append(html); $('.pagination').append(html);
$('label').filter('[data-pagination="' + page.pageId + '"]').click(function(e){ $('label').filter('[data-pagination="' + page.pageId + '"]').click(function(e){
var pageId = $(this).data('pagination'); var pageId = $(this).data('pagination');
$('div.row').addClass('hide'); $('div.page').addClass('hide');
$('div.row').filter('[data-page="' + pageId + '"]').removeClass('hide'); $('div.page').filter('[data-page="' + pageId + '"]').removeClass('hide');
}); });
} }
}, },
simulateDevices: function() { simulateDevices: function() {
//this.mqttClient.publish('/iotcc/' + mqttConfig.clientId + '/device', '{"command":"getDevice","param":""}', {qos: 1, retained: false}); //this.mqttClient.publish('/iotcc/' + mqttConfig.clientId + '/device', '{"command":"getDevice","param":""}', {qos: 1, retained: false});
this.mqttClient.publish('/iotcc/heater1/device', '{"name":"House heating 1","desc":"", "pages" : [{"pageId" : 10, "pageName" : "House heating", "icon": "ion-ios-home"}]}', {qos: 1, retained: false}); this.mqttClient.publish('/iotcc/heater1/device', '{"name":"House heating 1","desc":"", "pages" : [{"pageId" : 10, "pageName" : "House heating", "icon": "ion-ios-home"}]}', {qos: 1, retained: false});
this.mqttClient.publish('/iotcc/heater1/heater/config', '{"id":"100", "pageName": "House heating", "pageId": 10, "widget":"radios", "title":"Hollway Heater", "topic":"/iotcc/heater1/heater", "options":[{"checked":true, "label": "Off", "status":"1"}, {"label": "Confort", "status":"2"}, {"label": "Anti freeze", "status":"3"}, {"label": "Confort -2", "status":"4"}], "template": "template-3", "icon": "ion-ios-home", "bgcolor": "bg-blue", "order": 40}', {qos: 1, retained: false}); this.mqttClient.publish('/iotcc/heater1/heater/config', '{"id":"100", "pageName": "House heating", "pageId": 10, "widget":"radios", "title":"Hollway Heater", "topic":"/iotcc/heater1/heater", "options":[{"checked":true, "label": "Off", "status":"1"}, {"label": "Confort", "status":"2"}, {"label": "Anti freeze", "status":"3"}, {"label": "Confort -2", "status":"4"}], "template": "template-3", "icon": "ion-ios-home", "bgcolor": "bg-blue", "order": 40}', {qos: 1, retained: false});
   
this.mqttClient.publish('/iotcc/heater2/device', '{"name":"House heating 2","desc":"", "pages" : [{"pageId" : 10, "pageName" : "House heating", "icon": "ion-ios-home"}]}', {qos: 1, retained: false}); this.mqttClient.publish('/iotcc/heater2/device', '{"name":"House heating 2","desc":"", "pages" : [{"pageId" : 10, "pageName" : "House heating", "icon": "ion-ios-home"}]}', {qos: 1, retained: false});
this.mqttClient.publish('/iotcc/heater2/heater/config', '{"id":"101", "pageName": "House heating", "pageId": 10, "widget":"radios", "title":"Kitchen Heater", "topic":"/iotcc/heater2/heater", "options":[{"checked":true, "label": "Off", "status":"1"}, {"label": "Confort", "status":"2"}, {"label": "Anti freeze", "status":"3"}, {"label": "Confort -2", "status":"4"}], "template": "template-3", "icon": "ion-ios-home", "bgcolor": "bg-blue", "order": 30}', {qos: 1, retained: false}); this.mqttClient.publish('/iotcc/heater2/heater/config', '{"id":"101", "pageName": "House heating", "pageId": 10, "widget":"radios", "title":"Kitchen Heater", "topic":"/iotcc/heater2/heater", "options":[{"checked":true, "label": "Off", "status":"1"}, {"label": "Confort", "status":"2"}, {"label": "Anti freeze", "status":"3"}, {"label": "Confort -2", "status":"4"}], "template": "template-3", "icon": "ion-ios-home", "bgcolor": "bg-blue", "order": 30}', {qos: 1, retained: false});
   
this.mqttClient.publish('/iotcc/heater3/device', '{"name":"House heating 3","desc":"", "pages" : [{"pageId" : 10, "pageName" : "House heating", "icon": "ion-ios-home"}]}', {qos: 1, retained: false}); this.mqttClient.publish('/iotcc/heater3/device', '{"name":"House heating 3","desc":"", "pages" : [{"pageId" : 10, "pageName" : "House heating", "icon": "ion-ios-home"}]}', {qos: 1, retained: false});
this.mqttClient.publish('/iotcc/heater3/heater/config', '{"id":"102", "pageName": "House heating", "pageId": 10, "widget":"radios", "title":"Bedroom Heater", "topic":"/iotcc/heater3/heater", "options":[{"checked":true, "label": "Off", "status":"1"}, {"label": "Confort", "status":"2"}, {"label": "Anti freeze", "status":"3"}, {"label": "Confort -2", "status":"4"}], "template": "template-3", "icon": "ion-ios-home", "bgcolor": "bg-blue", "order": 10}', {qos: 1, retained: false}); this.mqttClient.publish('/iotcc/heater3/heater/config', '{"id":"102", "pageName": "House heating", "pageId": 10, "widget":"radios", "title":"Bedroom Heater", "topic":"/iotcc/heater3/heater", "options":[{"checked":true, "label": "Off", "status":"1"}, {"label": "Confort", "status":"2"}, {"label": "Anti freeze", "status":"3"}, {"label": "Confort -2", "status":"4"}], "template": "template-3", "icon": "ion-ios-home", "bgcolor": "bg-blue", "order": 10}', {qos: 1, retained: false});
   
this.mqttClient.publish('/iotcc/heater4/device', '{"name":"House heating 4","desc":"", "pages" : [{"pageId" : 10, "pageName" : "House heating", "icon": "ion-ios-home"}]}', {qos: 1, retained: false}); this.mqttClient.publish('/iotcc/heater4/device', '{"name":"House heating 4","desc":"", "pages" : [{"pageId" : 10, "pageName" : "House heating", "icon": "ion-ios-home"}]}', {qos: 1, retained: false});
this.mqttClient.publish('/iotcc/heater4/heater/config', '{"id":"103", "pageName": "House heating", "pageId": 10, "widget":"radios", "title":"Living Heater", "topic":"/iotcc/heater4/heater", "options":[{"checked":true, "label": "Off", "status":"1"}, {"label": "Confort", "status":"2"}, {"label": "Anti freeze", "status":"3"}, {"label": "Confort -2", "status":"4"}], "template": "template-3", "icon": "ion-ios-home", "bgcolor": "bg-blue", "order": 20}', {qos: 1, retained: false}); this.mqttClient.publish('/iotcc/heater4/heater/config', '{"id":"103", "pageName": "House heating", "pageId": 10, "widget":"radios", "title":"Living Heater", "topic":"/iotcc/heater4/heater", "options":[{"checked":true, "label": "Off", "status":"1"}, {"label": "Confort", "status":"2"}, {"label": "Anti freeze", "status":"3"}, {"label": "Confort -2", "status":"4"}], "template": "template-3", "icon": "ion-ios-home", "bgcolor": "bg-blue", "order": 20}', {qos: 1, retained: false});
   
this.mqttClient.publish('/iotcc/dogsheating/device', '{"name":"Dogs heating","desc":"", "pages" : [{"pageId" : 20, "pageName" : "Dogs heating", "icon": "ion-ios-paw"}]}', {qos: 1, retained: false}); this.mqttClient.publish('/iotcc/dogsheating/device', '{"name":"Dogs heating","desc":"", "pages" : [{"pageId" : 20, "pageName" : "Dogs heating", "icon": "ion-ios-paw"}]}', {qos: 1, retained: false});
this.mqttClient.publish('/iotcc/dogsheating/toggle1/config', '{"id":"200", "pageName": "Dogs heating", "pageId": 20, "widget":"toggle", "title":"Mara", "topic":"/iotcc/dogsheating/toggle1", "checked":true, "template": "template-1", "icon": "ion-ios-paw", "bgcolor": "bg-green", "order" : 10}', {qos: 1, retained: false}); this.mqttClient.publish('/iotcc/dogsheating/toggle1/config', '{"id":"200", "pageName": "Dogs heating", "pageId": 20, "widget":"toggle", "title":"Mara", "topic":"/iotcc/dogsheating/toggle1", "checked":true, "template": "template-1", "icon": "ion-ios-paw", "bgcolor": "bg-green", "order" : 10}', {qos: 1, retained: false});
this.mqttClient.publish('/iotcc/dogsheating/toggle2/config', '{"id":"201", "pageName": "Dogs Heating", "pageId": 20, "widget":"toggle", "title":"Linda", "topic":"/iotcc/dogsheating/toggle2", "template": "template-1", "icon": "ion-ios-paw", "bgcolor": "bg-green", "order" : 20}', {qos: 1, retained: false}); this.mqttClient.publish('/iotcc/dogsheating/toggle2/config', '{"id":"201", "pageName": "Dogs Heating", "pageId": 20, "widget":"toggle", "title":"Linda", "topic":"/iotcc/dogsheating/toggle2", "template": "template-1", "icon": "ion-ios-paw", "bgcolor": "bg-green", "order" : 20}', {qos: 1, retained: false});
   
this.mqttClient.publish('/iotcc/outdoorlights1/device', '{"name":"Outdoor lighting 1","desc":"", "pages" : [{"pageId" : 30, "pageName" : "Outdoor Lights", "icon": "ion-ios-home"}]}', {qos: 1, retained: false}); this.mqttClient.publish('/iotcc/outdoorlights1/device', '{"name":"Outdoor lighting 1","desc":"", "pages" : [{"pageId" : 30, "pageName" : "Outdoor Lights", "icon": "ion-ios-home"}]}', {qos: 1, retained: false});
this.mqttClient.publish('/iotcc/outdoorlights1/garage/config', '{"id":"300", "pageName": "Outdoor lightling", "pageId": 30, "widget":"toggle", "title":"Garage", "topic":"/iotcc/outdoorlights/garage", "checked":true, "template": "template-1", "icon": "ion-model-s", "bgcolor": "bg-orange", "order": 40}', {qos: 1, retained: false}); this.mqttClient.publish('/iotcc/outdoorlights1/garage/config', '{"id":"300", "pageName": "Outdoor lightling", "pageId": 30, "widget":"toggle", "title":"Garage", "topic":"/iotcc/outdoorlights/garage", "checked":true, "template": "template-1", "icon": "ion-model-s", "bgcolor": "bg-orange", "order": 40}', {qos: 1, retained: false});
   
this.mqttClient.publish('/iotcc/outdoorlights2/device', '{"name":"Outdoor lighting 2","desc":"", "pages" : [{"pageId" : 30, "pageName" : "Outdoor Lights", "icon": "ion-ios-home"}]}', {qos: 1, retained: false}); this.mqttClient.publish('/iotcc/outdoorlights2/device', '{"name":"Outdoor lighting 2","desc":"", "pages" : [{"pageId" : 30, "pageName" : "Outdoor Lights", "icon": "ion-ios-home"}]}', {qos: 1, retained: false});
this.mqttClient.publish('/iotcc/outdoorlights2/house1/config', '{"id":"301", "pageName": "Outdoor lightling", "pageId": 30, "widget":"toggle", "title":"House front", "topic":"/iotcc/outdoorlights/house1", "template": "template-1", "icon": "ion-ios-home", "bgcolor": "bg-orange", "order": 10}', {qos: 1, retained: false}); this.mqttClient.publish('/iotcc/outdoorlights2/house1/config', '{"id":"301", "pageName": "Outdoor lightling", "pageId": 30, "widget":"toggle", "title":"House front", "topic":"/iotcc/outdoorlights/house1", "template": "template-1", "icon": "ion-ios-home", "bgcolor": "bg-orange", "order": 10}', {qos: 1, retained: false});
   
this.mqttClient.publish('/iotcc/outdoorlights3/device', '{"name":"Outdoor lighting 3","desc":"", "pages" : [{"pageId" : 30, "pageName" : "Outdoor Lights", "icon": "ion-ios-home"}]}', {qos: 1, retained: false}); this.mqttClient.publish('/iotcc/outdoorlights3/device', '{"name":"Outdoor lighting 3","desc":"", "pages" : [{"pageId" : 30, "pageName" : "Outdoor Lights", "icon": "ion-ios-home"}]}', {qos: 1, retained: false});
this.mqttClient.publish('/iotcc/outdoorlights3/house2/config', '{"id":"302", "pageName": "Outdoor lightling", "pageId": 30, "widget":"toggle", "title":"House back", "topic":"/iotcc/outdoorlights/house2", "template": "template-1", "icon": "ion-ios-home", "bgcolor": "bg-orange", "order": 20}', {qos: 1, retained: false}); this.mqttClient.publish('/iotcc/outdoorlights3/house2/config', '{"id":"302", "pageName": "Outdoor lightling", "pageId": 30, "widget":"toggle", "title":"House back", "topic":"/iotcc/outdoorlights/house2", "template": "template-1", "icon": "ion-ios-home", "bgcolor": "bg-orange", "order": 20}', {qos: 1, retained: false});
   
this.mqttClient.publish('/iotcc/outdoorlights4/device', '{"name":"Outdoor lighting 4","desc":"", "pages" : [{"pageId" : 30, "pageName" : "Outdoor Lights", "icon": "ion-ios-home"}]}', {qos: 1, retained: false}); this.mqttClient.publish('/iotcc/outdoorlights4/device', '{"name":"Outdoor lighting 4","desc":"", "pages" : [{"pageId" : 30, "pageName" : "Outdoor Lights", "icon": "ion-ios-home"}]}', {qos: 1, retained: false});
this.mqttClient.publish('/iotcc/outdoorlights4/house3/config', '{"id":"302", "pageName": "Outdoor lightling", "pageId": 30, "widget":"toggle", "title":"House sides", "topic":"/iotcc/outdoorlights/house3", "template": "template-1", "icon": "ion-ios-home", "bgcolor": "bg-orange", "order": 30}', {qos: 1, retained: false}); this.mqttClient.publish('/iotcc/outdoorlights4/house3/config', '{"id":"302", "pageName": "Outdoor lightling", "pageId": 30, "widget":"toggle", "title":"House sides", "topic":"/iotcc/outdoorlights/house3", "template": "template-1", "icon": "ion-ios-home", "bgcolor": "bg-orange", "order": 30}', {qos: 1, retained: false});
}, },
  saveConfig: function() {
  var config = {
  'clientId': $('#mqttClientId').val(),
  'host': $('#mqttHost').val(),
  'port': $('#mqttPort').val(),
  'username': $('#mqttUser').val(),
  'password': $('#mqttPass').val(),
  'secure': $('#mqttSecure').prop('checked'),
  'debug': $('#mqttDebug').prop('checked'),
  'simulateDevices': $('#mqttSimulateDevices').prop('checked'),
  };
   
  try {
  localStorage.setItem('mqttConfig', JSON.stringify(config));
  iotCC.showNotification('MQTT connection data', 'Data saved succesfully', 'settings', 'info', 3);
  } catch(ex) {
  iotCC.showNotification('MQTT connection data', 'Cannot save data to local storage', 'settings', 'danger', 5);
  }
  },
  getConfig: function() {
  try {
  var config = JSON.parse(localStorage.getItem('mqttConfig'));
  $('#mqttClientId').val(config.clientId);
  $('#mqttHost').val(config.host);
  $('#mqttPort').val(config.port);
  $('#mqttUser').val(config.username);
  $('#mqttPass').val(config.password);
  $('#mqttSecure').prop('checked', config.secure);
  $('#mqttDebug').prop('checked', config.debug);
  $('#mqttSimulateDevices').prop('checked', config.simulateDevices);
  } catch(ex){
  return;
  }
  return config;
  },
  showNotification: function(title, content, section, cls, timer) {
  $('div.notification-' + section).removeClass('hide').addClass('callout-' + cls);
  $('div.notification-' + section).find('span').html(content);
  $('div.notification-' + section).find('h4').html(title);
  if (timer != undefined) {
  setTimeout(function(){
  $('div.notification-' + section).addClass('hide');
  }, timer * 1000)
  }
  }
} }
   
jQuery.fn.exists = function(){return ($(this).length > 0);} jQuery.fn.exists = function(){return ($(this).length > 0);}
$(document).ready(function(){ $(document).ready(function(){
iotCC.init(mqttConfig); iotCC.init();
   
$('.navigation').click(function(e){ $('.navigation').click(function(e){
e.preventDefault(); e.preventDefault();
var section = $(this).data('section'); var section = $(this).data('section');
$('.navigation').parent().removeClass('active'); $('.navigation').parent().removeClass('active');
$(this).parent().addClass('active'); $(this).parent().addClass('active');
$('section.content,section.content-header').addClass('hide'); $('section.content,section.content-header').addClass('hide');
$('section.content,section.content-header').filter('[data-section="' + section + '"]').removeClass('hide'); $('section.content,section.content-header').filter('[data-section="' + section + '"]').removeClass('hide');
}); });
   
$('label').filter('[data-pagination="0"]').click(function(e){ $('label').filter('[data-pagination="0"]').click(function(e){
$('div.row').removeClass('hide'); $('div.page').removeClass('hide');
  });
   
  $('#saveMqttConfig').click(function(e){
  e.preventDefault();
  iotCC.saveConfig();
}); });
}); });
   
// custom console // custom console
console = new Object(); logger = new Object();
console.log = function(log) { logger.log = function(log) {
$('.console').prepend(log + '<br />'); $('.console').prepend(log + '<br />');
}; };
console.debug = console.log;  
console.info = console.log;  
console.warn = console.log;  
console.error = console.log;  
   
file:a/config.sample.js (deleted)
var mqttConfig = {}  
mqttConfig.clientId = '';  
mqttConfig.host = '';  
mqttConfig.port = '';  
mqttConfig.user = '';  
mqttConfig.pass = '';  
mqttConfig.secure = 0;  
file:a/index.html -> file:b/index.html
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>IoT Control Center</title> <title>IoT Control Center</title>
<!-- Tell the browser to be responsive to screen width --> <!-- Tell the browser to be responsive to screen width -->
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport"> <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.min.css"> <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.5.0/css/font-awesome.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.5.0/css/font-awesome.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ionicons/2.0.1/css/ionicons.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ionicons/2.0.1/css/ionicons.min.css">
   
<link rel="stylesheet" href="bower_components/AdminLTE/dist/css/AdminLTE.min.css"> <link rel="stylesheet" href="bower_components/AdminLTE/dist/css/AdminLTE.min.css">
<link rel="stylesheet" href="bower_components/AdminLTE/dist/css/skins/_all-skins.min.css"> <link rel="stylesheet" href="bower_components/AdminLTE/dist/css/skins/_all-skins.min.css">
<link rel="stylesheet" href="assets/css/iotcc.css"> <link rel="stylesheet" href="assets/css/iotcc.css">
</head> </head>
   
<body class="hold-transition skin-blue sidebar-mini sidebar-collapse"> <body class="hold-transition skin-blue sidebar-mini sidebar-collapse">
<div class="wrapper"> <div class="wrapper">
<header class="main-header"> <header class="main-header">
<!-- Logo --> <!-- Logo -->
<a href="#dashboard" class="logo"> <a href="#dashboard" class="logo">
<!-- mini logo for sidebar mini 50x50 pixels --> <!-- mini logo for sidebar mini 50x50 pixels -->
<span class="logo-mini"><b>IoT</b></span> <span class="logo-mini"><b>IoT</b></span>
<!-- logo for regular state and mobile devices --> <!-- logo for regular state and mobile devices -->
<span class="logo-lg"><b>IoT</b> Control Center</span> <span class="logo-lg"><b>IoT</b> Control Center</span>
</a> </a>
   
<!-- Header Navbar: style can be found in header.less --> <!-- Header Navbar: style can be found in header.less -->
<nav class="navbar navbar-static-top"> <nav class="navbar navbar-static-top">
<!-- Sidebar toggle button--> <!-- Sidebar toggle button-->
<a href="#" class="sidebar-toggle" data-toggle="offcanvas" role="button"> <a href="#" class="sidebar-toggle" data-toggle="offcanvas" role="button">
<span class="sr-only">Toggle navigation</span> <span class="sr-only">Toggle navigation</span>
</a> </a>
<!-- Navbar Right Menu --> <!-- Navbar Right Menu -->
<div class="navbar-custom-menu"> <div class="navbar-custom-menu">
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">
<!-- Control Sidebar Toggle Button --> <!-- Control Sidebar Toggle Button -->
<li> <li>
<a href="#" data-toggle="control-sidebar"><i class="fa fa-gear"></i></a> <a href="#" data-toggle="control-sidebar"><i class="fa fa-gear"></i></a>
</li> </li>
</ul> </ul>
</div> </div>
</nav> </nav>
</header> </header>
<!-- Left side column. contains the logo and sidebar --> <!-- Left side column. contains the logo and sidebar -->
<aside class="main-sidebar"> <aside class="main-sidebar">
<!-- sidebar: style can be found in sidebar.less --> <!-- sidebar: style can be found in sidebar.less -->
<section class="sidebar"> <section class="sidebar">
<!-- /.search form --> <!-- /.search form -->
<!-- sidebar menu: : style can be found in sidebar.less --> <!-- sidebar menu: : style can be found in sidebar.less -->
<ul class="sidebar-menu"> <ul class="sidebar-menu">
<!--li class="header">Options</li--> <!--li class="header">Options</li-->
<li class="active"> <li class="active">
<a href="#" class="navigation" data-section="dashboard"> <a href="#" class="navigation" data-section="dashboard">
<i class="fa fa-dashboard"></i> <span>Dashboard</span> <i class="fa fa-dashboard"></i> <span>Dashboard</span>
</a> </a>
</li> </li>
<li> <li>
<a href="#" class="navigation" data-section="settings"> <a href="#" class="navigation" data-section="settings">
<i class="fa fa-gears"></i> <span>Settings</span> <i class="fa fa-gears"></i> <span>Settings</span>
</a> </a>
</li> </li>
<li> <li>
<a href="#" class="navigation" data-section="log"> <a href="#" class="navigation" data-section="log">
<i class="fa fa-align-justify"></i> <span>Log</span> <i class="fa fa-align-justify"></i> <span>Log</span>
</a> </a>
</li> </li>
</ul> </ul>
</section> </section>
<!-- /.sidebar --> <!-- /.sidebar -->
</aside> </aside>
   
<!-- Content Wrapper. Contains page content --> <!-- Content Wrapper. Contains page content -->
<div class="content-wrapper"> <div class="content-wrapper">
<section class="content-header" data-section="dashboard"> <section class="content-header" data-section="dashboard">
<h1>Dashboard</h1> <h1>Dashboard</h1>
<ol class="breadcrumb"> <ol class="breadcrumb">
<li class="active"><i class="fa fa-dashboard"></i> Dashboard</li> <li class="active"><i class="fa fa-dashboard"></i> Dashboard</li>
</ol> </ol>
<div class="">  
<div class="tab-bar tab-bar--top tab-bar--material pagination">  
<label class="tab-bar__item tab-bar--material__item" data-pagination="0">  
<input type="radio" name="tab-bar-material-a" checked="checked">  
<button class="tab-bar__button tab-bar--material__button">  
<i class="tab-bar__icon tab-bar--material__icon ion-drag"></i>  
<div class="tab-bar__label tab-bar--material__label">All</div>  
</button>  
</label>  
</div>  
</div>  
</section> </section>
   
<section class="content-header hide" data-section="settings"> <section class="content-header hide" data-section="settings">
<h1>Settings</h1> <h1>Settings</h1>
<ol class="breadcrumb"> <ol class="breadcrumb">
<li class="active"><i class="fa fa-gears"></i> Settings</li> <li class="active"><i class="fa fa-gears"></i> Settings</li>
</ol> </ol>
</section> </section>
   
<section class="content-header hide" data-section="log"> <section class="content-header hide" data-section="log">
<h1>Log</h1> <h1>Log</h1>
<ol class="breadcrumb"> <ol class="breadcrumb">
<li class="active"><i class="fa fa-gears"></i> Logs</li> <li class="active"><i class="fa fa-gears"></i> Logs</li>
</ol> </ol>
</section> </section>
   
<!-- Dashboard content --> <!-- Dashboard content -->
<section class="content" data-section="dashboard"> <section class="content" data-section="dashboard">
   
  <div class="row">
  <div class="col-md-12">
  <div class="tab-bar tab-bar--top tab-bar--material pagination hide">
  <label class="tab-bar__item tab-bar--material__item" data-pagination="0">
  <input type="radio" name="tab-bar-material-a" checked="checked">
  <button class="tab-bar__button tab-bar--material__button">
  <i class="tab-bar__icon tab-bar--material__icon ion-drag"></i>
  <div class="tab-bar__label tab-bar--material__label">All</div>
  </button>
  </label>
  </div>
  </div>
  </div>
   
  <div class="row">
  <div class="col-md-12">
  <div class="callout notification-dashboard hide">
  <h4></h4>
  <span></span>
  </div>
  </div>
  </div>
   
</section> </section>
   
<!-- Settings content --> <!-- Settings content -->
<section class="content hide" data-section="settings"> <section class="content hide" data-section="settings">
   
  <div class="row">
  <div class="col-md-12">
  <div class="callout notification-settings hide">
  <h4></h4>
  <span></span>
  </div>
  </div>
  </div>
   
  <div class="row">
  <div class="col-md-12">
  <div class="box box-primary">
  <div class="box-header with-border">
  <h3 class="box-title">MQTT connection data</h3>
  </div>
  <!-- /.box-header -->
  <!-- form start -->
  <form id="mqttConfig">
  <div class="box-body">
  <div class="form-group">
  <label for="mqttClientId">Client ID</label>
  <input type="text" class="form-control" id="mqttClientId" name="mqttClientId" placeholder="Client ID" />
  </div>
  <div class="form-group">
  <label for="mqttHost">Host</label>
  <input type="text" class="form-control" id="mqttHost" name="mqttHost" placeholder="Host" />
  </div>
  <div class="form-group">
  <label for="mqttHost">Port</label>
  <input type="text" class="form-control" id="mqttPort" name="mqttPort" placeholder="Port" />
  </div>
  <div class="form-group">
  <label for="mqttUser">Username</label>
  <input type="text" class="form-control" id="mqttUser" name="mqttUser" placeholder="Username" />
  </div>
  <div class="form-group">
  <label for="mqttPass">Password</label>
  <input type="text" class="form-control" id="mqttPass" name="mqttPass" placeholder="Password" />
  </div>
  <div class="checkbox">
  <label for="mqttSecure">
  <input type="checkbox" id="mqttSecure" name="mqttSecure" /> Secure
  </label>
  </div>
  <div class="checkbox">
  <label for="mqttDebug">
  <input type="checkbox" id="mqttDebug" name="mqttDebug" /> Debug
  </label>
  </div>
  <div class="checkbox">
  <label for="mqttSimulateDevices">
  <input type="checkbox" id="mqttSimulateDevices" name="mqttSimulateDevices" /> Simulate devices
  </label>
  </div>
  </div>
  <!-- /.box-body -->
   
  <div class="box-footer">
  <button type="submit" class="btn btn-primary" id="saveMqttConfig">Save</button>
  </div>
  </form>
  </div>
  </div>
  </div>
</section> </section>
<!-- /.content --> <!-- /.content -->
   
<!-- Log content --> <!-- Log content -->
<section class="content hide" data-section="log"> <section class="content hide" data-section="log">
<pre class="console"></pre> <pre class="console"></pre>
</section> </section>
<!-- /.content --> <!-- /.content -->
   
</div> </div>
<!-- /.content-wrapper --> <!-- /.content-wrapper -->
   
<footer class="main-footer"> <footer class="main-footer">
<div class="pull-right hidden-xs"> <div class="pull-right hidden-xs">
<b>Version</b> 0.1 <b>Version</b> 0.1
</div> </div>
<strong>Copyright &copy; 2017 <a href="http://www.razvi.ro">Razvan Stanga</a>.</strong> All rights reserved. <strong>Copyright &copy; 2017 <a href="http://www.razvi.ro">Razvan Stanga</a>.</strong> All rights reserved.
</footer> </footer>
   
<!-- Control Sidebar --> <!-- Control Sidebar -->
<aside class="control-sidebar control-sidebar-dark"> <aside class="control-sidebar control-sidebar-dark">
<!-- Create the tabs --> <!-- Create the tabs -->
<ul class="nav nav-tabs nav-justified control-sidebar-tabs"> <ul class="nav nav-tabs nav-justified control-sidebar-tabs">
<li><a href="#control-sidebar-home-tab" data-toggle="tab"><i class="fa fa-home"></i></a></li> <li><a href="#control-sidebar-home-tab" data-toggle="tab"><i class="fa fa-home"></i></a></li>
<li><a href="#control-sidebar-settings-tab" data-toggle="tab"><i class="fa fa-gear"></i></a></li> <li><a href="#control-sidebar-settings-tab" data-toggle="tab"><i class="fa fa-gear"></i></a></li>
</ul> </ul>
<!-- Tab panes --> <!-- Tab panes -->
<div class="tab-content"> <div class="tab-content">
<!-- Home tab content --> <!-- Home tab content -->
<div class="tab-pane" id="control-sidebar-home-tab"> <div class="tab-pane" id="control-sidebar-home-tab">
   
</div> </div>
<!-- /.tab-pane --> <!-- /.tab-pane -->
   
<!-- Settings tab content --> <!-- Settings tab content -->
<div class="tab-pane" id="control-sidebar-settings-tab"> <div class="tab-pane" id="control-sidebar-settings-tab">
<form method="post"> <form method="post">
<h3 class="control-sidebar-heading">General Settings</h3> <h3 class="control-sidebar-heading">General Settings</h3>
   
<div class="form-group"> <div class="form-group">
<label class="control-sidebar-subheading"> <label class="control-sidebar-subheading">
Report panel usage Report panel usage
<input type="checkbox" class="pull-right" checked> <input type="checkbox" class="pull-right" checked>
</label> </label>
   
<p> <p>
Some information about this general settings option Some information about this general settings option
</p> </p>
</div> </div>
<!-- /.form-group --> <!-- /.form-group -->
   
<div class="form-group"> <div class="form-group">
<label class="control-sidebar-subheading"> <label class="control-sidebar-subheading">
Allow mail redirect Allow mail redirect
<input type="checkbox" class="pull-right" checked> <input type="checkbox" class="pull-right" checked>
</label> </label>
   
<p> <p>
Other sets of options are available Other sets of options are available
</p> </p>
</div> </div>
<!-- /.form-group --> <!-- /.form-group -->
   
<div class="form-group"> <div class="form-group">
<label class="control-sidebar-subheading"> <label class="control-sidebar-subheading">
Expose author name in posts Expose author name in posts
<input type="checkbox" class="pull-right" checked> <input type="checkbox" class="pull-right" checked>
</label> </label>
   
<p> <p>
Allow the user to show his name in blog posts Allow the user to show his name in blog posts
</p> </p>
</div> </div>
<!-- /.form-group --> <!-- /.form-group -->
   
<h3 class="control-sidebar-heading">Chat Settings</h3> <h3 class="control-sidebar-heading">Chat Settings</h3>
   
<div class="form-group"> <div class="form-group">
<label class="control-sidebar-subheading"> <label class="control-sidebar-subheading">
Show me as online Show me as online
<input type="checkbox" class="pull-right" checked> <input type="checkbox" class="pull-right" checked>
</label> </label>
</div> </div>
<!-- /.form-group --> <!-- /.form-group -->
   
<div class="form-group"> <div class="form-group">
<label class="control-sidebar-subheading"> <label class="control-sidebar-subheading">
Turn off notifications Turn off notifications
<input type="checkbox" class="pull-right"> <input type="checkbox" class="pull-right">
</label> </label>
</div> </div>
<!-- /.form-group --> <!-- /.form-group -->
   
<div class="form-group"> <div class="form-group">
<label class="control-sidebar-subheading"> <label class="control-sidebar-subheading">
Delete chat history Delete chat history
<a href="javascript:void(0)" class="text-red pull-right"><i class="fa fa-trash-o"></i></a> <a href="javascript:void(0)" class="text-red pull-right"><i class="fa fa-trash-o"></i></a>
</label> </label>
</div> </div>
<!-- /.form-group --> <!-- /.form-group -->
</form> </form>
</div> </div>
<!-- /.tab-pane --> <!-- /.tab-pane -->
</div> </div>
</aside> </aside>
<!-- /.control-sidebar --> <!-- /.control-sidebar -->
<!-- Add the sidebar's background. This div must be placed <!-- Add the sidebar's background. This div must be placed
immediately after the control sidebar --> immediately after the control sidebar -->
<div class="control-sidebar-bg"></div> <div class="control-sidebar-bg"></div>
   
</div> </div>
<!-- ./wrapper --> <!-- ./wrapper -->
   
<script type="text/javascript" src="bower_components/jquery/dist/jquery.min.js"></script> <script type="text/javascript" src="bower_components/jquery/dist/jquery.min.js"></script>
<script type="text/javascript" src="bower_components/bootstrap/dist/js/bootstrap.min.js"></script> <script type="text/javascript" src="bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
<!-- AdminLTE App --> <!-- AdminLTE App -->
<script type="text/javascript"> <script type="text/javascript">
var AdminLTEOptions = { var AdminLTEOptions = {
sidebarExpandOnHover: true sidebarExpandOnHover: true
}; };
</script> </script>
<script type="text/javascript" src="bower_components/AdminLTE/dist/js/app.min.js"></script> <script type="text/javascript" src="bower_components/AdminLTE/dist/js/app.min.js"></script>
<!-- AdminLTE for demo purposes --> <!-- AdminLTE for demo purposes -->
<script type="text/javascript" src="bower_components/AdminLTE/dist/js/demo.js"></script> <script type="text/javascript" src="bower_components/AdminLTE/dist/js/demo.js"></script>
   
<script type="text/javascript" src="assets/js/mqtt.min.js"></script> <script type="text/javascript" src="assets/js/mqtt.min.js"></script>
<script type="text/javascript" src="config.js"></script> <script type="text/javascript" src="config.js"></script>
<script type="text/javascript" src="assets/js/iotcc.js"></script> <script type="text/javascript" src="assets/js/iotcc.js"></script>
   
</body> </body>
</html> </html>
   
comments