'use strict'; /* global Backbone, _, $, AmCharts, notification */ /* global mainview */ /* jshint browser: true , devel: true*/ (function($) { var GraphView; var mqttConfig = { orgId: 'qz0da4', userName: 'a-qz0da4-dfwwdkmkzr', appKey: '9txJEf3Cjy7hkSOvkv', prefix: 'iot-2/type/mDot/id/' }; var sendAuthentication = function(xhr) { var user = 'a-qz0da4-dfwwdkmkzr'; // Your actual username var pass = '9txJEf3Cjy7hkSOvkv'; // Your actual password var token = user.concat(':', pass); xhr.setRequestHeader('Authorization', ('Basic '.concat(btoa(token)))); console.log('Auth:', ('Basic '.concat(btoa(token)))); }; var EventsModel = Backbone.Model.extend({ initialize: function() { _.bindAll(this, 'processAdded'); this.on('all', function(d) { console.log('model:all', d); this.temporal = {low: 0, high: 0}; }); this.on('remove', function() { $('#output').empty(); }); this.on('add', function() { this.processAdded(); }); }, findOccupancy: function(ts) { /* Get a branch from the date tree and see if the reduced set of records has a matching timestamp.. */ let count = 0; let tsDate = new Date(ts); let tsMS = tsDate.getTime(); let branch = this.dateTree[tsDate.getFullYear().toString()][tsDate.getMonth().toString()][tsDate.getDate().toString()]; if (typeof branch === 'undefined') {return count;} _(branch).each(function(item) { if ((tsMS >= item.startMS) && (tsMS <= item.endMS)) { count = item.count; return count; } }, this); return count; }, buildSpeedDateTree: function(occupancy) { /* Builds a tree to help speed up occupancy searching */ var _tree = {}; _(occupancy).each(function(item) { let newItem = item; let day, month,year; let _date = new Date(item.start); newItem.startMS = new Date(item.start).getTime(); newItem.endMS = new Date(item.end).getTime(); day = _date.getDate().toString(); month = _date.getMonth().toString(); year = _date.getFullYear().toString(); if (!_tree.hasOwnProperty(year)) { _tree[year] = {}; } if (_tree.hasOwnProperty(year)) { if (!_tree[year].hasOwnProperty(month)) { _tree[year][month] = {}; } if (_tree[year].hasOwnProperty(month)) { if (!_tree[year][month].hasOwnProperty(day)) { _tree[year][month][day] = []; } _tree[year][month][day].push(newItem); } } }, this); return _tree; }, processAdded: function() { console.log('Model:ProcessAdded'); console.time('processAdd'); var skipOccupancy = false; var tempCollection = new Backbone.Collection(); var events; _.invoke(DeviceCollection.toArray(), 'destroy'); events = this.get('events'); this.dateTree = this.buildSpeedDateTree(events.occupancy); if (Object.keys(this.dateTree).length === 0) { skipOccupancy = true; } _(events.data).each(function(i) { let _occupancy = 0; if (!skipOccupancy) { _occupancy = this.findOccupancy(i.timestamp, events.occupancy); } tempCollection.add({ dt: Date.create(i.timestamp).addHours(1), lux: i.lux, temp: i.temp, co2: i.co2, humid: i.humidity, noise: i.sound, occupancy: _occupancy }); }, this); DeviceCollection.models = tempCollection.models; console.timeEnd('processAdd'); DeviceCollection.trigger('update'); notification.notify('success', 'Data loaded'); }, decoder: function(data) { var _obj = {}; var _data = window.atob(data).split(''); var bytes = _data.map(i => i.charCodeAt()); _obj.light = parseInt('0x' + ('0' + bytes[0]).substr(-2) + ('0' + bytes[1]).substr( -2)); _obj.co2 = parseInt(_data[2] + _data[3] + _data[4] + _data[5] + _data[6], 10); _obj.temp = (parseInt(_data[7] + _data[8] + _data[9] + _data[10] + _data[11], 10) - 1000) / 10; _obj.humid = (parseInt(_data[12] + _data[13] + _data[14] + _data[15] + _data[16], 10) / 10); _obj.noise = parseInt('0x' + ('0' + bytes[17]).substr(-2) + ('0' + bytes[18]).substr( -2)); _obj.binData = bytes; return _obj; }, dateTime: function($date) { var dateTime = new Date.create($date); var date = dateTime.format('{yyyy}-{MM}-{dd}'); var time = dateTime.format('{HH}:{mm}:{ss}'); return { dateTime: dateTime.format('{yyyy}-{MM}-{dd} {HH}:{mm}:{ss}'), date: date, time: time }; } }); var mDotCollection = Backbone.Collection.extend({ model: EventsModel, url: 'https://qz0da4.internetofthings.ibmcloud.com/api/v0002/historian/types/mDot/devices/', initialize: function() { this.on('update', function() { }); } }); var ItemView = Backbone.View.extend({ tagName: 'div', className: 'item mui-container', initialize: function() { this.template = _.template($('#item-template').html()); console.log('ItemView:Init'); }, render: function() { console.log('ItemView:Render'); _(this.model.events).each(function(i) { this.$el.append(this.template({item: i})); }, this); return this; } }); var MDOT = Backbone.View.extend({ model: EventsModel, el: $('#output'), events: { 'click button#refresh': 'refresh' }, initialize: function() { _.bindAll(this, 'render', 'refresh', 'update'); this.collection.bind('change reset add remove', this.render, this); this.template = _.template($('#loaded-template').html()); }, refresh: function() { }, update: function() { console.log('MDOT:update'); this.collection.each(function(model) { }); }, render: function() { console.log('MDOT:render'); $('#output').empty(); return this; } }); var MainModel = Backbone.Model.extend({}); var MainView = Backbone.View.extend({ el: $('#main'), template: _.template($('#main-template').html()), events: { 'change select#device': 'changeDevice', 'click button#refresh': 'updateDevice', submit: function(event) {} }, initialize: function() { _.bindAll(this, 'render', 'changeDevice', 'updateDevice'); this.model.on('change', this.updateDevice); console.log('MainView:', this); this.render(); }, render: function() { $(this.el).html(this.template()); return this; }, changeDevice: function() { var newDevice; console.log('MainView:ChangeDevice'); newDevice = this.$el.find('#device')[0].value; this.model.set('device', newDevice); }, updateDevice: function() { var fetchObj = {beforeSend: sendAuthentication}; console.log('MainView:Updatedevice'); notification.clearAll(); if (this.model.has('device')) { this.collection.url = '/apiv2/mdot/' + this.model.get('device'); $('#output').empty(); notification.notify('info', 'Loading...'); this.collection.fetch(fetchObj); } else { console.error('Nothing to get!'); } } }); GraphView = Backbone.View.extend({ el: $('#graph'), initialize: function() { this.modes = ['', 'lux', 'temp', 'co2', 'humid', 'noise']; this.titles = [ '', 'Light Levels', 'Temperature', 'Co2 Levels', 'Humidity', 'Sound' ]; this.mode = 0; // Config AMChart this.chart = {}; this.categoryAxesSettings = new AmCharts.CategoryAxesSettings(); this.dataSet = new AmCharts.DataSet(); console.log('GraphView!'); _.bindAll(this, 'render', 'updateGraphV2', 'setupChart'); this.collection.on('update', function(d) { console.log('GraphView Collection update trigger!!'); this.updateGraphV2(); }, this); }, events: { 'change select#displaymode': 'changeMode' }, setupChart: function() { console.log('chart:SetupChart'); this.categoryAxesSettings.minPeriod = 'mm'; this.chart.categoryAxesSettings = this.categoryAxesSettings; this.dataSet.color = '#b0de09'; this.chart.dataSets = [this.dataSet]; }, doChartV2: function(chartData) { var self = this; console.time('doChartV2'); self.chart = AmCharts.makeChart('chartdiv', { type: 'serial', theme: 'light', legend: { useGraphSettings: true, color: '#fff', switchColor: '#556374' }, color: '#ffffff', dataProvider: chartData, dataDateFormat: 'YYYY-MM-DDTHH:NN:SS.QQQ', synchronizeGrid: true, valueAxes: [ { id: 'lux', axisColor: '#FFC802', axisThickness: 2, axisAlpha: 1, position: 'left', gridColor: '#556374' }, { id: 'co2', axisColor: 'rgba(0,191,255,1)', axisThickness: 2, axisAlpha: 1, position: 'right', gridColor: '#556374' }, { id: 'temp', axisColor: 'rgba(46,255,0,1)', axisThickness: 2, gridAlpha: 0, offset: 50, axisAlpha: 1, position: 'left', gridColor: '#556374' }, { id: 'humid', axisColor: 'rgba(255,0,99,1)', axisThickness: 2, axisAlpha: 1, offset: 50, position: 'right', gridColor: '#556374' }/*, { id: 'noise', axisColor: 'rgb(99, 157, 189)', axisThickness: 2, gridAlpha: 0, offset: 100, axisAlpha: 1, position: 'left', gridColor: '#556374' }*/ ,{ id: 'occupancy', axisColor: '#aaaaaa', axisThickness: 2, gridAlpha: 0, offset: 100, axisAlpha: 1, position: 'right', gridColor: '#556374' } ], graphs: [{id: 'occ', valueAxis: 'occupancy', type: 'line', clustered: false, columnWidth: 1, lineColor: '#aaaaaa', title: 'Occupancy', valueField: 'occupancy', fillColor: '#888888', fillAlphas: 0.2, fillToAxis: 'x' }, { valueAxis: 'lux', lineColor: '#FFC802', title: 'Light Level', valueField: 'lux', fillAlphas: 0 }, { valueAxis: 'co2', lineColor: 'rgba(0,191,255,1)', title: 'Co2', valueField: 'co2', fillAlphas: 0 }, { valueAxis: 'temp', lineColor: 'rgba(46,255,0,1)', title: 'Temperature', valueField: 'temp', fillAlphas: 0 }, { valueAxis: 'humid', lineColor: 'rgba(255,0,99,1)', title: 'Humidity', valueField: 'humid', fillAlphas: 0 }/*, { valueAxis: 'noise', lineColor: 'rgb(99, 157, 189)', title: 'Sound', valueField: 'noise', fillAlphas: 0 }*/ ], chartScrollbar: { graph: 'occ',oppositeAxis: false, offset: 30, scrollbarHeight: 80, backgroundAlpha: 0, selectedBackgroundAlpha: 0.1, selectedBackgroundColor: '#888888', graphFillAlpha: 0, graphLineAlpha: 0.5, selectedGraphFillAlpha: 0, selectedGraphLineAlpha: 1, autoGridCount: true, color: '#AAAAAA', updateOnReleaseOnly: true }, chartCursor: { cursorPosition: 'mouse', cursorColor: '#14bfff', color: '#000' }, categoryField: 'date', categoryAxis: { minPeriod: '15mm', parseDates: true, axisColor: 'rgba(255,255,255,0.8)', minorGridEnabled: true, gridColor: '#556374' }, export: { enabled: true, position: 'bottom-right' } }); $('#chartdiv').empty(); self.chart.write('chartdiv'); console.timeEnd('doChartV2'); }, updateGraphV2: function() { console.time('updateGraphV2'); var self = this; var chartData = []; console.time('chartData'); _(this.collection.models).each(function(i) { chartData.push({ date: i.get('dt'), co2: i.get('co2'), humid: i.get('humid'), lux: i.get('lux'), noise: i.get('noise'), temp: i.get('temp'), occupancy: i.get('occupancy') }); }); console.timeEnd('chartData'); console.log('Record count:',chartData.length); // Console.log(chartData); self.doChartV2(chartData); console.timeEnd('updateGraphV2'); } }); notification.configProfile('global', { stacking: false }); var DeviceCollection = new Backbone.Collection; var OccupancyCollection = new Backbone.Collection; var mdotCollection = new mDotCollection(); var mainSettings = new MainModel(); var mainview = new MainView({ collection: mdotCollection, model: mainSettings }); var mdot = new MDOT({collection: mdotCollection}); var grapher = new GraphView({collection: DeviceCollection}); })(jQuery);