(function () { 'use strict'; angular .module('hyperion') .controller('EquipmentPoELightController', ['$rootScope', '$scope', '$attrs', '$http', '$location', '$timeout', '$filter', '$sessionStorage', '$mdPanel', 'timerange', 'modalDialog', 'authService', 'CONFIG', '$stateParams', 'debounce', 'sysClock', 'wiz', function($rootScope, $scope, $attrs, $http, $location, $timeout, $filter, $sessionStorage, $mdPanel, timerange, modalDialog, authService, CONFIG, $stateParams, debounce, sysClock, wiz){ const scenes = [ {id:0, name:'OFF', icon:'power_settings_new', rgbw:false, state: false, speed:null, brightness: null, temperature: null, color:null}, // Custom Color {id:33, name:'Custom Color', icon:'style', rgbw:true, state: true, speed:null, brightness: 100, temperature: null, color:'ffffff'}, // White {id:11, name:'Warm white', icon:'light_mode', rgbw:false, state: true, speed:null, brightness: 100, temperature: null, color:null}, {id:12, name:'Daylight', icon:'light_mode', rgbw:false, state: true, speed:null, brightness: 100, temperature: null, color:null}, {id:13, name:'Cool white', icon:'light_mode', rgbw:false, state: true, speed:null, brightness: 100, temperature: null, color:null}, {id:34, name:'Custom white', icon:'light_mode', rgbw:false, state: true, speed:null, brightness: 100, temperature: 2700, color:null}, // Simple {id:6, name:'Cozy', icon:'light_mode', rgbw:true, state: true, speed:null, brightness: 100, temperature: null, color:null}, {id:17, name:'True colors', icon:'light_mode', rgbw:true, state: true, speed:null, brightness: 100, temperature: null, color:null}, {id:16, name:'Relax', icon:'light_mode', rgbw:true, state: true, speed:null, brightness: 100, temperature: null, color:null}, {id:15, name:'Focus', icon:'light_mode', rgbw:true, state: true, speed:null, brightness: 100, temperature: null, color:null}, {id:18, name:'TV Time', icon:'light_mode', rgbw:true, state: true, speed:null, brightness: 100, temperature: null, color:null}, // Routine {id:9, name:'Wake Up', icon:'alarm', rgbw:false, state: true, speed:null, brightness: null, temperature: null, color:null}, {id:10, name:'Bedtime', icon:'hotel', rgbw:false, state: true, speed:null, brightness: 100, temperature: null, color:null}, // Functional {id:19, name:'Plant growth', icon:'light_mode', rgbw:true, state: true, speed:null, brightness: 100, temperature: null, color:null}, {id:14, name:'Night Light', icon:'nights_stay', rgbw:false, state: true, speed:null, brightness: null, temperature: null, color:null}, // Dynamic {id:1, name:'Ocean', icon:'light_mode', rgbw:true, state: true, speed:100, brightness: 100, temperature: null, color:null}, {id:2, name:'Romance', icon:'light_mode', rgbw:true, state: true, speed:100, brightness: 100, temperature: null, color:null}, {id:3, name:'Sunset', icon:'light_mode', rgbw:true, state: true, speed:100, brightness: 100, temperature: null, color:null}, {id:4, name:'Party', icon:'light_mode', rgbw:true, state: true, speed:100, brightness: 100, temperature: null, color:null}, {id:5, name:'Fireplace', icon:'light_mode', rgbw:true, state: true, speed:100, brightness: 100, temperature: null, color:null}, {id:7, name:'Forest', icon:'light_mode', rgbw:true, state: true, speed:100, brightness: 100, temperature: null, color:null}, {id:8, name:'Pastel Colors', icon:'light_mode', rgbw:true, state: true, speed:100, brightness: 100, temperature: null, color:null}, {id:20, name:'Spring', icon:'light_mode', rgbw:true, state: true, speed:100, brightness: 100, temperature: null, color:null}, {id:21, name:'Summer', icon:'light_mode', rgbw:true, state: true, speed:100, brightness: 100, temperature: null, color:null}, {id:22, name:'Fall', icon:'light_mode', rgbw:true, state: true, speed:100, brightness: 100, temperature: null, color:null}, {id:23, name:'Deep dive', icon:'light_mode', rgbw:true, state: true, speed:100, brightness: 100, temperature: null, color:null}, {id:24, name:'Jungle', icon:'light_mode', rgbw:true, state: true, speed:100, brightness: 100, temperature: null, color:null}, {id:25, name:'Mojito', icon:'light_mode', rgbw:true, state: true, speed:100, brightness: 100, temperature: null, color:null}, {id:26, name:'Club', icon:'light_mode', rgbw:true, state: true, speed:100, brightness: 100, temperature: null, color:null}, {id:29, name:'Candlelight', icon:'light_mode', rgbw:true, state: true, speed:100, brightness: 100, temperature: null, color:null}, {id:30, name:'Golden white', icon:'light_mode', rgbw:true, state: true, speed:100, brightness: 100, temperature: null, color:null}, {id:31, name:'Pulse', icon:'light_mode', rgbw:true, state: true, speed:100, brightness: 100, temperature: null, color:null}, {id:32, name:'Steampunk', icon:'light_mode', rgbw:true, state: true, speed:100, brightness: 100, temperature: null, color:null}, // Special {id:27, name:'Christmas', icon:'light_mode', rgbw:true, state: true, speed:100, brightness: 100, temperature: null, color:null}, {id:28, name:'Halloween', icon:'light_mode', rgbw:true, state: true, speed:100, brightness: 100, temperature: null, color:null}, ]; var equipment_state_backup = null; $scope.settings = {}; $rootScope.light_settings_changed = false; // FARBTASTIC Color Picker //////////////////////////////////////////////////////////// // https://acko.net/blog/farbtastic-jquery-color-picker-plug-in/ var farbtastic = null; // farbtastic object $(document).ready(function() { farbtastic = $.farbtastic('#colorpicker'); //farbtastic.setColor('#f7a1a1'); // default value - shoulw be current Light Color value farbtastic.linkTo(color => { if(!$scope.$$phase) { $scope.$apply(function(scope) { scope.settings.color = color.slice(1); // remove '#' $scope.changeSettings(); }); } }); }); $scope.$watch('settings.color', function(newValue, oldValue, scope) { if(farbtastic) { farbtastic.setColor('#' + newValue); } }); /////////////////////////////////////////////////////////////////////////////////////// var time = timerange.calc($sessionStorage.currentUser.timerange); $scope.$on('trUpdate', function(event, payload) { // time range update event console.log('trUpdate event: ' + $sessionStorage.currentUser.timerange); time = timerange.calc($sessionStorage.currentUser.timerange); load_data().catch(error => { console.warn(error); }); }); /////////////////////////////////////////////////////////////////////////////////////// $scope.$on('clockUpdate', function(event, payload) { // time range update event console.log('clockUpdate event: ' + sysClock.getTimeZone()); Highcharts.setOptions({ colors: ['#192a56', '#00b894', '#ff4757'], time: { timezone: sysClock.getTimeZone() } }); if($scope.chart_consumption_drilldown) { // if data is shown in hourly graph chart_load_hourly_data($scope.chart_consumption_drilldown_timestamp); } else { // if data is shown in daily graph chart_load_daily_data(); } }); /////////////////////////////////////////////////////////////////////////////////////// function load_data() { return new Promise((resolve, reject) => { let time_start = moment(time.start).format('YYYY-MM-DDTHH:mm:ss'); let time_end = moment(time.today).format('YYYY-MM-DDTHH:mm:ss'); console.log('time_start: ' + time_start); console.log('time_end: ' + time_end); authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'equipment(id:"' + $scope.equipment.id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'id ' + 'name ' + 'status ' + 'company {' + 'id ' + '}' + 'location {' + 'id ' + '}' + 'room {' + 'id ' + '}' + 'type { ' + 'id ' + 'name ' + '} ' + 'brand { ' + 'id ' + 'name ' + '} ' + 'model { ' + 'id ' + 'name ' + '} ' + 'request {' + 'id ' + 'created ' + 'status ' + '} ' + 'groups {' + 'id ' + 'name ' + '} ' + '... on EquipmentLight { ' + 'mac ' + 'ip ' + 'power_consumption ' + 'co2_emission ' + 'isemergency ' + 'power_lock ' + 'lifespan ' + 'rgbw ' + 'runtime ' + 'scenes {' + 'id ' + 'name ' + '} ' + 'ups { ' + 'id ' + 'name ' + '} ' + 'state { ' + 'connected ' + 'rssi ' + 'state ' + 'r ' + 'g ' + 'b ' + 'c ' + 'w ' + 'temperature ' + 'brightness ' + 'scene ' + 'on_since ' + 'off_since ' + '} ' + '} ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve if(response.data.data.equipment.length){ console.log(response.data.data.equipment[0]); $scope.equipment = response.data.data.equipment[0]; $scope.equipment.isvisible = true; // request status $rootScope.request_ongoing = ['new','waiting'].includes($scope.equipment.request.status); setLightSettings(); // LifeSpan $scope.equipment.life = { visible: isNaN($scope.equipment.lifespan), max: parseInt($scope.equipment.lifespan), used: ($scope.equipment.runtime?parseInt($scope.equipment.runtime):0), percentage: parseFloat((($scope.equipment.runtime?parseInt($scope.equipment.runtime):0)/parseInt($scope.equipment.lifespan))*100).toFixed(2) }; /* console.log($scope.equipment.isemergency); if($scope.equipment.isemergency) { var state = $scope.equipment.isemergency; $scope.equipment.isemergency = {state: state, display:(state?'Yes':'No')}; } else { $scope.equipment.isemergency = {state: false, display:'No'}; } console.log($scope.equipment.isemergency); */ if($scope.equipment.state) { if(!$scope.equipment.state.connected) { $scope.equipment.state.rssi = 'Disconnected'; // display value $scope.equipment.state.rssi_level = 'disconnected'; // icon name } else { var strength = Math.abs(parseInt($scope.equipment.state.rssi)); if(strength<=55) { $scope.equipment.state.rssi_level = 'excellent'; } else if(55 { // create chart object resolve(null); }); } else { $scope.isLoading = false; // hide ajax loader } },function(error) { // failure console.error(error); reject(error); } ); }); }); } $timeout(function() { load_data().then(() => { // nothing }).catch(error => { console.warn(error); }); }, 100); //////////////////////////////////////////////////////////////////////////////////////////////////////////// // SET $scope.settings (LIGHT) based on $scope.equipment.state const setLightSettings = () => { if(!$scope.equipment.state) { $scope.equipment.state = {}; } equipment_state_backup = angular.copy($scope.equipment.state); if($scope.equipment.state.state) { // if turned on let scene_id = null; if(!$scope.equipment.state.scene && $scope.equipment.state.temperature) { scene_id= 34; // Custom White } else if(!$scope.equipment.state.scene && $scope.equipment.state.r) { scene_id = 33; // Custom Color } else { scene_id = $scope.equipment.state.scene; } equipment_state_backup.scene = scene_id; $scope.settings = angular.copy($filter('filter')(scenes, {id: scene_id})[0]); if($scope.settings.speed) { $scope.settings.speed_show = true; $scope.settings.speed = $scope.equipment.state.speed; } else { $scope.settings.speed_show = false; } if($scope.settings.brightness) { $scope.settings.brightness_show = true; $scope.settings.brightness = $scope.equipment.state.brightness; } else { $scope.settings.brightness_show = false; } if($scope.settings.temperature) { $scope.settings.temperature_show = true; $scope.settings.temperature = $scope.equipment.state.temperature; } else { $scope.settings.temperature_show = false; } if($scope.settings.color) { $scope.settings.color_show = true; $scope.settings.r = $scope.equipment.state.r; $scope.settings.g = $scope.equipment.state.g; $scope.settings.b = $scope.equipment.state.b; $scope.settings.c = $scope.equipment.state.c; $scope.settings.w = $scope.equipment.state.w; if($scope.settings.r) { // ... or any other channels $scope.settings.color = $scope.settings.r.toString(16) + $scope.settings.g.toString(16) + $scope.settings.b.toString(16); farbtastic.setColor($scope.settings.color); } } else { $scope.settings.color_show = false; } } else { $scope.settings = angular.copy($filter('filter')(scenes, {id: 0})[0]); } }; //////////////////////////////////////////////////////////////////////////////////////////////////////////// $scope.loadLightScenes = () => { let sceneList = scenes.filter(scene => scene.rgbw === false); // ligh equipment always supports non-rgbw scenes if($scope.equipment.rgbw) { sceneList = sceneList.concat(scenes.filter(scene => scene.rgbw === true)); } return sceneList; }; //////////////////////////////////////////////////////////////////////////////////////////////////////////// $scope.loadEquipmentUPS = () => { authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'equipment_index(location_id:"' + $scope.equipment.location.id + '", type_id:1) { ' + 'id ' + 'name ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve $scope.ups = response.data.data.equipment_index; },function(error) { // failure console.error(error); } ); }); }; $scope.selectEquipmentUPS = () => { if($scope.equipment.ups) { var request = {'query': 'mutation { ' + 'equipmentupsattach ( ' + 'company_id: "' + $scope.equipment.company.id + '", ' + 'equipment_id: "' + $scope.equipment.id + '", ' + 'ups_id: "' + $scope.equipment.ups.id + '" ' + ')' + '}' }; } else { var request = {'query': 'mutation { ' + 'equipmentupsdetach ( ' + 'company_id: "' + $scope.equipment.company.id + '", ' + 'equipment_id: "' + $scope.equipment.id + '"' + ')' + '}' }; } authService.getJWTAuth().then(authHeader => { $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve // none console.log(response); },function(error) { // failure console.error(error); } ); }); }; $scope.setEquipmentEmergency = () => { if($scope.equipment.isemergency) { var request = {'query': 'mutation { ' + 'equipmentemergencyattach ( ' + 'company_id: "' + $scope.equipment.company.id + '", ' + 'equipment_id: "' + $scope.equipment.id + '", ' + ')' + '}' }; } else { var request = {'query': 'mutation { ' + 'equipmentemergencydetach ( ' + 'company_id: "' + $scope.equipment.company.id + '", ' + 'equipment_id: "' + $scope.equipment.id + '"' + ')' + '}' }; } authService.getJWTAuth().then(authHeader => { $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve // none },function(error) { // failure console.error(error); } ); }); }; //////////////////////////////////////////////////////////////////////////////////////////////////////////// // Power Lock const equipmentPowerLockRequest = () => { console.log('power_lock: ' + $scope.equipment.power_lock); var request = {'query': 'mutation { ' + 'lightpowerlock ( ' + 'light_id: "' + $scope.equipment.id + '", ' + 'powerlock: ' + (parseInt($scope.equipment.power_lock) >= 100?null:parseInt($scope.equipment.power_lock)) + ')' + '}' }; console.log(request); authService.getJWTAuth().then(authHeader => { $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve // none },function(error) { // failure console.error(error); } ); }); }; $scope.powerlock_debouncer = debounce(equipmentPowerLockRequest, 500); $scope.setEquipmentPowerLock = () => { if($scope.equipment.power_lock >= 100) { $scope.equipment.power_lock_display = 'Disabled'; } else { $scope.equipment.power_lock_display = $scope.equipment.power_lock+'%'; } $scope.powerlock_debouncer(); }; //////////////////////////////////////////////////////////////////////////////////////////////////////////// // Schedules $scope.showSchedules = ($event) => { console.log('showSchedules'); var config = { attachTo: angular.element(document.body), controller: ['mdPanelRef', '$scope', function(mdPanelRef, $scope) { this.closeMenu = function() { mdPanelRef && mdPanelRef.close(); }; this.selectSchedule = function(scene_id){ mdPanelRef && mdPanelRef.close(); $location.path('/system/schedules/view/' + schedule_id); }; }], controllerAs: 'ctrl', targetEvent: $event, openFrom: $event, templateUrl: 'panel.equipmentschedules.html', clickOutsideToClose: true, escapeToClose: true, position: $mdPanel.newPanelPosition() .relativeTo($event.target) .addPanelPosition( $mdPanel.xPosition.OFFSET_END, $mdPanel.yPosition.ABOVE ), locals: { items: $scope.equipment.schedules }, }; if($scope.equipment.schedules.length) { $mdPanel.open(config); } }; //////////////////////////////////////////////////////////////////////////////////////////////////////////// // Groups $scope.showGroups = ($event) => { console.log('showGroups'); var config = { attachTo: angular.element(document.body), controller: ['mdPanelRef', '$scope', function(mdPanelRef, $scope) { this.closeMenu = function() { mdPanelRef && mdPanelRef.close(); }; this.selectGroup = function(group_id){ mdPanelRef && mdPanelRef.close(); $location.path('/system/groups/view/' + group_id); }; }], controllerAs: 'ctrl', targetEvent: $event, openFrom: $event, templateUrl: 'panel.equipmentgroupslist.html', clickOutsideToClose: true, escapeToClose: true, position: $mdPanel.newPanelPosition() .relativeTo($event.target) .addPanelPosition( $mdPanel.xPosition.OFFSET_END, $mdPanel.yPosition.ABOVE ), locals: { items: $scope.equipment.groups }, }; if($scope.equipment.groups.length) { $mdPanel.open(config); } }; //////////////////////////////////////////////////////////////////////////////////////////////////////////// // Light Scenes $scope.showScenes = ($event) => { console.log('showScenes'); var config = { attachTo: angular.element(document.body), controller: ['mdPanelRef', '$scope', function(mdPanelRef, $scope) { this.closeMenu = function() { mdPanelRef && mdPanelRef.close(); }; this.selectScene = function(scene_id){ mdPanelRef && mdPanelRef.close(); $location.path('/system/scenes/view/' + scene_id); }; }], controllerAs: 'ctrl', targetEvent: $event, openFrom: $event, templateUrl: 'panel.equipmentlightsceneslist.html', clickOutsideToClose: true, escapeToClose: true, position: $mdPanel.newPanelPosition() .relativeTo($event.target) .addPanelPosition( $mdPanel.xPosition.OFFSET_END, $mdPanel.yPosition.ABOVE ), locals: { items: $scope.equipment.scenes }, }; if($scope.equipment.scenes.length) { $mdPanel.open(config); } }; //////////////////////////////////////////////////////////////////////////////////////////////////////////// // Light Settings $scope.setlightScene = (scene_id) => { let scene = $filter('filter')(scenes, {id: scene_id})[0]; $scope.settings.id = scene.id; $scope.settings.name = scene.name; $scope.settings.icon = scene.icon; $scope.settings.state = scene.state; if(scene.speed) { $scope.settings.speed_show = true; $scope.settings.speed = $scope.settings.speed?$scope.settings.speed:scene.speed; } else { $scope.settings.speed_show = false; } if(scene.brightness) { $scope.settings.brightness_show = true; $scope.settings.brightness = $scope.settings.brightness?$scope.settings.brightness:scene.brightness; } else { $scope.settings.brightness_show = false; } if(scene.temperature) { $scope.settings.temperature_show = true; $scope.settings.temperature = $scope.settings.temperature?$scope.settings.temperature:scene.temperature; } else { $scope.settings.temperature_show = false; } if(scene.color) { $scope.settings.color_show = true; $scope.settings.color = $scope.settings.color?$scope.settings.color:scene.color; farbtastic.setColor($scope.settings.color); } else { $scope.settings.color_show = false; } $scope.changeSettings(); }; $scope.changeStateSpeed = () => { //$scope.settings.speed = $scope.equipment.state.speed; $scope.changeSettings(); }; $scope.changeStateBrightness = () => { //$scope.settings.brightness = $scope.equipment.state.brightness; $scope.changeSettings(); }; $scope.changeStateTemperature = () => { //$scope.settings.temperature = $scope.equipment.state.temperature; $scope.changeSettings(); }; $scope.changeSettings = () => { $rootScope.light_settings_changed = true; }; $scope.$on('LightChangeSettingsApply', (payload) => { applyChangeSettings(); }); $scope.$on('LightChangeSettingsCancel', (payload) => { cancelChangeSettings(); }); var cancelChangeSettings = () => { if(equipment_state_backup.state) { // if turned on let scene_id = null; if(!equipment_state_backup.scene && equipment_state_backup.temperature) { scene_id= 34; // Custom White } else if(!equipment_state_backup.scene && equipment_state_backup.r) { scene_id = 33; // Custom Color } else { scene_id = equipment_state_backup.scene; } equipment_state_backup.scene = scene_id; $scope.settings = angular.copy($filter('filter')(scenes, {id: scene_id})[0]); if($scope.settings.speed) { $scope.settings.speed_show = true; $scope.settings.speed = equipment_state_backup.speed; } else { $scope.settings.speed_show = false; } if($scope.settings.brightness) { $scope.settings.brightness_show = true; $scope.settings.brightness = equipment_state_backup.brightness; } else { $scope.settings.brightness_show = false; } if($scope.settings.temperature) { $scope.settings.temperature_show = true; $scope.settings.temperature = equipment_state_backup.temperature; } else { $scope.settings.temperature_show = false; } if($scope.settings.color) { $scope.settings.color_show = true; $scope.settings.r = equipment_state_backup.r; $scope.settings.g = equipment_state_backup.g; $scope.settings.b = equipment_state_backup.b; $scope.settings.c = equipment_state_backup.c; $scope.settings.w = equipment_state_backup.w; if($scope.settings.r) { // ... or any other channels $scope.settings.color = $scope.settings.r.toString(16) + $scope.settings.g.toString(16) + $scope.settings.b.toString(16); farbtastic.setColor($scope.settings.color); } } else { $scope.settings.color_show = false; } } else { $scope.settings = angular.copy($filter('filter')(scenes, {id: 0})[0]); } $rootScope.light_settings_changed = false; }; $rootScope.request_ongoing = false; var applyChangeSettings = () => { console.log($scope.settings); if($scope.settings.color_show) { $scope.settings.r = parseInt($scope.settings.color.substring(0, 2), 16); $scope.settings.g = parseInt($scope.settings.color.substring(2, 4), 16); $scope.settings.b = parseInt($scope.settings.color.substring(4, 6), 16); $scope.settings.c = 0; $scope.settings.w = 0; } authService.getJWTAuth().then(authHeader => { var request = {'query': 'mutation { ' + 'equipmentlightchange ( ' + 'company_id: "' + $scope.equipment.company.id + '", ' + 'equipment_id: "' + $scope.equipment.id + '", ' + 'state: ' + $scope.settings.state + ', ' + (($scope.settings.id <= 32) ? ('scene: ' + $scope.settings.id + ', ') : '') + ($scope.settings.brightness_show ? ('brightness: ' + wiz.dimm2wiz($scope.settings.brightness) + ', ') : '') + ($scope.settings.color_show ? ('r: ' + $scope.settings.r + ', ') : '') + ($scope.settings.color_show ? ('g: ' + $scope.settings.g + ', ') : '') + ($scope.settings.color_show ? ('b: ' + $scope.settings.b + ', ') : '') + ($scope.settings.color_show ? ('cw: ' + $scope.settings.c + ', ') : '') + ($scope.settings.color_show ? ('ww: ' + $scope.settings.w + ', ') : '') + ($scope.settings.speed_show ? ('speed: ' + $scope.settings.speed + ', ') : '') + ($scope.settings.temperature_show ? ('temperature: ' + $scope.settings.temperature + ', ') : '') + '){id, created, status}' + '}' }; console.log(request); $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve $rootScope.request_ongoing = true; let request = response.data.data.equipmentlightchange; $scope.equipment.request = request; $scope.equipment.request.received = null; setTimeout(() => { // check status after 1min - if still ongoing then mark as failed and unlock UI if($scope.equipment.request.status === 'new' || $scope.equipment.request.status === 'waiting') { console.log('request failed', $scope.equipment.request); $scope.equipment.request.status = 'failed'; } else { console.log('request status after 1min:', $scope.equipment.request); } $rootScope.request_ongoing = false; }, 60000); },function(error) { // failure console.error(error); } ); }); $rootScope.light_settings_changed = false; }; //////////////////////////////////////////////////////////////////////////////////////////////////////////// // Events $scope.$on('light:state', function(event, payload) { // light state update event if(!$rootScope.request_ongoing&&!$rootScope.light_settings_changed) { if($scope.equipment.id === payload.id) { let now = moment.utc(new Date()).format('X'); var response = moment.utc($scope.equipment.request.received).format('X'); let duration = now - response; // diff in seconds if(duration > 5) { // wait 5 sec till show state change on UI to filter out in-between updates $scope.equipment.state.connected = payload.state.connected; $scope.equipment.state.rssi = payload.state.rssi $scope.equipment.state.state = payload.state.state; $scope.equipment.state.r = payload.state.r; $scope.equipment.state.g = payload.state.g; $scope.equipment.state.b = payload.state.b; $scope.equipment.state.c = payload.state.c; $scope.equipment.state.w = payload.state.w; $scope.equipment.state.temperature = payload.state.temperature; $scope.equipment.state.brightness = payload.state.brightness; $scope.equipment.state.scene = payload.state.scene; setLightSettings(); } } } }); $scope.$on('light:request', function(event, payload) { // light state update event if($scope.equipment.id === payload.id) { if($scope.equipment.request.id === payload.request.request) { if(payload.request.status) { $scope.equipment.request.status = payload.request.status; $scope.equipment.request.received = moment.utc(new Date()).format('X'); console.log('request response:', $scope.equipment.request); if($scope.equipment.request.status !== 'new' && $scope.equipment.request.status !== 'waiting') { $rootScope.request_ongoing = false; } } } } }); //////////////////////////////////////////////////////////////////////////////////////////////////////////// // CHART: POWER CONSUMPTION DATA var chart_consumption = null; // chart object $scope.chart_consumption_loading = true; // turn on and off AJAX loader and show graph $scope.chart_consumption_drilldown = false; // false: graph shows data aggregated to 1d, true: aggregation to 1h $scope.chart_consumption_drilldown_timestamp = null; // if 1h aggregation is shown then this variable holds information about which day $scope.chart_drillup = () => { // exit from drilldown (back to 1d aggregation) chart_load_daily_data(); } const chart_clean = () => { // remove data servies (empty graph) while(chart_consumption.series.length) { chart_consumption.series[0].remove(); } } const chart_load_daily_data = () => { // prepare loading 1d aggregation data to graph $scope.chart_consumption_drilldown = false; // mark drolldown false $scope.chart_consumption_drilldown_timestamp = null; // remove drilldown timestamp chart_clean(); // remove data servies (empty graph) $scope.chart_consumption_title = 'Last ' + $sessionStorage.currentUser.timerange + ' Days Power Consumption'; // set graph title let tickInterval = 86400000; // 3600 * 24 * 1000 = 1 day (tickInterval = 1d in milliseconds) chart_consumption.xAxis.forEach((xaxis, i) => { // set graph X Axis (time:days) display range xaxis.options.tickInterval = tickInterval; xaxis.setExtremes(time.start, time.today, true); }); let time_start = moment.utc(time.start).format('YYYY-MM-DDTHH:mm:ss'); let time_end = moment.utc(time.today).format('YYYY-MM-DDTHH:mm:ss'); chart_add_power_daily(time_start, time_end); // execute data request and series prepare } const chart_load_hourly_data = (timestamp) => { // drilldown: 24h data $scope.chart_consumption_drilldown = true; // mark drolldown true $scope.chart_consumption_drilldown_timestamp = timestamp; // remember for which day we show drilldown chart_clean(); // remove data servies (empty graph) $scope.chart_consumption_title = moment.utc(timestamp).format('ll') + ' Power Consumption'; // set graph title let tickInterval = 3600000; // 3600 * 1000 = 1 hour chart_consumption.xAxis.forEach((xaxis, i) => { // set graph X Axis (time:hours) display range xaxis.options.tickInterval = tickInterval; xaxis.setExtremes(timestamp, (timestamp + (tickInterval * 23)), true); }); let time_start = moment.utc(timestamp).format('YYYY-MM-DDTHH:mm:ss'); let time_end = moment.utc(timestamp + (tickInterval * 24)).format('YYYY-MM-DDTHH:mm:ss'); chart_add_power_hourly(time_start, time_end); // execute data request and series prepare } const chart_add_power_daily = (time_start, time_end) => { let power_series = { name: "Energy Consumption", type: "column", marker: { symbol: 'circle' }, yAxis: 0, zIndex: 1, tooltip: { valueSuffix: 'kWh' }, color: '#51c4d3', data: [] }; authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'power_daily(object:"' + $stateParams.equipment_id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'step ' + 'epoch ' + 'p_avg ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( (response) => { // resolve response.data.data.power_daily.forEach(day => { power_series.data.push({ x: parseInt(day.epoch, 10), y: day.p_avg }); }); chart_consumption.addSeries(power_series); },(error) => { // failure console.error(error); } ); }); } const chart_add_power_hourly = (time_start, time_end) => { let power_series = { name: "Energy Consumption", type: "column", marker: { symbol: 'circle' }, yAxis: 0, zIndex: 1, tooltip: { xDateFormat: '%b %e, %Y %l %p %Z', valueSuffix: 'kWh' }, color: '#51c4d3', data: [], }; authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'power_hourly(object:"' + $stateParams.equipment_id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'step ' + 'epoch ' + 'p_avg ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( (response) => { // resolve response.data.data.power_hourly.forEach(hour => { power_series.data.push({ x: parseInt(hour.epoch, 10), y: hour.p_avg }); }); chart_consumption.addSeries(power_series); },(error) => { // failure console.error(error); } ); }); } //////////////////////////////////////////////////////////////////////////////////////////////////////////// // CHART: POWER CONSUMPTION OBJECT Highcharts.setOptions({ colors: ['#192a56', '#00b894', '#ff4757'], time: { timezone: sysClock.getTimeZone() } }); Highcharts.dateFormats = { 'Z': () => { return moment.tz(sysClock.getTimeZone()).format('Z'); } } const loadChart = () => { return new Promise((resolve, reject) => { new Highcharts.Chart({ chart: { renderTo: 'dashboard_consumption', height: 200, borderWidth: 0 }, time: { useUTC: true }, title: { text: null }, subtitle: { text: null }, exporting: { enabled: false }, credits: { enabled: false }, accessibility: { announceNewData: { enabled: true } }, xAxis: { type: 'datetime', tickInterval: 86400000, // 24 * 3600 * 1000 = 1 day }, yAxis: [{ // Power Consumption labels: { format: '{value}kWh', style: { color: Highcharts.getOptions().colors[0] } }, title: { text: 'Power Consumption', style: { color: Highcharts.getOptions().colors[0] } } }], tooltip: { xDateFormat: '%b %e, %Y', shared: true }, legend: { enabled: false }, plotOptions: { column: { stacking: 'normal', dataLabels: { enabled: false } }, series: { cursor: 'pointer', pointPadding: 0.01, groupPadding: 0.01, borderWidth: 0.01, shadow: false, dataLabels: { enabled: false }, marker: { enabled: false }, point: { events: { click: function() { //console.log('drilldown: ' + this.series.name + ' ['+this.x+'] -> ' + $scope.chart_consumption_drilldown); if(!$scope.chart_consumption_drilldown) { $scope.chart_consumption_drilldown = true; chart_load_hourly_data(this.x); } } } } } }, drilldown: { activeAxisLabelStyle: { cursor: 'default', color: '#3E576F', fontWeight: 'normal', textDecoration: 'none' }, activeDataLabelStyle: { cursor: 'default', color: '#3E576F', fontWeight: 'normal', textDecoration: 'none' } } }, (chart) => { chart_consumption = chart; chart_load_daily_data(); // dafault $scope.chart_consumption_loading = false; resolve(); }); }); } }]) .controller('EquipmentUPSController', ['$rootScope', '$scope', '$http', '$location', '$timeout', '$filter', '$sessionStorage', '$mdPanel', 'timerange', 'modalDialog', 'authService', 'CONFIG', '$stateParams', 'debounce', 'sysClock', function($rootScope, $scope, $http, $location, $timeout, $filter, $sessionStorage, $mdPanel, timerange, modalDialog, authService, CONFIG, $stateParams, debounce, sysClock, wiz){ /////////////////////////////////////////////////////////////////////////////////////// var time = timerange.calc($sessionStorage.currentUser.timerange); $scope.$on('trUpdate', function(event, payload) { // time range update event console.log('trUpdate event: ' + $sessionStorage.currentUser.timerange); time = timerange.calc($sessionStorage.currentUser.timerange); load_data().catch(error => { console.warn(error); }); }); /////////////////////////////////////////////////////////////////////////////////////// $scope.$on('clockUpdate', function(event, payload) { // time range update event console.log('clockUpdate event: ' + sysClock.getTimeZone()); Highcharts.setOptions({ colors: ['#192a56', '#00b894', '#ff4757'], time: { timezone: sysClock.getTimeZone() } }); if($scope.chart_consumption_drilldown) { // if data is shown in hourly graph chart_load_hourly_data($scope.chart_consumption_drilldown_timestamp); } else { // if data is shown in daily graph chart_load_daily_data(); } }); /////////////////////////////////////////////////////////////////////////////////////// function load_data() { return new Promise((resolve, reject) => { let time_start = moment(time.start).format('YYYY-MM-DDTHH:mm:ss'); let time_end = moment(time.today).format('YYYY-MM-DDTHH:mm:ss'); console.log('time_start: ' + time_start); console.log('time_end: ' + time_end); authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'equipment(id:"' + $scope.equipment.id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'id ' + 'name ' + 'status ' + 'company {' + 'id ' + '}' + 'location {' + 'id ' + '}' + 'room {' + 'id ' + '}' + 'type { ' + 'id ' + 'name ' + '} ' + 'brand { ' + 'id ' + 'name ' + '} ' + 'model { ' + 'id ' + 'name ' + '} ' + 'groups { ' + 'id ' + 'name ' + '} ' + 'request { ' + 'id ' + '} ' + '... on EquipmentUPS { ' + 'mac ' + 'ip ' + 'firmware ' + 'serial ' + 'socket { ' + 'id ' + 'name ' + '} ' + 'state { ' + 'updated ' + 'status ' + 'load ' + 'battery_charge ' + 'battery_runtime ' + 'battery_voltage ' + 'input_current ' + 'input_voltage ' + 'input_phases ' + 'input_frequency ' + 'output_current ' + 'output_voltage ' + 'output_phases ' + 'output_frequency ' + 'last_data ' + 'self_test_result ' + 'self_test_time ' + 'report_isrunning ' + '} ' + '} ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve if(response.data.data.equipment.length){ $scope.equipment = response.data.data.equipment[0]; $scope.equipment.isvisible = true; console.log($scope.equipment); if($scope.equipment.socket) { $scope.socket = new Array($scope.equipment.socket); } else { $scope.socket = new Array({id:null, name:'NONE'}); } ////////////////////////////////////////////////// SELF-TEST MOCKUP //////////////////////////////////////////////// $scope.equipment.state.test_possible = false; if($scope.equipment.state.report_isrunning == false && parseInt($scope.equipment.state.battery_charge) == 100 && $scope.equipment.state.status.toLowerCase() == 'ol') { $scope.equipment.state.test_possible = true; } $scope.equipment.state.test_running = false; if($scope.equipment.request.id) { $scope.equipment.state.test_running = true; } ////////////////////////////////////////////////// SELF-TEST MOCKUP //////////////////////////////////////////////// var last = moment.utc($scope.equipment.state.last_data).format('X'); var now = moment.utc(new Date()).format('X'); var duration = now - last; // diff in seconds $scope.equipment.state.outdated = false; if(duration > 3600) { // more than 1 hour $scope.equipment.state.outdated = true; // whenever there is no fresh data available } if(!$scope.equipment.state.status) { $scope.equipment.state.status_display = 'NO DATA'; $scope.equipment.state.status_type = 'nodata'; } else { switch($scope.equipment.state.status.toLowerCase()) { case 'ol': // On Line $scope.equipment.state.status_display = 'ON GRID'; $scope.equipment.state.status_type = 'normal'; break; case 'ol chrg': // On Line - Charging $scope.equipment.state.status_display = 'CHARGING'; $scope.equipment.state.status_type = 'normal'; break; case 'ol dischrg': // On Line - Charging $scope.equipment.state.status_display = 'DISCHARGING'; $scope.equipment.state.status_type = 'warning'; break; case 'ob': // On Battery $scope.equipment.state.status_display = 'ON BATTERY'; $scope.equipment.state.status_type = 'alert'; break; case 'lb': // Low Battery $scope.equipment.state.status_display = 'LOW BATTERY'; $scope.equipment.state.status_type = 'alert'; break; case 'rb': // Replace Battery $scope.equipment.state.status_display = 'REPLACE BATTERY'; $scope.equipment.state.status_type = 'warning'; break; case 'nocomm': // NO COMMUNICATION $scope.equipment.state.status_display = 'OFFLINE'; $scope.equipment.state.status_type = 'alert'; break; default: $scope.equipment.state.status_display = $scope.equipment.state.status; $scope.equipment.state.status_type = 'warning'; } } $scope.isLoading = false; // hide ajax loader loadChart().then(() => { // create chart object resolve(null); }); } else { $scope.isLoading = false; // hide ajax loader } },function(error) { // failure console.error(error); reject(error); } ); }); }); } $scope.loadEquipmentSocket = () => { authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'equipment_index(location_id:"' + $scope.equipment.location.id + '", type_id:7) { ' + 'id ' + 'name ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve $scope.socket = response.data.data.equipment_index; },function(error) { // failure console.error(error); } ); }); }; $scope.selectEquipmentSocket = () => { if($scope.equipment.socket) { var request = {'query': 'mutation { ' + 'equipmentsocketattach ( ' + 'company_id: "' + $scope.equipment.company.id + '", ' + 'equipment_id: "' + $scope.equipment.id + '", ' + 'socket_id: "' + $scope.equipment.socket.id + '" ' + ')' + '}' }; } else { var request = {'query': 'mutation { ' + 'equipmentsocketdetach ( ' + 'company_id: "' + $scope.equipment.company.id + '", ' + 'equipment_id: "' + $scope.equipment.id + '"' + ')' + '}' }; } authService.getJWTAuth().then(authHeader => { $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve console.log(response); },function(error) { // failure console.error(error); } ); }); }; $timeout(function() { load_data().then(() => { // nothing }).catch(error => { console.warn(error); }); }, 100); //////////////////////////////////////////////////////////////////////////////////////////////////////////// // Groups $scope.showGroups = ($event) => { console.log('showGroups'); var config = { attachTo: angular.element(document.body), controller: ['mdPanelRef', '$scope', function(mdPanelRef, $scope) { this.closeMenu = function() { mdPanelRef && mdPanelRef.close(); }; this.selectGroup = function(group_id){ mdPanelRef && mdPanelRef.close(); $location.path('/system/groups/view/' + group_id); }; }], controllerAs: 'ctrl', targetEvent: $event, openFrom: $event, templateUrl: 'panel.equipmentgroupslist.html', clickOutsideToClose: true, escapeToClose: true, position: $mdPanel.newPanelPosition() .relativeTo($event.target) .addPanelPosition( $mdPanel.xPosition.OFFSET_END, $mdPanel.yPosition.ABOVE ), locals: { items: $scope.equipment.groups }, }; if($scope.equipment.groups.length) { $mdPanel.open(config); } }; //////////////////////////////////////////////////////////////////////////////////////////////////////////// // CHART: UPS STATS DATA var chart_consumption = null; // chart object $scope.chart_consumption_loading = true; $scope.chart_consumption_drilldown = false; $scope.chart_consumption_drilldown_timestamp = null; // if 1h aggregation is shown then this variable holds information about which day $scope.chart_drillup = function() { chart_load_daily_data(); } const chart_clean = () => { while(chart_consumption.series.length) { chart_consumption.series[0].remove(); } } const chart_load_daily_data = () => { $scope.chart_consumption_drilldown = false; // mark drolldown false $scope.chart_consumption_drilldown_timestamp = null; // remove drilldown timestamp chart_clean(); // remove data servies (empty graph) $scope.chart_consumption_title = 'Last ' + $sessionStorage.currentUser.timerange + ' days UPS Condition'; let tickInterval = 86400000; // 3600 * 24 * 1000 = 1 day chart_consumption.xAxis.forEach((xaxis, i) => { // set graph X Axis (time:days) display range xaxis.options.tickInterval = tickInterval; xaxis.setExtremes(time.start, time.today, true); }); let time_start = moment.utc(time.start).format('YYYY-MM-DDTHH:mm:ss'); let time_end = moment.utc(time.today).format('YYYY-MM-DDTHH:mm:ss'); chart_add_ups_load_daily(time_start, time_end); chart_add_battery_charge_daily(time_start, time_end); } const chart_load_hourly_data = (timestamp) => { // drilldown: 24h data $scope.chart_consumption_drilldown = true; // mark drolldown true $scope.chart_consumption_drilldown_timestamp = timestamp; // remember for which day we show drilldown chart_clean(); // remove data servies (empty graph) $scope.chart_consumption_title = moment.utc(timestamp).format('ll') + ' UPS Load'; let tickInterval = 3600000; // 3600 * 1000 = 1 hour chart_consumption.xAxis.forEach((xaxis, i) => { // set graph X Axis (time:hours) display range xaxis.options.tickInterval = tickInterval; xaxis.setExtremes(timestamp, (timestamp + (tickInterval * 23)), true); }); let time_start = moment.utc(timestamp).format('YYYY-MM-DDTHH:mm:ss'); let time_end = moment.utc(timestamp + (tickInterval * 24)).format('YYYY-MM-DDTHH:mm:ss'); chart_add_ups_load_hourly(time_start, time_end); chart_add_battery_charge_hourly(time_start, time_end); } const chart_add_ups_load_daily = (time_start, time_end) => { console.log('chart_add_ups_load_daily: ', time_start, time_end); let load_series = { name: "UPS Load", type: "column", marker: { symbol: 'circle' }, yAxis: 0, zIndex: 1, tooltip: { valueSuffix: '%' }, color: '#51c4d3', data: [] }; authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'ups_daily(object:"' + $stateParams.equipment_id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'step ' + 'epoch ' + 'load_avg ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve console.log(response); response.data.data.ups_daily.forEach(day => { load_series.data.push({ x: parseInt(day.epoch, 10), y: day.load_avg }); }); chart_consumption.addSeries(load_series); },function(error) { // failure console.error(error); } ); }); } const chart_add_ups_load_hourly = (time_start, time_end) => { console.log('chart_add_ups_load_hourly: ', time_start, time_end); let load_series = { name: "UPS Load", type: "column", marker: { symbol: 'circle' }, yAxis: 0, zIndex: 1, tooltip: { xDateFormat: '%b %e, %Y %l %p %Z', valueSuffix: '%' }, color: '#51c4d3', data: [], }; authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'ups_hourly(object:"' + $stateParams.equipment_id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'step ' + 'epoch ' + 'load_avg ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve response.data.data.ups_hourly.forEach(day => { load_series.data.push({ x: parseInt(day.epoch, 10), y: day.load_avg }); }); chart_consumption.addSeries(load_series); },function(error) { // failure console.error(error); } ); }); } const chart_add_battery_charge_daily = (time_start, time_end) => { console.log('chart_add_battery_charge_daily: ', time_start, time_end); let charge_series = { name: "Battery Charge", type: "spline", marker: { symbol: 'circle' }, yAxis: 1, zIndex: 2, tooltip: { valueSuffix: '%' }, color: '#ff4757', data: [] }; authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'ups_daily(object:"' + $stateParams.equipment_id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'step ' + 'epoch ' + 'charge_avg ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve console.log(response); response.data.data.ups_daily.forEach(day => { charge_series.data.push({ x: parseInt(day.epoch, 10), y: day.charge_avg }); }); chart_consumption.addSeries(charge_series); },function(error) { // failure console.error(error); } ); }); } const chart_add_battery_charge_hourly = (time_start, time_end) => { console.log('chart_add_battery_charge_hourly: ', time_start, time_end); let charge_series = { name: "Battery Charge", type: "spline", marker: { symbol: 'circle' }, yAxis: 1, zIndex: 2, tooltip: { xDateFormat: '%b %e, %Y %l %p %Z', valueSuffix: '%' }, color: '#ff4757', data: [], }; authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'ups_hourly(object:"' + $stateParams.equipment_id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'step ' + 'epoch ' + 'charge_avg ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve response.data.data.ups_hourly.forEach(day => { charge_series.data.push({ x: parseInt(day.epoch, 10), y: day.charge_avg }); }); chart_consumption.addSeries(charge_series); },function(error) { // failure console.error(error); } ); }); } //////////////////////////////////////////////////////////////////////////////////////////////////////////// // CHART: UPS STATS OBJECT Highcharts.setOptions({ colors: ['#192a56', '#ff4757'], time: { timezone: sysClock.getTimeZone() } }); Highcharts.dateFormats = { 'Z': () => { return moment.tz(sysClock.getTimeZone()).format('Z'); } } function loadChart() { return new Promise((resolve, reject) => { new Highcharts.Chart({ chart: { renderTo: 'dashboard_consumption', height: 200, borderWidth: 0 }, time: { useUTC: true }, title: { text: null }, subtitle: { text: null }, exporting: { enabled: false }, credits: { enabled: false }, accessibility: { announceNewData: { enabled: true } }, xAxis: { type: 'datetime', tickInterval: 86400000, // 24 * 3600 * 1000 = 1 day }, yAxis: [{ // UPS Load labels: { format: '{value}%', style: { color: Highcharts.getOptions().colors[0] } }, title: { text: 'UPS Load', style: { color: Highcharts.getOptions().colors[0] } } },{ // Battery Charge labels: { format: '{value}%', style: { color: Highcharts.getOptions().colors[1] } }, title: { text: 'Battery Charge', style: { color: Highcharts.getOptions().colors[1] } }, opposite: true }], tooltip: { xDateFormat: '%b %e, %Y', shared: true }, legend: { enabled: false }, plotOptions: { column: { stacking: 'normal', dataLabels: { enabled: false } }, series: { cursor: 'pointer', pointPadding: 0.01, groupPadding: 0.01, borderWidth: 0.01, shadow: false, dataLabels: { enabled: false }, marker: { enabled: false }, point: { events: { click: function() { //console.log('drilldown: ' + this.series.name + ' ['+this.x+'] -> ' + $scope.chart_consumption_drilldown); if(!$scope.chart_consumption_drilldown) { $scope.chart_consumption_drilldown = true; chart_load_hourly_data(this.x); } } } } } }, drilldown: { activeAxisLabelStyle: { cursor: 'default', color: '#3E576F', fontWeight: 'normal', textDecoration: 'none' }, activeDataLabelStyle: { cursor: 'default', color: '#3E576F', fontWeight: 'normal', textDecoration: 'none' } } }, function(chart){ chart_consumption = chart; chart_load_daily_data(); // dafault $scope.chart_consumption_loading = false; resolve(); }); }); } }]) .controller('EquipmentInverterController', ['$rootScope', '$scope', '$http', '$location', '$timeout', '$filter', '$sessionStorage', '$mdPanel', 'timerange', 'modalDialog', 'authService', 'CONFIG', '$stateParams', 'debounce', 'sysClock', function($rootScope, $scope, $http, $location, $timeout, $filter, $sessionStorage, $mdPanel, timerange, modalDialog, authService, CONFIG, $stateParams, debounce, sysClock, wiz){ /////////////////////////////////////////////////////////////////////////////////////// var time = timerange.calc($sessionStorage.currentUser.timerange); $scope.$on('trUpdate', function(event, payload) { // time range update event console.log('trUpdate event: ' + $sessionStorage.currentUser.timerange); time = timerange.calc($sessionStorage.currentUser.timerange); load_data().catch(error => { console.warn(error); }); }); /////////////////////////////////////////////////////////////////////////////////////// $scope.$on('clockUpdate', function(event, payload) { // time range update event console.log('clockUpdate event: ' + sysClock.getTimeZone()); Highcharts.setOptions({ colors: ['#192a56', '#00b894', '#ff4757'], time: { timezone: sysClock.getTimeZone() } }); if($scope.chart_consumption_drilldown) { // if data is shown in hourly graph chart_load_hourly_data($scope.chart_consumption_drilldown_timestamp); } else { // if data is shown in daily graph chart_load_daily_data(); } }); /////////////////////////////////////////////////////////////////////////////////////// function load_data() { return new Promise((resolve, reject) => { let time_start = moment(time.start).format('YYYY-MM-DDTHH:mm:ss'); let time_end = moment(time.today).format('YYYY-MM-DDTHH:mm:ss'); console.log('time_start: ' + time_start); console.log('time_end: ' + time_end); authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'equipment(id:"' + $scope.equipment.id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'id ' + 'name ' + 'status ' + 'company {' + 'id ' + '}' + 'location {' + 'id ' + '}' + 'room {' + 'id ' + '}' + 'type { ' + 'id ' + 'name ' + '} ' + 'brand { ' + 'id ' + 'name ' + '} ' + 'model { ' + 'id ' + 'name ' + '} ' + 'groups { ' + 'id ' + 'name ' + '} ' + '... on EquipmentInverter { ' + 'mac ' + 'ip ' + 'firmware ' + 'state { ' + 'updated ' + 'status ' + 'load ' + 'battery_charge ' + 'battery_runtime ' + 'battery_voltage ' + 'input_current ' + 'input_voltage ' + 'input_phases ' + 'input_frequency ' + 'output_current ' + 'output_voltage ' + 'output_phases ' + 'output_frequency ' + '} ' + '} ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve if(response.data.data.equipment.length){ $scope.equipment = response.data.data.equipment[0]; $scope.equipment.isvisible = true; console.log($scope.equipment); if(!$scope.equipment.state.status) { $scope.equipment.state.status_display = 'NO DATA'; $scope.equipment.state.status_type = 'nodata'; } else { switch($scope.equipment.state.status.toLowerCase()) { case 'ol': // On Line $scope.equipment.state.status_display = 'ON GRID'; $scope.equipment.state.status_type = 'normal'; break; case 'ob': // On Battery $scope.equipment.state.status_display = 'ON BATTERY'; $scope.equipment.state.status_type = 'alert'; break; case 'lb': // Low Battery $scope.equipment.state.status_display = 'LOW BATTERY'; $scope.equipment.state.status_type = 'alert'; break; case 'rb': // Replace Battery $scope.equipment.state.status_display = 'REPLACE BATTERY'; $scope.equipment.state.status_type = 'warning'; break; default: $scope.equipment.state.status_display = $scope.equipment.state.status; $scope.equipment.state.status_type = 'warning'; } } $scope.isLoading = false; // hide ajax loader loadChart().then(() => { // create chart object resolve(null); }); } else { $scope.isLoading = false; // hide ajax loader } },function(error) { // failure console.error(error); reject(error); } ); }); }); } $timeout(function() { load_data().then(() => { // nothing }).catch(error => { console.warn(error); }); }, 100); //////////////////////////////////////////////////////////////////////////////////////////////////////////// // Groups $scope.showGroups = ($event) => { console.log('showGroups'); var config = { attachTo: angular.element(document.body), controller: ['mdPanelRef', '$scope', function(mdPanelRef, $scope) { this.closeMenu = function() { mdPanelRef && mdPanelRef.close(); }; this.selectGroup = function(group_id){ mdPanelRef && mdPanelRef.close(); $location.path('/system/groups/view/' + group_id); }; }], controllerAs: 'ctrl', targetEvent: $event, openFrom: $event, templateUrl: 'panel.equipmentgroupslist.html', clickOutsideToClose: true, escapeToClose: true, position: $mdPanel.newPanelPosition() .relativeTo($event.target) .addPanelPosition( $mdPanel.xPosition.OFFSET_END, $mdPanel.yPosition.ABOVE ), locals: { items: $scope.equipment.groups }, }; if($scope.equipment.groups.length) { $mdPanel.open(config); } }; //////////////////////////////////////////////////////////////////////////////////////////////////////////// // CHART: Inverter STATS DATA var chart_consumption = null; // chart object $scope.chart_consumption_loading = true; $scope.chart_consumption_drilldown = false; $scope.chart_consumption_drilldown_timestamp = null; // if 1h aggregation is shown then this variable holds information about which day $scope.chart_drillup = function() { chart_load_daily_data(); } const chart_clean = () => { while(chart_consumption.series.length) { chart_consumption.series[0].remove(); } } const chart_load_daily_data = () => { $scope.chart_consumption_drilldown = false; // mark drolldown false $scope.chart_consumption_drilldown_timestamp = null; // remove drilldown timestamp chart_clean(); // remove data servies (empty graph) $scope.chart_consumption_title = 'Last ' + $sessionStorage.currentUser.timerange + ' days Inverter Condition'; let tickInterval = 86400000; // 3600 * 24 * 1000 = 1 day chart_consumption.xAxis.forEach((xaxis, i) => { // set graph X Axis (time:days) display range xaxis.options.tickInterval = tickInterval; xaxis.setExtremes(time.start, time.today, true); }); let time_start = moment.utc(time.start).format('YYYY-MM-DDTHH:mm:ss'); let time_end = moment.utc(time.today).format('YYYY-MM-DDTHH:mm:ss'); chart_add_ups_load_daily(time_start, time_end); chart_add_battery_charge_daily(time_start, time_end); } const chart_load_hourly_data = (timestamp) => { // drilldown: 24h data $scope.chart_consumption_drilldown = true; // mark drolldown true $scope.chart_consumption_drilldown_timestamp = timestamp; // remember for which day we show drilldown chart_clean(); // remove data servies (empty graph) $scope.chart_consumption_title = moment.utc(timestamp).format('ll') + ' Inverter Load'; let tickInterval = 3600000; // 3600 * 1000 = 1 hour chart_consumption.xAxis.forEach((xaxis, i) => { // set graph X Axis (time:hours) display range xaxis.options.tickInterval = tickInterval; xaxis.setExtremes(timestamp, (timestamp + (tickInterval * 23)), true); }); let time_start = moment.utc(timestamp).format('YYYY-MM-DDTHH:mm:ss'); let time_end = moment.utc(timestamp + (tickInterval * 24)).format('YYYY-MM-DDTHH:mm:ss'); chart_add_ups_load_hourly(time_start, time_end); chart_add_battery_charge_hourly(time_start, time_end); } const chart_add_ups_load_daily = (time_start, time_end) => { console.log('chart_add_ups_load_daily: ', time_start, time_end); let load_series = { name: "Inverter Load", type: "column", marker: { symbol: 'circle' }, yAxis: 0, zIndex: 1, tooltip: { valueSuffix: '%' }, color: '#51c4d3', data: [] }; authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'ups_daily(object:"' + $stateParams.equipment_id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'step ' + 'epoch ' + 'load_avg ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve response.data.data.ups_daily.forEach(day => { load_series.data.push({ x: parseInt(day.epoch, 10), y: day.load_avg }); }); chart_consumption.addSeries(load_series); },function(error) { // failure console.error(error); } ); }); } const chart_add_ups_load_hourly = (time_start, time_end) => { console.log('chart_add_ups_load_hourly: ', time_start, time_end); let load_series = { name: "Inverter Load", type: "column", marker: { symbol: 'circle' }, yAxis: 0, zIndex: 1, tooltip: { xDateFormat: '%b %e, %Y %l %p %Z', valueSuffix: '%' }, color: '#51c4d3', data: [], }; authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'ups_hourly(object:"' + $stateParams.equipment_id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'step ' + 'epoch ' + 'load_avg ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve response.data.data.ups_hourly.forEach(day => { load_series.data.push({ x: parseInt(day.epoch, 10), y: day.load_avg }); }); chart_consumption.addSeries(load_series); },function(error) { // failure console.error(error); } ); }); } const chart_add_battery_charge_daily = (time_start, time_end) => { console.log('chart_add_battery_charge_daily: ', time_start, time_end); let charge_series = { name: "Battery Charge", type: "spline", marker: { symbol: 'circle' }, yAxis: 1, zIndex: 2, tooltip: { valueSuffix: '%' }, color: '#ff4757', data: [] }; authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'ups_daily(object:"' + $stateParams.equipment_id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'step ' + 'epoch ' + 'charge_avg ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve response.data.data.ups_daily.forEach(day => { charge_series.data.push({ x: parseInt(day.epoch, 10), y: day.charge_avg }); }); chart_consumption.addSeries(charge_series); },function(error) { // failure console.error(error); } ); }); } const chart_add_battery_charge_hourly = (time_start, time_end) => { console.log('chart_add_battery_charge_hourly: ', time_start, time_end); let charge_series = { name: "Battery Charge", type: "spline", marker: { symbol: 'circle' }, yAxis: 1, zIndex: 2, tooltip: { xDateFormat: '%b %e, %Y %l %p %Z', valueSuffix: '%' }, color: '#ff4757', data: [], }; authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'ups_hourly(object:"' + $stateParams.equipment_id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'step ' + 'epoch ' + 'charge_avg ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve response.data.data.ups_hourly.forEach(day => { charge_series.data.push({ x: parseInt(day.epoch, 10), y: day.charge_avg }); }); chart_consumption.addSeries(charge_series); },function(error) { // failure console.error(error); } ); }); } //////////////////////////////////////////////////////////////////////////////////////////////////////////// // CHART: Inverter STATS OBJECT Highcharts.setOptions({ colors: ['#192a56', '#ff4757'], time: { timezone: sysClock.getTimeZone() } }); Highcharts.dateFormats = { 'Z': () => { return moment.tz(sysClock.getTimeZone()).format('Z'); } } function loadChart() { return new Promise((resolve, reject) => { new Highcharts.Chart({ chart: { renderTo: 'dashboard_consumption', height: 200, borderWidth: 0 }, time: { useUTC: true }, title: { text: null }, subtitle: { text: null }, exporting: { enabled: false }, credits: { enabled: false }, accessibility: { announceNewData: { enabled: true } }, xAxis: { type: 'datetime', tickInterval: 86400000, // 24 * 3600 * 1000 = 1 day }, yAxis: [{ // Inverter Load labels: { format: '{value}%', style: { color: Highcharts.getOptions().colors[0] } }, title: { text: 'Inverter Load', style: { color: Highcharts.getOptions().colors[0] } } },{ // Battery Charge labels: { format: '{value}%', style: { color: Highcharts.getOptions().colors[1] } }, title: { text: 'Battery Charge', style: { color: Highcharts.getOptions().colors[1] } }, opposite: true }], tooltip: { xDateFormat: '%b %e, %Y', shared: true }, legend: { enabled: false }, plotOptions: { column: { stacking: 'normal', dataLabels: { enabled: false } }, series: { cursor: 'pointer', pointPadding: 0.01, groupPadding: 0.01, borderWidth: 0.01, shadow: false, dataLabels: { enabled: false }, marker: { enabled: false }, point: { events: { click: function() { //console.log('drilldown: ' + this.series.name + ' ['+this.x+'] -> ' + $scope.chart_consumption_drilldown); if(!$scope.chart_consumption_drilldown) { $scope.chart_consumption_drilldown = true; chart_load_hourly_data(this.x); } } } } } }, drilldown: { activeAxisLabelStyle: { cursor: 'default', color: '#3E576F', fontWeight: 'normal', textDecoration: 'none' }, activeDataLabelStyle: { cursor: 'default', color: '#3E576F', fontWeight: 'normal', textDecoration: 'none' } } }, function(chart){ chart_consumption = chart; chart_load_daily_data(); // dafault $scope.chart_consumption_loading = false; resolve(); }); }); } }]) .controller('EquipmentOOCController', ['$rootScope', '$scope', '$http', '$location', '$timeout', '$filter', '$sessionStorage', '$mdPanel', 'timerange', 'modalDialog', 'authService', 'CONFIG', '$stateParams', 'debounce', 'sysClock', function($rootScope, $scope, $http, $location, $timeout, $filter, $sessionStorage, $mdPanel, timerange, modalDialog, authService, CONFIG, $stateParams, debounce, sysClock, wiz){ /////////////////////////////////////////////////////////////////////////////////////// var time = timerange.calc($sessionStorage.currentUser.timerange); $scope.$on('trUpdate', function(event, payload) { // time range update event console.log('trUpdate event: ' + $sessionStorage.currentUser.timerange); time = timerange.calc($sessionStorage.currentUser.timerange); load_data().catch(error => { console.warn(error); }); }); /////////////////////////////////////////////////////////////////////////////////////// $scope.$on('clockUpdate', function(event, payload) { // time range update event console.log('clockUpdate event: ' + sysClock.getTimeZone()); Highcharts.setOptions({ colors: ['#192a56', '#00b894', '#ff4757'], time: { timezone: sysClock.getTimeZone() } }); if($scope.chart_consumption_drilldown) { // if data is shown in hourly graph chart_load_hourly_data($scope.chart_consumption_drilldown_timestamp); } else { // if data is shown in daily graph chart_load_daily_data(); } }); /////////////////////////////////////////////////////////////////////////////////////// function load_data() { return new Promise((resolve, reject) => { let time_start = moment(time.start).format('YYYY-MM-DDTHH:mm:ss'); let time_end = moment(time.today).format('YYYY-MM-DDTHH:mm:ss'); console.log('time_start: ' + time_start); console.log('time_end: ' + time_end); authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'equipment(id:"' + $scope.equipment.id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'id ' + 'name ' + 'status ' + 'company {' + 'id ' + '}' + 'location {' + 'id ' + '}' + 'room {' + 'id ' + '}' + 'type { ' + 'id ' + 'name ' + '} ' + 'brand { ' + 'id ' + 'name ' + '} ' + 'model { ' + 'id ' + 'name ' + '} ' + 'groups { ' + 'id ' + 'name ' + '} ' + '... on EquipmentOOC { ' + 'mac ' + 'ip ' + 'firmware ' + 'state { ' + 'updated ' + 'status ' + '} ' + '} ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve if(response.data.data.equipment.length){ $scope.equipment = response.data.data.equipment[0]; $scope.equipment.isvisible = true; if(!$scope.equipment.state.status) { $scope.equipment.state.status_display = 'NO DATA'; $scope.equipment.state.status_type = 'nodata'; } else { switch($scope.equipment.state.status.toLowerCase()) { case 'online': // On Line $scope.equipment.state.status_display = 'ONLINE'; $scope.equipment.state.status_type = 'normal'; break; case 'reboot': // On Battery $scope.equipment.state.status_display = 'REBOOT'; $scope.equipment.state.status_type = 'warning'; break; case 'offline': // Low Battery $scope.equipment.state.status_display = 'OFFLINE'; $scope.equipment.state.status_type = 'alert'; break; default: $scope.equipment.state.status_display = $scope.equipment.state.status; $scope.equipment.state.status_type = 'warning'; } } $scope.devices = [ {name:'PoE Light', count:850, messages:347583, requests:87}, {name:'UPS', count:19, messages:3455, requests:0} ] console.log($scope.equipment); $scope.isLoading = false; // hide ajax loader loadCPUChart().then(() => { /***/ }); // OOC CPU Chart loadMEMChart().then(() => { /***/ }); // OOC Memory Chart loadHDDChart().then(() => { /***/ }); // OOC Hard Drive Chart //loadMSGChart().then(() => { /***/ }); // OOC Message Chart } else { $scope.isLoading = false; // hide ajax loader } },function(error) { // failure console.error(error); reject(error); } ); }); }); } $timeout(function() { load_data().then(() => { // nothing }).catch(error => { console.warn(error); }); }, 100); //////////////////////////////////////////////////////////////////////////////////////////////////////////// // Groups $scope.showGroups = ($event) => { console.log('showGroups'); var config = { attachTo: angular.element(document.body), controller: ['mdPanelRef', '$scope', function(mdPanelRef, $scope) { this.closeMenu = function() { mdPanelRef && mdPanelRef.close(); }; this.selectGroup = function(group_id){ mdPanelRef && mdPanelRef.close(); $location.path('/system/groups/view/' + group_id); }; }], controllerAs: 'ctrl', targetEvent: $event, openFrom: $event, templateUrl: 'panel.equipmentgroupslist.html', clickOutsideToClose: true, escapeToClose: true, position: $mdPanel.newPanelPosition() .relativeTo($event.target) .addPanelPosition( $mdPanel.xPosition.OFFSET_END, $mdPanel.yPosition.ABOVE ), locals: { items: $scope.equipment.groups }, }; if($scope.equipment.groups.length) { $mdPanel.open(config); } }; //////////////////////////////////////////////////////////////////////////////////////////////////////////// // CHART: CPU STATS DATA var chart_cpu = null; // chart object $scope.chart_cpu_loading = true; $scope.chart_cpu_drilldown = false; $scope.chart_cpu_drilldown_timestamp = null; // if 1h aggregation is shown then this variable holds information about which day $scope.chart_cpu_drillup = function() { chart_load_cpu_daily_data(); } const chart_cpu_clean = () => { while(chart_cpu.series.length) { chart_cpu.series[0].remove(); } } const chart_load_cpu_daily_data = () => { console.log('chart_load_cpu_daily_data'); $scope.chart_cpu_drilldown = false; // mark drolldown false $scope.chart_cpu_drilldown_timestamp = null; // remove drilldown timestamp chart_cpu_clean(); // remove data servies (empty graph) $scope.chart_cpu_title = 'Last ' + $sessionStorage.currentUser.timerange + ' days CPU Load'; let tickInterval = 86400000; // 3600 * 24 * 1000 = 1 day chart_cpu.xAxis.forEach((xaxis, i) => { // set graph X Axis (time:days) display range xaxis.options.tickInterval = tickInterval; xaxis.setExtremes(time.start, time.today, true); }); let time_start = moment.utc(time.start).format('YYYY-MM-DDTHH:mm:ss'); let time_end = moment.utc(time.today).format('YYYY-MM-DDTHH:mm:ss'); chart_add_cpu_load_daily(time_start, time_end); } const chart_load_cpu_hourly_data = (timestamp) => { // drilldown: 24h data $scope.chart_cpu_drilldown = true; // mark drolldown true $scope.chart_cpu_drilldown_timestamp = timestamp; // remember for which day we show drilldown chart_clean(); // remove data servies (empty graph) $scope.chart_cpu_title = moment.utc(timestamp).format('ll') + ' CPU Load'; let tickInterval = 3600000; // 3600 * 1000 = 1 hour chart_cpu.xAxis.forEach((xaxis, i) => { // set graph X Axis (time:hours) display range xaxis.options.tickInterval = tickInterval; xaxis.setExtremes(timestamp, (timestamp + (tickInterval * 23)), true); }); let time_start = moment.utc(timestamp).format('YYYY-MM-DDTHH:mm:ss'); let time_end = moment.utc(timestamp + (tickInterval * 24)).format('YYYY-MM-DDTHH:mm:ss'); //chart_add_cpu_load_hourly(time_start, time_end); } const chart_add_cpu_load_daily = (time_start, time_end) => { console.log('chart_add_cpu_load_daily: ', time_start, time_end); let load_series = { name: "CPU Load", //type: "column", type: "spline", marker: { symbol: 'circle' }, yAxis: 0, zIndex: 1, tooltip: { valueSuffix: '%' }, color: '#51c4d3', data: [] }; authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'cpu_daily(object:"' + $stateParams.equipment_id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'step ' + 'epoch ' + 'load_avg ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve response.data.data.cpu_daily.forEach(day => { load_series.data.push({ x: parseInt(day.epoch, 10), y: day.load_avg }); }); chart_cpu.addSeries(load_series); },function(error) { // failure console.error(error); } ); }); } const chart_add_cpu_load_hourly = (time_start, time_end) => { console.log('chart_add_cpu_load_hourly: ', time_start, time_end); let load_series = { name: "CPU Load", type: "column", marker: { symbol: 'circle' }, yAxis: 0, zIndex: 1, tooltip: { xDateFormat: '%b %e, %Y %l %p %Z', valueSuffix: '%' }, color: '#51c4d3', data: [], }; authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'cpu_hourly(object:"' + $stateParams.equipment_id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'step ' + 'epoch ' + 'load_avg ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve response.data.data.cpu_hourly.forEach(day => { load_series.data.push({ x: parseInt(day.epoch, 10), y: day.load_avg }); }); chart_cpu.addSeries(load_series); },function(error) { // failure console.error(error); } ); }); } //////////////////////////////////////////////////////////////////////////////////////////////////////////// // CHART: MEM STATS DATA var chart_mem = null; // chart object $scope.chart_mem_loading = true; $scope.chart_mem_drilldown = false; $scope.chart_mem_drilldown_timestamp = null; // if 1h aggregation is shown then this variable holds information about which day $scope.chart_mem_drillup = function() { chart_load_mem_daily_data(); } const chart_mem_clean = () => { while(chart_mem.series.length) { chart_mem.series[0].remove(); } } const chart_load_mem_daily_data = () => { console.log('chart_load_mem_daily_data'); $scope.chart_mem_drilldown = false; // mark drolldown false $scope.chart_mem_drilldown_timestamp = null; // remove drilldown timestamp chart_mem_clean(); // remove data servies (empty graph) $scope.chart_mem_title = 'Last ' + $sessionStorage.currentUser.timerange + ' days Memory Allocation (GB)'; let tickInterval = 86400000; // 3600 * 24 * 1000 = 1 day chart_mem.xAxis.forEach((xaxis, i) => { // set graph X Axis (time:days) display range xaxis.options.tickInterval = tickInterval; xaxis.setExtremes(time.start, time.today, true); }); let time_start = moment.utc(time.start).format('YYYY-MM-DDTHH:mm:ss'); let time_end = moment.utc(time.today).format('YYYY-MM-DDTHH:mm:ss'); chart_add_mem_load_daily(time_start, time_end); } const chart_load_mem_hourly_data = (timestamp) => { // drilldown: 24h data $scope.chart_mem_drilldown = true; // mark drolldown true $scope.chart_mem_drilldown_timestamp = timestamp; // remember for which day we show drilldown chart_clean(); // remove data servies (empty graph) $scope.chart_mem_title = moment.utc(timestamp).format('ll') + ' Memory Allocation (MB)'; let tickInterval = 3600000; // 3600 * 1000 = 1 hour chart_mem.xAxis.forEach((xaxis, i) => { // set graph X Axis (time:hours) display range xaxis.options.tickInterval = tickInterval; xaxis.setExtremes(timestamp, (timestamp + (tickInterval * 23)), true); }); let time_start = moment.utc(timestamp).format('YYYY-MM-DDTHH:mm:ss'); let time_end = moment.utc(timestamp + (tickInterval * 24)).format('YYYY-MM-DDTHH:mm:ss'); //chart_add_mem_load_hourly(time_start, time_end); } const chart_add_mem_load_daily = (time_start, time_end) => { console.log('chart_add_mem_load_daily: ', time_start, time_end); let size_series = { name: "Memory Size", type: "spline", marker: { symbol: 'circle' }, yAxis: 0, zIndex: 1, tooltip: { valueSuffix: 'GB' }, color: '#51c4d3', data: [] }; let used_series = { name: "Memory Allocation", type: "spline", marker: { symbol: 'circle' }, yAxis: 0, zIndex: 1, tooltip: { valueSuffix: 'GB' }, color: '#ff4757', data: [] }; authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'mem_daily(object:"' + $stateParams.equipment_id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'step ' + 'epoch ' + 'mem_size ' + 'mem_used ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve response.data.data.mem_daily.forEach(day => { size_series.data.push({ x: parseInt(day.epoch, 10), y: parseFloat((parseInt(day.mem_size)/1024/1024/1024).toFixed(2)) }); used_series.data.push({ x: parseInt(day.epoch, 10), y: parseFloat((parseInt(day.mem_used)/1024/1024/1024).toFixed(2)) }); }); chart_mem.addSeries(size_series); chart_mem.addSeries(used_series); },function(error) { // failure console.error(error); } ); }); } const chart_add_mem_load_hourly = (time_start, time_end) => { console.log('chart_add_mem_load_hourly: ', time_start, time_end); let load_series = { name: "Memory Allocation", type: "column", marker: { symbol: 'circle' }, yAxis: 0, zIndex: 1, tooltip: { xDateFormat: '%b %e, %Y %l %p %Z', valueSuffix: '%' }, color: '#51c4d3', data: [], }; authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'mem_hourly(object:"' + $stateParams.equipment_id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'step ' + 'epoch ' + 'load_avg ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve response.data.data.cpu_hourly.forEach(day => { load_series.data.push({ x: parseInt(day.epoch, 10), y: day.load_avg }); }); chart_mem.addSeries(load_series); },function(error) { // failure console.error(error); } ); }); } //////////////////////////////////////////////////////////////////////////////////////////////////////////// // CHART: HDD STATS DATA var chart_hdd = null; // chart object $scope.chart_hdd_loading = true; $scope.chart_hdd_drilldown = false; $scope.chart_hdd_drilldown_timestamp = null; // if 1h aggregation is shown then this variable holds information about which day $scope.chart_hdd_drillup = function() { chart_load_hdd_daily_data(); } const chart_hdd_clean = () => { while(chart_hdd.series.length) { chart_hdd.series[0].remove(); } } const chart_load_hdd_daily_data = () => { console.log('chart_load_hdd_daily_data'); $scope.chart_hdd_drilldown = false; // mark drolldown false $scope.chart_hdd_drilldown_timestamp = null; // remove drilldown timestamp chart_hdd_clean(); // remove data servies (empty graph) $scope.chart_hdd_title = 'Last ' + $sessionStorage.currentUser.timerange + ' days Hard Drive Utilization (GB)'; let tickInterval = 86400000; // 3600 * 24 * 1000 = 1 day chart_hdd.xAxis.forEach((xaxis, i) => { // set graph X Axis (time:days) display range xaxis.options.tickInterval = tickInterval; xaxis.setExtremes(time.start, time.today, true); }); let time_start = moment.utc(time.start).format('YYYY-MM-DDTHH:mm:ss'); let time_end = moment.utc(time.today).format('YYYY-MM-DDTHH:mm:ss'); chart_add_hdd_load_daily(time_start, time_end); } const chart_load_hdd_hourly_data = (timestamp) => { // drilldown: 24h data $scope.chart_hdd_drilldown = true; // mark drolldown true $scope.chart_hdd_drilldown_timestamp = timestamp; // remember for which day we show drilldown chart_clean(); // remove data servies (empty graph) $scope.chart_hdd_title = moment.utc(timestamp).format('ll') + ' Hard Drive Utilization'; let tickInterval = 3600000; // 3600 * 1000 = 1 hour chart_hdd.xAxis.forEach((xaxis, i) => { // set graph X Axis (time:hours) display range xaxis.options.tickInterval = tickInterval; xaxis.setExtremes(timestamp, (timestamp + (tickInterval * 23)), true); }); let time_start = moment.utc(timestamp).format('YYYY-MM-DDTHH:mm:ss'); let time_end = moment.utc(timestamp + (tickInterval * 24)).format('YYYY-MM-DDTHH:mm:ss'); //chart_add_hdd_load_hourly(time_start, time_end); } const chart_add_hdd_load_daily = (time_start, time_end) => { console.log('chart_add_hdd_load_daily: ', time_start, time_end); let size_series = { name: "HDD Size", type: "spline", marker: { symbol: 'circle' }, yAxis: 0, zIndex: 1, tooltip: { valueSuffix: 'GB' }, color: '#51c4d3', data: [] }; let used_series = { name: "HDD Utilization", type: "spline", marker: { symbol: 'circle' }, yAxis: 0, zIndex: 1, tooltip: { valueSuffix: 'GB' }, color: '#ff4757', data: [] }; authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'hdd_daily(object:"' + $stateParams.equipment_id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'step ' + 'epoch ' + 'hdd_size ' + 'hdd_used ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve response.data.data.hdd_daily.forEach(day => { size_series.data.push({ x: parseInt(day.epoch, 10), y: parseFloat((parseInt(day.hdd_size)/1024/1024/1024).toFixed(2)) }); used_series.data.push({ x: parseInt(day.epoch, 10), y: parseFloat((parseInt(day.hdd_used)/1024/1024/1024).toFixed(2)) }); }); chart_hdd.addSeries(size_series); chart_hdd.addSeries(used_series); },function(error) { // failure console.error(error); } ); }); } const chart_add_hdd_load_hourly = (time_start, time_end) => { console.log('chart_add_hdd_load_hourly: ', time_start, time_end); let load_series = { name: "HDD Utilization", type: "column", marker: { symbol: 'circle' }, yAxis: 0, zIndex: 1, tooltip: { xDateFormat: '%b %e, %Y %l %p %Z', valueSuffix: '%' }, color: '#51c4d3', data: [], }; authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'hdd_hourly(object:"' + $stateParams.equipment_id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'step ' + 'epoch ' + 'load_avg ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve response.data.data.cpu_hourly.forEach(day => { load_series.data.push({ x: parseInt(day.epoch, 10), y: day.load_avg }); }); chart_hdd.addSeries(load_series); },function(error) { // failure console.error(error); } ); }); } //////////////////////////////////////////////////////////////////////////////////////////////////////////// // CHART: MSG STATS DATA var chart_msg = null; // chart object $scope.chart_msg_loading = true; $scope.chart_msg_drilldown = false; $scope.chart_msg_drilldown_timestamp = null; // if 1h aggregation is shown then this variable holds information about which day $scope.chart_msg_drillup = function() { chart_load_msg_daily_data(); } const chart_msg_clean = () => { while(chart_msg.series.length) { chart_msg.series[0].remove(); } } const chart_load_msg_daily_data = () => { console.log('chart_load_msg_daily_data'); $scope.chart_msg_drilldown = false; // mark drolldown false $scope.chart_msg_drilldown_timestamp = null; // remove drilldown timestamp chart_msg_clean(); // remove data servies (empty graph) $scope.chart_msg_title = 'Last ' + $sessionStorage.currentUser.timerange + ' days Messages Count'; let tickInterval = 86400000; // 3600 * 24 * 1000 = 1 day chart_msg.xAxis.forEach((xaxis, i) => { // set graph X Axis (time:days) display range xaxis.options.tickInterval = tickInterval; xaxis.setExtremes(time.start, time.today, true); }); let time_start = moment.utc(time.start).format('YYYY-MM-DDTHH:mm:ss'); let time_end = moment.utc(time.today).format('YYYY-MM-DDTHH:mm:ss'); //chart_add_msg_load_daily(time_start, time_end); } const chart_load_msg_hourly_data = (timestamp) => { // drilldown: 24h data $scope.chart_msg_drilldown = true; // mark drolldown true $scope.chart_msg_drilldown_timestamp = timestamp; // remember for which day we show drilldown chart_clean(); // remove data servies (empty graph) $scope.chart_msg_title = moment.utc(timestamp).format('ll') + ' Messages Count'; let tickInterval = 3600000; // 3600 * 1000 = 1 hour chart_msg.xAxis.forEach((xaxis, i) => { // set graph X Axis (time:hours) display range xaxis.options.tickInterval = tickInterval; xaxis.setExtremes(timestamp, (timestamp + (tickInterval * 23)), true); }); let time_start = moment.utc(timestamp).format('YYYY-MM-DDTHH:mm:ss'); let time_end = moment.utc(timestamp + (tickInterval * 24)).format('YYYY-MM-DDTHH:mm:ss'); //chart_add_msg_load_hourly(time_start, time_end); } const chart_add_msg_load_daily = (time_start, time_end) => { console.log('chart_add_msg_load_daily: ', time_start, time_end); let load_series = { name: "Messages Count", type: "column", marker: { symbol: 'circle' }, yAxis: 0, zIndex: 1, tooltip: { valueSuffix: '%' }, color: '#51c4d3', data: [] }; authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'msg_daily(object:"' + $stateParams.equipment_id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'step ' + 'epoch ' + 'load_avg ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve response.data.data.cpu_daily.forEach(day => { load_series.data.push({ x: parseInt(day.epoch, 10), y: day.load_avg }); }); chart_msg.addSeries(load_series); },function(error) { // failure console.error(error); } ); }); } const chart_add_msg_load_hourly = (time_start, time_end) => { console.log('chart_add_msg_load_hourly: ', time_start, time_end); let load_series = { name: "Messages Count", type: "column", marker: { symbol: 'circle' }, yAxis: 0, zIndex: 1, tooltip: { xDateFormat: '%b %e, %Y %l %p %Z', valueSuffix: '%' }, color: '#51c4d3', data: [], }; authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'msg_hourly(object:"' + $stateParams.equipment_id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'step ' + 'epoch ' + 'load_avg ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve response.data.data.cpu_hourly.forEach(day => { load_series.data.push({ x: parseInt(day.epoch, 10), y: day.load_avg }); }); chart_msg.addSeries(load_series); },function(error) { // failure console.error(error); } ); }); } //////////////////////////////////////////////////////////////////////////////////////////////////////////// // HIGHCHARTS SETTINGS Highcharts.setOptions({ colors: ['#192a56', '#ff4757'], time: { timezone: sysClock.getTimeZone() } }); Highcharts.dateFormats = { 'Z': () => { return moment.tz(sysClock.getTimeZone()).format('Z'); } } //////////////////////////////////////////////////////////////////////////////////////////////////////////// // CHART: CPU STATS OBJECT function loadCPUChart() { return new Promise((resolve, reject) => { new Highcharts.Chart({ chart: { renderTo: 'dashboard_cpu', height: 200, borderWidth: 0 }, time: { useUTC: true }, title: { text: null }, subtitle: { text: null }, exporting: { enabled: false }, credits: { enabled: false }, accessibility: { announceNewData: { enabled: true } }, xAxis: { type: 'datetime', tickInterval: 86400000, // 24 * 3600 * 1000 = 1 day }, yAxis: [{ // UPS Load labels: { format: '{value}%', style: { color: Highcharts.getOptions().colors[0] } }, title: { text: 'CPU Load', style: { color: Highcharts.getOptions().colors[0] } } }], tooltip: { xDateFormat: '%b %e, %Y', shared: true }, legend: { enabled: false }, plotOptions: { column: { stacking: 'normal', dataLabels: { enabled: false } }, series: { cursor: 'pointer', pointPadding: 0.01, groupPadding: 0.01, borderWidth: 0.01, shadow: false, dataLabels: { enabled: false }, marker: { enabled: false }, point: { events: { click: function() { //console.log('drilldown: ' + this.series.name + ' ['+this.x+'] -> ' + $scope.chart_cpu_drilldown); /* if(!$scope.chart_cpu_drilldown) { $scope.chart_cpu_drilldown = true; chart_load_cpu_hourly_data(this.x); } */ } } } } }, drilldown: { activeAxisLabelStyle: { cursor: 'default', color: '#3E576F', fontWeight: 'normal', textDecoration: 'none' }, activeDataLabelStyle: { cursor: 'default', color: '#3E576F', fontWeight: 'normal', textDecoration: 'none' } } }, function(chart){ chart_cpu = chart; chart_load_cpu_daily_data(); // dafault $scope.chart_cpu_loading = false; resolve(); }); }); } //////////////////////////////////////////////////////////////////////////////////////////////////////////// // CHART: MEM STATS OBJECT function loadMEMChart() { return new Promise((resolve, reject) => { new Highcharts.Chart({ chart: { renderTo: 'dashboard_mem', height: 200, borderWidth: 0 }, time: { useUTC: true }, title: { text: null }, subtitle: { text: null }, exporting: { enabled: false }, credits: { enabled: false }, accessibility: { announceNewData: { enabled: true } }, xAxis: { type: 'datetime', tickInterval: 86400000, // 24 * 3600 * 1000 = 1 day }, yAxis: [{ // Memory Total labels: { format: '{value} GB', style: { color: Highcharts.getOptions().colors[0] } }, title: { text: 'Memory Total', style: { color: Highcharts.getOptions().colors[0] } } },{ // Memory Used labels: { format: '{value} GB', style: { color: Highcharts.getOptions().colors[1] } }, title: { text: 'Memory Used', style: { color: Highcharts.getOptions().colors[1] } }, opposite: true }], tooltip: { xDateFormat: '%b %e, %Y', shared: true }, legend: { enabled: false }, plotOptions: { column: { stacking: 'normal', dataLabels: { enabled: false } }, series: { cursor: 'pointer', pointPadding: 0.01, groupPadding: 0.01, borderWidth: 0.01, shadow: false, dataLabels: { enabled: false }, marker: { enabled: false }, point: { events: { click: function() { /* //console.log('drilldown: ' + this.series.name + ' ['+this.x+'] -> ' + $scope.chart_mem_drilldown); if(!$scope.chart_mem_drilldown) { $scope.chart_mem_drilldown = true; chart_load_mem_hourly_data(this.x); } */ } } } } }, drilldown: { activeAxisLabelStyle: { cursor: 'default', color: '#3E576F', fontWeight: 'normal', textDecoration: 'none' }, activeDataLabelStyle: { cursor: 'default', color: '#3E576F', fontWeight: 'normal', textDecoration: 'none' } } }, function(chart){ chart_mem = chart; chart_load_mem_daily_data(); // dafault $scope.chart_mem_loading = false; resolve(); }); }); } //////////////////////////////////////////////////////////////////////////////////////////////////////////// // CHART: HDD STATS OBJECT function loadHDDChart() { return new Promise((resolve, reject) => { new Highcharts.Chart({ chart: { renderTo: 'dashboard_hdd', height: 200, borderWidth: 0 }, time: { useUTC: true }, title: { text: null }, subtitle: { text: null }, exporting: { enabled: false }, credits: { enabled: false }, accessibility: { announceNewData: { enabled: true } }, xAxis: { type: 'datetime', tickInterval: 86400000, // 24 * 3600 * 1000 = 1 day }, yAxis: [{ // HDD Total labels: { format: '{value}', style: { color: Highcharts.getOptions().colors[0] } }, title: { text: 'HDD Total', style: { color: Highcharts.getOptions().colors[0] } } },{ // HDD Used labels: { format: '{value}', style: { color: Highcharts.getOptions().colors[1] } }, title: { text: 'HDD Used', style: { color: Highcharts.getOptions().colors[1] } }, opposite: true }], tooltip: { xDateFormat: '%b %e, %Y', shared: true }, legend: { enabled: false }, plotOptions: { column: { stacking: 'normal', dataLabels: { enabled: false } }, series: { cursor: 'pointer', pointPadding: 0.01, groupPadding: 0.01, borderWidth: 0.01, shadow: false, dataLabels: { enabled: false }, marker: { enabled: false }, point: { events: { click: function() { /* //console.log('drilldown: ' + this.series.name + ' ['+this.x+'] -> ' + $scope.chart_hdd_drilldown); if(!$scope.chart_hdd_drilldown) { $scope.chart_hdd_drilldown = true; chart_load_hdd_hourly_data(this.x); } */ } } } } }, drilldown: { activeAxisLabelStyle: { cursor: 'default', color: '#3E576F', fontWeight: 'normal', textDecoration: 'none' }, activeDataLabelStyle: { cursor: 'default', color: '#3E576F', fontWeight: 'normal', textDecoration: 'none' } } }, function(chart){ chart_hdd = chart; chart_load_hdd_daily_data(); // dafault $scope.chart_hdd_loading = false; resolve(); }); }); } //////////////////////////////////////////////////////////////////////////////////////////////////////////// // CHART: MESSAGE STATS OBJECT function loadMSGChart() { return new Promise((resolve, reject) => { new Highcharts.Chart({ chart: { renderTo: 'dashboard_msg', height: 200, borderWidth: 0 }, time: { useUTC: true }, title: { text: null }, subtitle: { text: null }, exporting: { enabled: false }, credits: { enabled: false }, accessibility: { announceNewData: { enabled: true } }, xAxis: { type: 'datetime', tickInterval: 86400000, // 24 * 3600 * 1000 = 1 day }, yAxis: [{ // Messages Total labels: { format: '{value}', style: { color: Highcharts.getOptions().colors[0] } }, title: { text: 'Messages Total', style: { color: Highcharts.getOptions().colors[0] } } }], tooltip: { xDateFormat: '%b %e, %Y', shared: true }, legend: { enabled: false }, plotOptions: { column: { stacking: 'normal', dataLabels: { enabled: false } }, series: { cursor: 'pointer', pointPadding: 0.01, groupPadding: 0.01, borderWidth: 0.01, shadow: false, dataLabels: { enabled: false }, marker: { enabled: false }, point: { events: { click: function() { //console.log('drilldown: ' + this.series.name + ' ['+this.x+'] -> ' + $scope.chart_msg_drilldown); if(!$scope.chart_msg_drilldown) { $scope.chart_msg_drilldown = true; chart_load_msg_hourly_data(this.x); } } } } } }, drilldown: { activeAxisLabelStyle: { cursor: 'default', color: '#3E576F', fontWeight: 'normal', textDecoration: 'none' }, activeDataLabelStyle: { cursor: 'default', color: '#3E576F', fontWeight: 'normal', textDecoration: 'none' } } }, function(chart){ chart_msg = chart; chart_load_msg_daily_data(); // dafault $scope.chart_msg_loading = false; resolve(); }); }); } }]) .controller('EquipmentNTController', ['$rootScope', '$scope', '$http', '$location', '$timeout', '$filter', '$sessionStorage', '$mdPanel', 'timerange', 'modalDialog', 'authService', 'CONFIG', '$stateParams', 'debounce', 'sysClock', function($rootScope, $scope, $http, $location, $timeout, $filter, $sessionStorage, $mdPanel, timerange, modalDialog, authService, CONFIG, $stateParams, debounce, sysClock, wiz){ $rootScope.nt_settings_changed = false; /////////////////////////////////////////////////////////////////////////////////////// $scope.tempscale = $sessionStorage.currentUser.temperature; function F2C(temp) { return temp !== null ? (parseFloat(((temp - 32)*(5/9)).toFixed(2))) : temp; } // function covering Fahrenheit to Celsius value function C2F(temp) { return temp !== null ? (parseFloat(((temp * (9/5))+32).toFixed(2))) : temp; } // function covering Celsius to Fahrenheit value const tempConvert = temp => { switch($sessionStorage.currentUser.temperature) { case 'F': return (temp !== null) ? C2F(temp) : null; case 'C': return (temp !== null) ? F2C(temp) : null; } }; $scope.$on('tempUpdate', function(event, payload) { // time range update event console.log('tempUpdate event: ' + $sessionStorage.currentUser.temperature); $scope.tempscale = $sessionStorage.currentUser.temperature; if(chart_readings) { // check if cart object is created // recalculate tempetarure series data const oldSeriesData = []; chart_readings.series.forEach(series => { if(series.name === 'Indoor Temperature') { oldSeriesData.push(series.userOptions.data); const newSeriesData = oldSeriesData[0].map(point => { return { x: point.x, y: tempConvert(point.y) } }); series.update({ tooltip: { valueSuffix: '°' + $sessionStorage.currentUser.temperature }, data: newSeriesData }); } }); // change Temperature yAxis title & tooltip suffix chart_readings.yAxis[0].update({ title: { text: 'Indoor Temperature °' + $sessionStorage.currentUser.temperature }, labels: { format: (($sessionStorage.currentUser.temperature === 'F') ? '{value}°F' : '{value}°C') } }); } }); /////////////////////////////////////////////////////////////////////////////////////// var time = timerange.calc($sessionStorage.currentUser.timerange); $scope.$on('trUpdate', function(event, payload) { // time range update event console.log('trUpdate event: ' + $sessionStorage.currentUser.timerange); time = timerange.calc($sessionStorage.currentUser.timerange); load_data().catch(error => { console.warn(error); }); }); /////////////////////////////////////////////////////////////////////////////////////// $scope.$on('clockUpdate', function(event, payload) { // time range update event console.log('clockUpdate event: ' + sysClock.getTimeZone()); Highcharts.setOptions({ colors: ['#192a56', '#00b894', '#ff4757'], time: { timezone: sysClock.getTimeZone() } }); if($scope.chart_readings_drilldown) { // if data is shown in hourly graph chart_load_hourly_data($scope.chart_readings_drilldown_timestamp); } else { // if data is shown in daily graph chart_load_daily_data(); } }); /////////////////////////////////////////////////////////////////////////////////////// function load_data() { return new Promise((resolve, reject) => { let time_start = moment(time.start).format('YYYY-MM-DDTHH:mm:ss'); let time_end = moment(time.today).format('YYYY-MM-DDTHH:mm:ss'); console.log('time_start: ' + time_start); console.log('time_end: ' + time_end); authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'equipment(id:"' + $scope.equipment.id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'id ' + 'name ' + 'status ' + 'company {' + 'id ' + '}' + 'location {' + 'id ' + '}' + 'room {' + 'id ' + '}' + 'type { ' + 'id ' + 'name ' + '} ' + 'brand { ' + 'id ' + 'name ' + '} ' + 'model { ' + 'id ' + 'name ' + '} ' + 'groups { ' + 'id ' + 'name ' + '} ' + '... on EquipmentNT { ' + 'mac ' + 'ip ' + 'state { ' + 'updated ' + 'scale ' + 'indoor_temperature ' + 'indoor_humidity ' + 'outdoor_temperature ' + 'outdoor_humidity ' + 'setpoint_heat ' + 'setpoint_cool ' + 'mode ' + 'fan ' + 'override ' + 'recovery ' + 'status ' + 'stage ' + 'event ' + '} ' + '} ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve if(response.data.data.equipment.length){ $scope.equipment = response.data.data.equipment[0]; $scope.equipment.isvisible = true; if($scope.equipment.state.scale.toLowerCase() === 'f') { $scope.equipment.state.indoor_temperature = F2C($scope.equipment.state.indoor_temperature); $scope.equipment.state.outdoor_temperature = F2C($scope.equipment.state.outdoor_temperature); $scope.equipment.state.setpoint_heat = F2C($scope.equipment.state.setpoint_heat); $scope.equipment.state.setpoint_cool = F2C($scope.equipment.state.setpoint_cool); } if(!$scope.equipment.state.mode) { $scope.equipment.state.status_display = 'NO DATA'; $scope.equipment.state.status_type = 'nodata'; } else { switch($scope.equipment.state.mode.toUpperCase()) { case 'H': // On Line $scope.equipment.state.status_display = 'HEAT'; $scope.equipment.state.status_type = 'normal'; break; case 'C': // On Line $scope.equipment.state.status_display = 'COOL'; $scope.equipment.state.status_type = 'normal'; break; case 'A': // On Line $scope.equipment.state.status_display = 'AUTO'; $scope.equipment.state.status_type = 'normal'; break; case 'E': // On Battery $scope.equipment.state.status_display = 'EHEAT'; $scope.equipment.state.status_type = 'normal'; break; case 'O': $scope.equipment.state.status_display = 'OFF'; $scope.equipment.state.status_type = 'warning'; break; default: $scope.equipment.state.status_display = $scope.equipment.state.status; $scope.equipment.state.status_type = 'warning'; } } $scope.isLoading = false; // hide ajax loader loadChart().then(() => { // create chart object resolve(null); }); } else { $scope.isLoading = false; // hide ajax loader } },function(error) { // failure console.error(error); reject(error); } ); }); }); } $timeout(function() { load_data().then(() => { // nothing }).catch(error => { console.warn(error); }); }, 100); //////////////////////////////////////////////////////////////////////////////////////////////////////////// // Settings $scope.$on('NTChangeSettingsApply', (payload) => { applyChangeSettings(); }); $scope.$on('NTChangeSettingsCancel', (payload) => { cancelChangeSettings(); }); var cancelChangeSettings = () => { $rootScope.nt_settings_changed = false; }; $rootScope.request_ongoing = false; var applyChangeSettings = () => { // API/GraphQL request here $rootScope.nt_settings_changed = false; }; $scope.changeSettings = () => { $rootScope.nt_settings_changed = true; }; $scope.setMode = (mode) => { /* switch(mode) { case 'heat': $scope.equipment.state.mode = 'H'; $scope.equipment.state.status_display = 'HEAT'; $scope.equipment.state.status_type = 'normal'; break; case 'cool': $scope.equipment.state.mode = 'C'; $scope.equipment.state.status_display = 'COOL'; $scope.equipment.state.status_type = 'normal'; break; case 'auto': $scope.equipment.state.mode = 'A'; $scope.equipment.state.status_display = 'AUTO'; $scope.equipment.state.status_type = 'normal'; break; case 'eheat': $scope.equipment.state.mode = 'E'; $scope.equipment.state.status_display = 'EHEAT'; $scope.equipment.state.status_type = 'normal'; break; case 'off': $scope.equipment.state.mode = 'O'; $scope.equipment.state.status_display = 'OFF'; $scope.equipment.state.status_type = 'warning'; break; default: $scope.equipment.state.status_display = $scope.equipment.state.status; $scope.equipment.state.status_type = 'warning'; } $scope.changeSettings(); */ }; $scope.setFan = (mode) => { /* switch(mode){ case 'auto': $scope.equipment.state.fan = 'A'; break; case 'on': $scope.equipment.state.fan = 'O'; break; default: $scope.equipment.state.fan = 'A'; } $scope.changeSettings(); */ }; //////////////////////////////////////////////////////////////////////////////////////////////////////////// // Groups $scope.showGroups = ($event) => { console.log('showGroups'); var config = { attachTo: angular.element(document.body), controller: ['mdPanelRef', '$scope', function(mdPanelRef, $scope) { this.closeMenu = function() { mdPanelRef && mdPanelRef.close(); }; this.selectGroup = function(group_id){ mdPanelRef && mdPanelRef.close(); $location.path('/system/groups/view/' + group_id); }; }], controllerAs: 'ctrl', targetEvent: $event, openFrom: $event, templateUrl: 'panel.equipmentgroupslist.html', clickOutsideToClose: true, escapeToClose: true, position: $mdPanel.newPanelPosition() .relativeTo($event.target) .addPanelPosition( $mdPanel.xPosition.OFFSET_END, $mdPanel.yPosition.ABOVE ), locals: { items: $scope.equipment.groups }, }; if($scope.equipment.groups.length) { $mdPanel.open(config); } }; //////////////////////////////////////////////////////////////////////////////////////////////////////////// // CHART: UPS STATS DATA var chart_readings = null; // chart object $scope.chart_readings_loading = true; $scope.chart_readings_drilldown = false; $scope.chart_readings_drilldown_timestamp = null; // if 1h aggregation is shown then this variable holds information about which day $scope.chart_drillup = function() { chart_load_daily_data(); } const chart_clean = () => { while(chart_readings.series.length) { chart_readings.series[0].remove(); } } const chart_load_daily_data = () => { $scope.chart_readings_drilldown = false; // mark drolldown false $scope.chart_readings_drilldown_timestamp = null; // remove drilldown timestamp chart_clean(); // remove data servies (empty graph) $scope.chart_readings_title = 'Last ' + $sessionStorage.currentUser.timerange + ' days Readings'; let tickInterval = 86400000; // 3600 * 24 * 1000 = 1 day chart_readings.xAxis.forEach((xaxis, i) => { // set graph X Axis (time:days) display range xaxis.options.tickInterval = tickInterval; xaxis.setExtremes(time.start, time.today, true); }); let time_start = moment.utc(time.start).format('YYYY-MM-DDTHH:mm:ss'); let time_end = moment.utc(time.today).format('YYYY-MM-DDTHH:mm:ss'); chart_add_indoor_temperature_daily(time_start, time_end); chart_add_indoor_humidity_daily(time_start, time_end); } const chart_load_hourly_data = (timestamp) => { // drilldown: 24h data $scope.chart_readings_drilldown = true; // mark drolldown true $scope.chart_readings_drilldown_timestamp = timestamp; // remember for which day we show drilldown chart_clean(); // remove data servies (empty graph) $scope.chart_readings_title = moment.utc(timestamp).format('ll') + ' Readings'; let tickInterval = 3600000; // 3600 * 1000 = 1 hour chart_readings.xAxis.forEach((xaxis, i) => { // set graph X Axis (time:hours) display range xaxis.options.tickInterval = tickInterval; xaxis.setExtremes(timestamp, (timestamp + (tickInterval * 23)), true); }); let time_start = moment.utc(timestamp).format('YYYY-MM-DDTHH:mm:ss'); let time_end = moment.utc(timestamp + (tickInterval * 24)).format('YYYY-MM-DDTHH:mm:ss'); chart_add_indoor_temperature_hourly(time_start, time_end); chart_add_indoor_humidity_hourly(time_start, time_end); } const chart_add_indoor_temperature_daily = (time_start, time_end) => { console.log('chart_add_indoor_temperature_daily: ', time_start, time_end); let temperature_series = { name: "Indoor Temperature", type: "spline", marker: { symbol: 'circle' }, yAxis: 0, zIndex: 1, tooltip: { valueSuffix: '°' + $sessionStorage.currentUser.temperature }, color: '#51c4d3', data: [] }; authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'temperature_daily(object:"' + $stateParams.equipment_id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'step ' + 'epoch ' + 't_min ' + 't_avg ' + 't_max ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve response.data.data.temperature_daily.forEach(day => { temperature_series.data.push({ x: parseInt(day.epoch, 10), y: ($sessionStorage.currentUser.temperature === 'F') ? tempConvert(day.t_avg) : day.t_avg }); }); chart_readings.addSeries(temperature_series); },function(error) { // failure console.error(error); } ); }); } const chart_add_indoor_temperature_hourly = (time_start, time_end) => { console.log('chart_add_indoor_temperature_hourly: ', time_start, time_end); let temperature_series = { name: "Indoor Temperature", type: "spline", marker: { symbol: 'circle' }, yAxis: 0, zIndex: 1, tooltip: { xDateFormat: '%b %e, %Y %l %p %Z', valueSuffix: '°' + $sessionStorage.currentUser.temperature }, color: '#51c4d3', data: [], }; authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'temperature_hourly(object:"' + $stateParams.equipment_id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'step ' + 'epoch ' + 't_min ' + 't_avg ' + 't_max ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve response.data.data.temperature_hourly.forEach(hour => { temperature_series.data.push({ x: parseInt(hour.epoch, 10), y: ($sessionStorage.currentUser.temperature === 'F') ? tempConvert(hour.t_avg) : hour.t_avg }); }); chart_readings.addSeries(temperature_series); },function(error) { // failure console.error(error); } ); }); } const chart_add_indoor_humidity_daily = (time_start, time_end) => { console.log('chart_add_indoor_humidity_daily: ', time_start, time_end); let humidity_series = { name: "Indoor Humidity", type: "spline", marker: { symbol: 'circle' }, yAxis: 1, zIndex: 2, tooltip: { valueSuffix: 'RH%' }, color: '#ff4757', data: [] }; authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'humidity_daily(object:"' + $stateParams.equipment_id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'step ' + 'epoch ' + 'h_min ' + 'h_avg ' + 'h_max ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve response.data.data.humidity_daily.forEach(day => { humidity_series.data.push({ x: parseInt(day.epoch, 10), y: day.h_avg }); }); chart_readings.addSeries(humidity_series); },function(error) { // failure console.error(error); } ); }); } const chart_add_indoor_humidity_hourly = (time_start, time_end) => { console.log('chart_add_indoor_humidity_hourly: ', time_start, time_end); let humidity_series = { name: "Indoor Humidity", type: "spline", marker: { symbol: 'circle' }, yAxis: 1, zIndex: 2, tooltip: { valueSuffix: 'RH%' }, color: '#ff4757', data: [] }; authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'humidity_hourly(object:"' + $stateParams.equipment_id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'step ' + 'epoch ' + 'h_min ' + 'h_avg ' + 'h_max ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve response.data.data.humidity_hourly.forEach(hour => { humidity_series.data.push({ x: parseInt(hour.epoch, 10), y: hour.h_avg }); }); chart_readings.addSeries(humidity_series); },function(error) { // failure console.error(error); } ); }); } //////////////////////////////////////////////////////////////////////////////////////////////////////////// // CHART: UPS STATS OBJECT Highcharts.setOptions({ colors: ['#192a56', '#ff4757'], time: { timezone: sysClock.getTimeZone() } }); Highcharts.dateFormats = { 'Z': () => { return moment.tz(sysClock.getTimeZone()).format('Z'); } } function loadChart() { return new Promise((resolve, reject) => { new Highcharts.Chart({ chart: { renderTo: 'dashboard_readings', height: 200, borderWidth: 0 }, time: { useUTC: true }, title: { text: null }, subtitle: { text: null }, exporting: { enabled: false }, credits: { enabled: false }, accessibility: { announceNewData: { enabled: true } }, xAxis: { type: 'datetime', tickInterval: 86400000, // 24 * 3600 * 1000 = 1 day }, yAxis: [{ // Indoor Temperature labels: { format: (($sessionStorage.currentUser.temperature === 'F') ? '{value}°F' : '{value}°C'), style: { color: Highcharts.getOptions().colors[0] } }, title: { text: 'Indoor Temperature °' + $sessionStorage.currentUser.temperature, style: { color: Highcharts.getOptions().colors[0], whiteSpace: 'nowrap', } }, },{ // Indoor Humidity labels: { format: '{value}RH%', style: { color: Highcharts.getOptions().colors[1] } }, title: { text: 'Indoor Humidity', style: { color: Highcharts.getOptions().colors[1], whiteSpace: 'nowrap', } }, opposite: true }], tooltip: { xDateFormat: '%b %e, %Y', shared: true }, legend: { enabled: false }, plotOptions: { column: { stacking: 'normal', dataLabels: { enabled: false } }, series: { cursor: 'pointer', pointPadding: 0.01, groupPadding: 0.01, borderWidth: 0.01, shadow: false, dataLabels: { enabled: false }, marker: { enabled: false }, point: { events: { click: function() { //console.log('drilldown: ' + this.series.name + ' ['+this.x+'] -> ' + $scope.chart_readings_drilldown); if(!$scope.chart_readings_drilldown) { $scope.chart_readings_drilldown = true; chart_load_hourly_data(this.x); } } } } } }, drilldown: { activeAxisLabelStyle: { cursor: 'default', color: '#3E576F', fontWeight: 'normal', textDecoration: 'none' }, activeDataLabelStyle: { cursor: 'default', color: '#3E576F', fontWeight: 'normal', textDecoration: 'none' } } }, function(chart){ chart_readings = chart; chart_load_daily_data(); // dafault $scope.chart_readings_loading = false; resolve(); }); }); } }]) .controller('EquipmentFACPController', ['$rootScope', '$scope', '$http', '$location', '$timeout', '$filter', '$sessionStorage', '$mdPanel', '$mdDialog', '$mdMenu', 'timerange', 'modalDialog', 'authService', 'CONFIG', '$stateParams', 'debounce', 'sysClock', function($rootScope, $scope, $http, $location, $timeout, $filter, $sessionStorage, $mdPanel, $mdDialog, $mdMenu, timerange, modalDialog, authService, CONFIG, $stateParams, debounce, sysClock, wiz){ $rootScope.nt_settings_changed = false; /////////////////////////////////////////////////////////////////////////////////////// var time = timerange.calc($sessionStorage.currentUser.timerange); $scope.$on('trUpdate', function(event, payload) { // time range update event console.log('trUpdate event: ' + $sessionStorage.currentUser.timerange); time = timerange.calc($sessionStorage.currentUser.timerange); load_data().catch(error => { console.warn(error); }); }); /////////////////////////////////////////////////////////////////////////////////////// $scope.$on('clockUpdate', function(event, payload) { // time range update event console.log('clockUpdate event: ' + sysClock.getTimeZone()); let tzabbr = moment.tz.zone(sysClock.getTimeZone()).abbr(moment(new Date())); let tztime = moment.utc(new Date(), 'YYYY-MM-DD HH:mm:ss').tz(sysClock.getTimeZone()).format('MMM DD, YYYY HH:mm:SS'); $scope.equipment.heartbeat = tztime + ' ' + tzabbr; Highcharts.setOptions({ colors: ['#192a56', '#00b894', '#ff4757'], time: { timezone: sysClock.getTimeZone() } }); if($scope.chart_events_drilldown) { // if data is shown in hourly graph chart_load_hourly_data($scope.chart_events_drilldown_timestamp); } else { // if data is shown in daily graph chart_load_daily_data(); } }); /////////////////////////////////////////////////////////////////////////////////////// function load_data() { return new Promise((resolve, reject) => { let time_start = moment(time.start).format('YYYY-MM-DDTHH:mm:ss'); let time_end = moment(time.today).format('YYYY-MM-DDTHH:mm:ss'); console.log('time_start: ' + time_start); console.log('time_end: ' + time_end); authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'equipment(id:"' + $scope.equipment.id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'id ' + 'name ' + 'status ' + 'company {' + 'id ' + '}' + 'location {' + 'id ' + '}' + 'room {' + 'id ' + '}' + 'type { ' + 'id ' + 'name ' + '} ' + 'brand { ' + 'id ' + 'name ' + '} ' + 'model { ' + 'id ' + 'name ' + '} ' + 'groups { ' + 'id ' + 'name ' + '} ' + '... on EquipmentFACP { ' + 'mac ' + 'ip ' + 'state { ' + 'status ' + '} ' + '} ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve $scope.query = { order: 'name', limit: 10, page: 1 }; ////[MOCKUP]////////////////////////////// $scope.devices = [ { status:0, name:"PS001", type:"Pull Station", loop:"001", number:"001", floor:"1st Floor", room:"WB Office", events:0 },{ status:0, name:"HS002", type:"Horn/Strobe", loop:"001", number:"002", floor:"1st Floor", room:"WB Office", events:0 },{ status:0, name:"HS003", type:"Horn/Strobe", loop:"001", number:"003", floor:"1st Floor", room:"WB Office", events:0 },{ status:0, name:"SD154", type:"Smoke Detector", loop:"001", number:"154", floor:"1st Floor", room:"WB Office", events:2 },{ status:1, name:"SD023", type:"Smoke Detector", loop:"001", number:"023", floor:"1st Floor", room:"WB Office", events:4 },{ status:0, name:"HD992", type:"Heat Detector", loop:"001", number:"992", floor:"1st Floor", room:"WB Office", events:0 },{ status:0, name:"HD045", type:"Heat Detector", loop:"001", number:"045", floor:"1st Floor", room:"WB Office", events:0 },{ status:0, name:"TS334", type:"Tamper Switch", loop:"001", number:"334", floor:"1st Floor", room:"WB Office", events:0 },{ status:0, name:"SD421", type:"Smoke Detector", loop:"001", number:"421", floor:"1st Floor", room:"WB Office", events:0 },{ status:0, name:"SD514", type:"Smoke Detector", loop:"001", number:"514", floor:"1st Floor", room:"WB Office", events:4 } ]; ////////////////////////////////////////// if(response.data.data.equipment.length){ $scope.equipment = response.data.data.equipment[0]; $scope.equipment.isvisible = true; console.log($scope.equipment); ////[MOCKUP]////////////////////////////// let tzabbr = moment.tz.zone(sysClock.getTimeZone()).abbr(moment(new Date())); let tztime = moment.utc(new Date(), 'YYYY-MM-DD HH:mm:ss').tz(sysClock.getTimeZone()).format('MMM DD, YYYY HH:mm:SS'); $scope.equipment.heartbeat = tztime + ' ' + tzabbr; $scope.equipment.count = { messages: 0, monitor: 0, trouble: 0, supervisory: 0, alarm: 0 }; ////////////////////////////////////////// if(!$scope.equipment.state.status) { $scope.equipment.state.status_display = 'NO DATA'; $scope.equipment.state.status_type = 'nodata'; } else { switch($scope.equipment.state.status.toUpperCase()) { case 'N': // On Line / Normal $scope.equipment.state.status_display = 'ONLINE'; $scope.equipment.state.status_type = 'normal'; break; case 'O': // Off Line $scope.equipment.state.status_display = 'OFFLINE'; $scope.equipment.state.status_type = 'warning'; break; case 'A': // Alert $scope.equipment.state.status_display = 'ALERT'; $scope.equipment.state.status_type = 'alert'; break; default: $scope.equipment.state.status_display = $scope.equipment.state.status; $scope.equipment.state.status_type = 'warning'; } } $scope.isLoading = false; // hide ajax loader loadChart().then(() => { // create chart object resolve(null); }); } else { $scope.isLoading = false; // hide ajax loader } },function(error) { // failure console.error(error); reject(error); } ); }); }); } $timeout(function() { load_data().then(() => { // nothing }).catch(error => { console.warn(error); }); }, 100); //////////////////////////////////////////////////////////////////////////////////////////////////////////// // Settings $scope.$on('FACPChangeSettingsApply', (payload) => { applyChangeSettings(); }); $scope.$on('FACPChangeSettingsCancel', (payload) => { cancelChangeSettings(); }); var cancelChangeSettings = () => { $rootScope.facp_settings_changed = false; }; $rootScope.request_ongoing = false; var applyChangeSettings = () => { // API/GraphQL request here $rootScope.facp_settings_changed = false; }; $scope.changeSettings = () => { $rootScope.facp_settings_changed = true; }; //////////////////////////////////////////////////////////////////////////////////////////////////////////// // Groups $scope.showGroups = ($event) => { console.log('showGroups'); var config = { attachTo: angular.element(document.body), controller: ['mdPanelRef', '$scope', function(mdPanelRef, $scope) { this.closeMenu = function() { mdPanelRef && mdPanelRef.close(); }; this.selectGroup = function(group_id){ mdPanelRef && mdPanelRef.close(); $location.path('/system/groups/view/' + group_id); }; }], controllerAs: 'ctrl', targetEvent: $event, openFrom: $event, templateUrl: 'panel.equipmentgroupslist.html', clickOutsideToClose: true, escapeToClose: true, position: $mdPanel.newPanelPosition() .relativeTo($event.target) .addPanelPosition( $mdPanel.xPosition.OFFSET_END, $mdPanel.yPosition.ABOVE ), locals: { items: $scope.equipment.groups }, }; if($scope.equipment.groups.length) { $mdPanel.open(config); } }; //////////////////////////////////////////////////////////////////////////////////////////////////////////// // CHART: FACP STATS DATA var chart_pie = null; // chart object var chart_events = null; // chart object $scope.chart_events_loading = true; $scope.chart_piechart_loading = true; $scope.chart_events_drilldown = false; $scope.chart_events_drilldown_timestamp = null; // if 1h aggregation is shown then this variable holds information about which day $scope.chart_drillup = function() { chart_load_daily_data(); } const chart_clean = () => { while(chart_events.series.length) { chart_events.series[0].remove(); } while(chart_pie.series.length) { chart_pie.series[0].remove(); } } const chart_load_daily_data = () => { $scope.chart_events_drilldown = false; // mark drolldown false $scope.chart_events_drilldown_timestamp = null; // remove drilldown timestamp chart_clean(); // remove data servies (empty graph) $scope.chart_events_title = 'Last ' + $sessionStorage.currentUser.timerange + ' days Events'; let tickInterval = 86400000; // 3600 * 24 * 1000 = 1 day chart_events.xAxis.forEach((xaxis, i) => { // set graph X Axis (time:days) display range xaxis.options.tickInterval = tickInterval; xaxis.setExtremes(time.start, time.today, true); }); let time_start = moment.utc(time.start).format('YYYY-MM-DDTHH:mm:ss'); let time_end = moment.utc(time.today).format('YYYY-MM-DDTHH:mm:ss'); chart_add_facp_daily(time_start, time_end); } const chart_load_hourly_data = (timestamp) => { // drilldown: 24h data $scope.chart_events_drilldown = true; // mark drolldown true $scope.chart_events_drilldown_timestamp = timestamp; // remember for which day we show drilldown chart_clean(); // remove data servies (empty graph) $scope.chart_events_title = moment.utc(timestamp).format('ll') + ' Events'; let tickInterval = 3600000; // 3600 * 1000 = 1 hour chart_events.xAxis.forEach((xaxis, i) => { // set graph X Axis (time:hours) display range xaxis.options.tickInterval = tickInterval; xaxis.setExtremes(timestamp, (timestamp + (tickInterval * 23)), true); }); let time_start = moment.utc(timestamp).format('YYYY-MM-DDTHH:mm:ss'); let time_end = moment.utc(timestamp + (tickInterval * 24)).format('YYYY-MM-DDTHH:mm:ss'); chart_add_facp_hourly(time_start, time_end); } const chart_add_facp_daily = (time_start, time_end) => { console.log('chart_add_facp_daily: ', time_start, time_end); let alarm_series = { name: "Alarm", type: "column", marker: { symbol: 'circle' }, yAxis: 0, zIndex: 1, tooltip: {}, color: '#ff2400', data: [] }; let superv_series = { name: "Supervisory", type: "column", marker: { symbol: 'circle' }, yAxis: 0, zIndex: 2, tooltip: {}, color: '#8e44ad', data: [] }; let trouble_series = { name: "Trouble", type: "column", marker: { symbol: 'circle' }, yAxis: 0, zIndex: 3, tooltip: {}, color: '#f1c40f', data: [] }; let monitor_series = { name: "Monitor", type: "column", marker: { symbol: 'circle' }, yAxis: 0, zIndex: 4, tooltip: {}, color: '#2980b9', data: [] }; let message_series = { name: "Message", type: "column", marker: { symbol: 'circle' }, yAxis: 0, zIndex: 5, tooltip: {}, color: '#dfe4ea', data: [] }; authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'facp_daily(object:"' + $stateParams.equipment_id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'step ' + 'epoch ' + 't_min ' + 't_avg ' + 't_max ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve response.data.data.facp_daily.forEach(day => { alarm_series.data.push({ x: parseInt(day.epoch, 10), y: day.e_count }); }); chart_events.addSeries(alarm_series); },function(error) { // failure console.error(error); } ); //[MOCKUP]//////////////////////////////// var startdate = moment().startOf('day'); console.log('startdate: ', startdate.toString()); var response = { data:{ data:{ facp_daily:[ {epoch:startdate.subtract(1, 'd').valueOf(), alarm:0, superv:0, trouble:0, monitor:0, message:0}, {epoch:startdate.subtract(1, 'd').valueOf(), alarm:0, superv:0, trouble:2, monitor:0, message:0}, {epoch:startdate.subtract(1, 'd').valueOf(), alarm:0, superv:0, trouble:0, monitor:0, message:0}, {epoch:startdate.subtract(1, 'd').valueOf(), alarm:0, superv:0, trouble:0, monitor:0, message:0}, {epoch:startdate.subtract(1, 'd').valueOf(), alarm:0, superv:0, trouble:0, monitor:0, message:0}, {epoch:startdate.subtract(1, 'd').valueOf(), alarm:0, superv:0, trouble:0, monitor:0, message:0}, {epoch:startdate.subtract(1, 'd').valueOf(), alarm:0, superv:0, trouble:0, monitor:0, message:0}, {epoch:startdate.subtract(1, 'd').valueOf(), alarm:0, superv:0, trouble:0, monitor:1, message:0}, {epoch:startdate.subtract(1, 'd').valueOf(), alarm:0, superv:0, trouble:0, monitor:1, message:0}, {epoch:startdate.subtract(1, 'd').valueOf(), alarm:0, superv:0, trouble:0, monitor:0, message:0}, {epoch:startdate.subtract(1, 'd').valueOf(), alarm:0, superv:0, trouble:0, monitor:0, message:0}, {epoch:startdate.subtract(1, 'd').valueOf(), alarm:0, superv:0, trouble:0, monitor:0, message:0}, {epoch:startdate.subtract(1, 'd').valueOf(), alarm:0, superv:0, trouble:0, monitor:0, message:2}, {epoch:startdate.subtract(1, 'd').valueOf(), alarm:0, superv:0, trouble:0, monitor:0, message:4}, {epoch:startdate.subtract(1, 'd').valueOf(), alarm:0, superv:0, trouble:1, monitor:0, message:1}, {epoch:startdate.subtract(1, 'd').valueOf(), alarm:0, superv:0, trouble:8, monitor:0, message:2}, {epoch:startdate.subtract(1, 'd').valueOf(), alarm:0, superv:0, trouble:2, monitor:0, message:1}, {epoch:startdate.subtract(1, 'd').valueOf(), alarm:0, superv:0, trouble:3, monitor:0, message:0}, {epoch:startdate.subtract(1, 'd').valueOf(), alarm:0, superv:0, trouble:2, monitor:0, message:0}, {epoch:startdate.subtract(1, 'd').valueOf(), alarm:0, superv:0, trouble:0, monitor:0, message:0}, {epoch:startdate.subtract(1, 'd').valueOf(), alarm:0, superv:0, trouble:0, monitor:0, message:0}, {epoch:startdate.subtract(1, 'd').valueOf(), alarm:0, superv:0, trouble:0, monitor:3, message:0}, {epoch:startdate.subtract(1, 'd').valueOf(), alarm:0, superv:0, trouble:0, monitor:0, message:0}, {epoch:startdate.subtract(1, 'd').valueOf(), alarm:1, superv:0, trouble:0, monitor:0, message:0}, {epoch:startdate.subtract(1, 'd').valueOf(), alarm:0, superv:0, trouble:0, monitor:0, message:0}, {epoch:startdate.subtract(1, 'd').valueOf(), alarm:0, superv:0, trouble:0, monitor:0, message:0}, {epoch:startdate.subtract(1, 'd').valueOf(), alarm:0, superv:1, trouble:0, monitor:0, message:0}, {epoch:startdate.subtract(1, 'd').valueOf(), alarm:0, superv:2, trouble:0, monitor:0, message:0}, {epoch:startdate.subtract(1, 'd').valueOf(), alarm:0, superv:1, trouble:0, monitor:0, message:0}, {epoch:startdate.subtract(1, 'd').valueOf(), alarm:0, superv:1, trouble:0, monitor:0, message:0} ] } } }; response.data.data.facp_daily.forEach(day => { alarm_series.data.push({ x: parseInt(day.epoch, 10), y: day.alarm }); superv_series.data.push({ x: parseInt(day.epoch, 10), y: day.superv }); trouble_series.data.push({ x: parseInt(day.epoch, 10), y: day.trouble }); monitor_series.data.push({ x: parseInt(day.epoch, 10), y: day.monitor }); message_series.data.push({ x: parseInt(day.epoch, 10), y: day.message }); }); chart_events.addSeries(alarm_series); chart_events.addSeries(superv_series); chart_events.addSeries(trouble_series); chart_events.addSeries(monitor_series); chart_events.addSeries(message_series); $scope.equipment.count.alarm = 1; $scope.equipment.count.supervisory = 4; $scope.equipment.count.trouble = 16; $scope.equipment.count.monitor = 5; $scope.equipment.count.messages = 10; let total_event_count = $scope.equipment.count.alarm + $scope.equipment.count.supervisory + $scope.equipment.count.trouble + $scope.equipment.count.monitor + $scope.equipment.count.messages; let pie_series = { type: "pie", data: [] }; pie_series.data.push({tooltipcolor:'#ff2400', name: 'ALARM', y:(($scope.equipment.count.alarm/total_event_count)*100)}); pie_series.data.push({tooltipcolor:'#8e44ad', name: 'SUPERVISORY', y:(($scope.equipment.count.supervisory/total_event_count)*100)}); pie_series.data.push({tooltipcolor:'#f1c40f', name: 'TROUBLE', y:(($scope.equipment.count.trouble/total_event_count)*100)}); pie_series.data.push({tooltipcolor:'#2980b9', name: 'MONITOR', y:(($scope.equipment.count.monitor/total_event_count)*100)}); pie_series.data.push({tooltipcolor:'#93989e', name: 'MESSAGE', y:(($scope.equipment.count.messages/total_event_count)*100)}); pie_series.data.push({tooltipcolor:'#000000', name: 'NO EVENTS', y:(total_event_count>0?0:100)}); chart_pie.addSeries(pie_series); ////////////////////////////////////////// }); } const chart_add_facp_hourly = (time_start, time_end) => { console.log('chart_add_facp_hourly: ', time_start, time_end); let alarm_series = { name: "Alarm", type: "column", marker: { symbol: 'circle' }, yAxis: 0, zIndex: 1, tooltip: {}, color: '#ff2400', data: [] }; let superv_series = { name: "Supervisory", type: "column", marker: { symbol: 'circle' }, yAxis: 0, zIndex: 2, tooltip: {}, color: '#8e44ad', data: [] }; let trouble_series = { name: "Trouble", type: "column", marker: { symbol: 'circle' }, yAxis: 0, zIndex: 3, tooltip: {}, color: '#f1c40f', data: [] }; let monitor_series = { name: "Monitor", type: "column", marker: { symbol: 'circle' }, yAxis: 0, zIndex: 4, tooltip: {}, color: '#2980b9', data: [] }; let message_series = { name: "Message", type: "column", marker: { symbol: 'circle' }, yAxis: 0, zIndex: 5, tooltip: {}, color: '#dfe4ea', data: [] }; let event_series = { name: "", type: "column", marker: { symbol: 'circle' }, yAxis: 0, zIndex: 1, tooltip: { xDateFormat: '%b %e, %Y %l %p %Z', valueSuffix: '°' + $sessionStorage.currentUser.temperature }, color: '#51c4d3', data: [], }; authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'facp_hourly(object:"' + $stateParams.equipment_id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'step ' + 'epoch ' + 't_min ' + 't_avg ' + 't_max ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve response.data.data.facp_hourly.forEach(hour => { event_series.data.push({ x: parseInt(hour.epoch, 10), y: hour.e_count }); }); chart_events.addSeries(event_series); },function(error) { // failure console.error(error); } ); //[MOCKUP]//////////////////////////////// var startdate = moment.utc(time_end); console.log(startdate.toString()); console.log(startdate.subtract(1, 'h').toString()); var response = { data:{ data:{ facp_daily:[ {epoch:startdate.subtract(1, 'h').valueOf(), alarm:0, superv:0, trouble:0, monitor:0, message:0}, {epoch:startdate.subtract(1, 'h').valueOf(), alarm:0, superv:0, trouble:2, monitor:0, message:0}, {epoch:startdate.subtract(1, 'h').valueOf(), alarm:0, superv:0, trouble:1, monitor:0, message:0}, {epoch:startdate.subtract(1, 'h').valueOf(), alarm:0, superv:0, trouble:0, monitor:0, message:0}, {epoch:startdate.subtract(1, 'h').valueOf(), alarm:0, superv:0, trouble:0, monitor:0, message:0}, {epoch:startdate.subtract(1, 'h').valueOf(), alarm:0, superv:0, trouble:0, monitor:0, message:0}, {epoch:startdate.subtract(1, 'h').valueOf(), alarm:0, superv:0, trouble:0, monitor:0, message:0}, {epoch:startdate.subtract(1, 'h').valueOf(), alarm:0, superv:0, trouble:0, monitor:0, message:0}, {epoch:startdate.subtract(1, 'h').valueOf(), alarm:0, superv:0, trouble:0, monitor:0, message:0}, {epoch:startdate.subtract(1, 'h').valueOf(), alarm:0, superv:0, trouble:0, monitor:0, message:0}, {epoch:startdate.subtract(1, 'h').valueOf(), alarm:0, superv:0, trouble:0, monitor:0, message:0}, {epoch:startdate.subtract(1, 'h').valueOf(), alarm:0, superv:0, trouble:0, monitor:0, message:0}, {epoch:startdate.subtract(1, 'h').valueOf(), alarm:0, superv:0, trouble:0, monitor:0, message:0}, {epoch:startdate.subtract(1, 'h').valueOf(), alarm:0, superv:0, trouble:0, monitor:0, message:0}, {epoch:startdate.subtract(1, 'h').valueOf(), alarm:0, superv:0, trouble:0, monitor:0, message:0}, {epoch:startdate.subtract(1, 'h').valueOf(), alarm:0, superv:0, trouble:0, monitor:0, message:0}, {epoch:startdate.subtract(1, 'h').valueOf(), alarm:0, superv:0, trouble:0, monitor:0, message:0}, {epoch:startdate.subtract(1, 'h').valueOf(), alarm:0, superv:0, trouble:0, monitor:0, message:0}, {epoch:startdate.subtract(1, 'h').valueOf(), alarm:0, superv:0, trouble:0, monitor:0, message:0}, {epoch:startdate.subtract(1, 'h').valueOf(), alarm:1, superv:0, trouble:0, monitor:0, message:0}, {epoch:startdate.subtract(1, 'h').valueOf(), alarm:1, superv:0, trouble:0, monitor:0, message:0}, {epoch:startdate.subtract(1, 'h').valueOf(), alarm:0, superv:0, trouble:0, monitor:3, message:0}, {epoch:startdate.subtract(1, 'h').valueOf(), alarm:0, superv:0, trouble:0, monitor:0, message:0}, {epoch:startdate.subtract(1, 'h').valueOf(), alarm:0, superv:0, trouble:0, monitor:0, message:0}, ] } } }; console.log(response.data.data.facp_daily); response.data.data.facp_daily.forEach(day => { alarm_series.data.push({ x: parseInt(day.epoch, 10), y: day.alarm }); superv_series.data.push({ x: parseInt(day.epoch, 10), y: day.superv }); trouble_series.data.push({ x: parseInt(day.epoch, 10), y: day.trouble }); monitor_series.data.push({ x: parseInt(day.epoch, 10), y: day.monitor }); message_series.data.push({ x: parseInt(day.epoch, 10), y: day.message }); }); chart_events.addSeries(alarm_series); chart_events.addSeries(superv_series); chart_events.addSeries(trouble_series); chart_events.addSeries(monitor_series); chart_events.addSeries(message_series); $scope.equipment.count.alarm = 2; $scope.equipment.count.supervisory = 0; $scope.equipment.count.trouble = 5; $scope.equipment.count.monitor = 3; $scope.equipment.count.messages = 0; let total_event_count = $scope.equipment.count.alarm + $scope.equipment.count.supervisory + $scope.equipment.count.trouble + $scope.equipment.count.monitor + $scope.equipment.count.messages; let pie_series = { type: "pie", data: [] }; pie_series.data.push({tooltipcolor:'#ff2400', name: 'ALARM', y:(($scope.equipment.count.alarm/total_event_count)*100)}); pie_series.data.push({tooltipcolor:'#8e44ad', name: 'SUPERVISORY', y:(($scope.equipment.count.supervisory/total_event_count)*100)}); pie_series.data.push({tooltipcolor:'#f1c40f', name: 'TROUBLE', y:(($scope.equipment.count.trouble/total_event_count)*100)}); pie_series.data.push({tooltipcolor:'#2980b9', name: 'MONITOR', y:(($scope.equipment.count.monitor/total_event_count)*100)}); pie_series.data.push({tooltipcolor:'#93989e', name: 'MESSAGE', y:(($scope.equipment.count.messages/total_event_count)*100)}); pie_series.data.push({tooltipcolor:'#000000', name: 'NO EVENTS', y:(total_event_count>0?0:100)}); chart_pie.addSeries(pie_series); ////////////////////////////////////////// }); } //////////////////////////////////////////////////////////////////////////////////////////////////////////// // CHART: FACP STATS OBJECT Highcharts.setOptions({ colors: ['#ff2400', '#8e44ad', '#f1c40f', '#2980b9', '#dfe4ea', '#dfe4ea'], time: { timezone: sysClock.getTimeZone() } }); Highcharts.dateFormats = { 'Z': () => { return moment.tz(sysClock.getTimeZone()).format('Z'); } } function loadChart() { return new Promise((resolve, reject) => { new Highcharts.Chart({ chart: { renderTo: 'dashboard_piechart', borderWidth: 0, plotBorderWidth: 0, type: 'pie', plotBackgroundColor: null, backgroundColor:'rgba(255,255,255,0.002)', height: '300px', // 200 width: null, spacing: [0, 0, 0, 0], margin: [0, 0, 0, 0], plotShadow: false }, title: {text: null}, subtitle: {text: null}, legend: {enabled: true}, exporting: {enabled: false}, credits: {enabled: false}, plotOptions: { pie: { dataLabels: { enabled: false, }, center: ['50%', '50%'] }, series: { innerSize: '50%', dataLabels: { enabled: false, format: '{point.name}: {point.y:.1f}%' } } }, tooltip: { headerFormat: null, pointFormat: '{point.name}: {point.y:.2f}% of total
', backgroundColor: '#fff' }, series: [{ data: null }] }, function(chart){ chart_pie = chart; //chart_load_daily_data(); // dafault $scope.chart_piechart_loading = false; }); new Highcharts.Chart({ chart: { renderTo: 'dashboard_events', height: 200, borderWidth: 0 }, time: { useUTC: true }, title: { text: null }, subtitle: { text: null }, exporting: { enabled: false }, credits: { enabled: false }, accessibility: { announceNewData: { enabled: true } }, xAxis: { type: 'datetime', tickInterval: 86400000, // 24 * 3600 * 1000 = 1 day }, yAxis: [{ // Events title: { text: 'Events', style: { whiteSpace: 'nowrap', } }, }], tooltip: { xDateFormat: '%b %e, %Y', shared: true }, legend: { enabled: true, margin: 0, padding: 0 }, plotOptions: { column: { stacking: 'normal', dataLabels: { enabled: false } }, series: { cursor: 'pointer', pointPadding: 0.01, groupPadding: 0.01, borderWidth: 0.01, shadow: false, dataLabels: { enabled: false }, marker: { enabled: false }, point: { events: { click: function() { //console.log('drilldown: ' + this.series.name + ' ['+this.x+'] -> ' + $scope.chart_events_drilldown); if(!$scope.chart_events_drilldown) { $scope.chart_events_drilldown = true; chart_load_hourly_data(this.x); } } } } } }, drilldown: { activeAxisLabelStyle: { cursor: 'default', color: '#3E576F', fontWeight: 'normal', textDecoration: 'none' }, activeDataLabelStyle: { cursor: 'default', color: '#3E576F', fontWeight: 'normal', textDecoration: 'none' } } }, function(chart){ chart_events = chart; chart_load_daily_data(); // dafault $scope.chart_events_loading = false; resolve(); }); }); } }]) .controller('EquipmentPowerSocketController', ['$rootScope', '$scope', '$http', '$location', '$timeout', '$filter', '$sessionStorage', '$mdPanel', '$mdToast', 'timerange', 'modalDialog', 'authService', 'CONFIG', '$stateParams', 'debounce', 'sysClock', function($rootScope, $scope, $http, $location, $timeout, $filter, $sessionStorage, $mdPanel, $mdToast, timerange, modalDialog, authService, CONFIG, $stateParams, debounce, sysClock, wiz){ $scope.issuper = $sessionStorage.currentUser.issuper; const modes = [ {id:0, name:'OFF', icon:'power_off'}, {id:1, name:'ON', icon:'power'} ]; /////////////////////////////////////////////////////////////////////////////////////// $scope.tempscale = $sessionStorage.currentUser.temperature; function F2C(temp) { return temp !== null ? (parseFloat(((temp - 32)*(5/9)).toFixed(2))) : temp; } // function covering Fahrenheit to Celsius value function C2F(temp) { return temp !== null ? (parseFloat(((temp * (9/5))+32).toFixed(2))) : temp; } // function covering Celsius to Fahrenheit value const tempConvert = temp => { switch($sessionStorage.currentUser.temperature) { case 'F': return (temp !== null) ? C2F(temp) : null; case 'C': return (temp !== null) ? F2C(temp) : null; } }; $scope.$on('tempUpdate', function(event, payload) { // time range update event console.log('tempUpdate event: ' + $sessionStorage.currentUser.temperature); $scope.tempscale = $sessionStorage.currentUser.temperature; if(chart_consumption) { // check if cart object is created // recalculate tempetarure series data const oldSeriesData = []; chart_consumption.series.forEach(series => { if(series.name === 'Device Temperature') { oldSeriesData.push(series.userOptions.data); const newSeriesData = oldSeriesData[0].map(point => { return { x: point.x, y: tempConvert(point.y) } }); series.update({ tooltip: { valueSuffix: '°' + $sessionStorage.currentUser.temperature }, data: newSeriesData }); } }); // change Temperature yAxis title & tooltip suffix chart_consumption.yAxis[1].update({ title: { text: 'Device Temperature °' + $sessionStorage.currentUser.temperature }, labels: { format: (($sessionStorage.currentUser.temperature === 'F') ? '{value}°F' : '{value}°C') } }); } }); /////////////////////////////////////////////////////////////////////////////////////// var time = timerange.calc($sessionStorage.currentUser.timerange); $scope.$on('trUpdate', function(event, payload) { // time range update event console.log('trUpdate event: ' + $sessionStorage.currentUser.timerange); time = timerange.calc($sessionStorage.currentUser.timerange); load_data().catch(error => { console.warn(error); }); }); /////////////////////////////////////////////////////////////////////////////////////// $scope.$on('clockUpdate', function(event, payload) { // time range update event console.log('clockUpdate event: ' + sysClock.getTimeZone()); Highcharts.setOptions({ colors: ['#192a56', '#00b894', '#ff4757'], time: { timezone: sysClock.getTimeZone() } }); if($scope.chart_consumption_drilldown) { // if data is shown in hourly graph chart_load_hourly_data($scope.chart_consumption_drilldown_timestamp); } else { // if data is shown in daily graph chart_load_daily_data(); } }); /////////////////////////////////////////////////////////////////////////////////////// $scope.loadModes = () => { let modeList = modes; return modeList; }; $scope.setMode = (mode_id) => { $scope.settings = $filter('filter')(modes, {id: mode_id})[0]; $scope.changeSettings(); }; $rootScope.socket_settings_changed = false; $scope.changeSettings = () => { console.log('changeSettings...'); $rootScope.socket_settings_changed = true; }; $scope.$on('PowerSocketChangeSettingsApply', (payload) => { applyChangeSettings(); }); $scope.$on('PowerSocketChangeSettingsCancel', (payload) => { cancelChangeSettings(); }); $rootScope.request_ongoing = false; var applyChangeSettings = () => { console.log('applyChangeSettings...'); $rootScope.request_ongoing = true; console.log('request equipment.id: ', $scope.equipment.id); console.log('request type: ', $scope.settings.name.toLowerCase()); modalDialog.showDialog({ 'template':'modal.codeconfirmation.html', 'controller':'modalCodeConfirmationController', 'subtitle': ("TURNING APPLIANCE " + $scope.settings.name.toUpperCase()), 'message': [] }).then( // acknowledge function(confirmationcode){ if(confirmationcode!==null) { authService.getJWTAuth().then(authHeader => { var request = {'query': 'mutation { ' + 'equipmentsocketchange ( ' + 'passcode: "' + confirmationcode + '", ' + 'equipment_id: "' + $scope.equipment.id + '", ' + 'type: "' + $scope.settings.name.toLowerCase() + '" ' + '){id, created, status}' + '}' }; console.log('request:', request); $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve console.log(response); if(response.data.data.equipmentsocketchange.status != 'failed') { $rootScope.request_ongoing = true; let request = response.data.data.equipmentsocketchange; $scope.equipment.request = request; $scope.equipment.requested = { id: request.id, settings: $scope.settings }; console.log('request pending:', $scope.equipment); setTimeout(() => { // check status after 1min - if still ongoing then mark as failed and unlock UI if($scope.equipment.request.status === 'new' || $scope.equipment.request.status === 'waiting') { console.log('request failed', $scope.equipment.request); $scope.equipment.request.status = 'failed'; } $rootScope.request_ongoing = false; }, 60000); } else { $scope.equipment.request.status = 'failed'; $rootScope.request_ongoing = false; $mdToast.show( $mdToast.simple() .toastClass('md-toast-error') .textContent('Request Failed') .position('bottom right') .hideDelay(3000) ) .then(function() { console.log('Toast dismissed.'); }).catch(function() { console.log('Toast failed or was forced to close early by another toast.'); }); } },function(error) { // failure console.error(error); } ); }); } } ); $rootScope.socket_settings_changed = false; }; var cancelChangeSettings = () => { console.log('cancelChangeSettings...'); $rootScope.socket_settings_changed = false; $rootScope.request_ongoing = false; //$scope.settings = $filter('filter')(modes, {id: 1})[0]; // ON MODE }; $scope.$on('socket:state', function(event, payload) { // socket state update event if($scope.equipment.id === payload.id) { $scope.equipment.state.updated = payload.state.updated; $scope.equipment.state.status = payload.state.status; $scope.equipment.state.power = payload.state.power; $scope.equipment.state.current = payload.state.current; $scope.equipment.state.voltage = payload.state.voltage; $scope.equipment.state.temperature = payload.state.temperature; $scope.equipment.state.rssi = payload.state.rssi; } }); $scope.$on('socket:request', function(event, payload) { // socket request update event if($scope.equipment.id === payload.id) { if($scope.equipment.request.id === payload.request.request) { if(payload.request.status) { $scope.equipment.request.status = payload.request.status; if($scope.equipment.request.status !== 'new' && $scope.equipment.request.status !== 'waiting') { $rootScope.request_ongoing = false; $scope.equipment.state.status = ($scope.equipment.requested.settings.name.toLowerCase()=='on'?'true':'false'); } } } } }); //////////////////////////////////////////////////////////////////////////////////////////////////////////// $scope.status = { display:'NO DATA', type:'nodata', voltage:null, current:null, power:null, temperature:null, rssi:null, rssi_level:null }; $scope.$watch('equipment.state', function() { console.log("**** equipment state changed ****", $scope.equipment.state); // STATUS if($scope.equipment.state) { switch($scope.equipment.state.status.toLowerCase()) { case 'true': // On Line $scope.status.display = 'ON'; $scope.status.type = 'normal'; $scope.settings = $filter('filter')(modes, {id: 1})[0]; // ON MODE break; case 'false': // Off $scope.status.display = 'OFF'; $scope.status.type = 'warning'; $scope.settings = $filter('filter')(modes, {id: 0})[0];// OFF MODE break; default: $scope.status.display = 'NO DATA'; $scope.status.type = 'nodata'; } // RSSI if($scope.equipment.state.rssi) { var strength = Math.abs(parseInt($scope.equipment.state.rssi)); if(strength<=55) { $scope.status.rssi_level = 'excellent'; } else if(55 { let time_start = moment(time.start).format('YYYY-MM-DDTHH:mm:ss'); let time_end = moment(time.today).format('YYYY-MM-DDTHH:mm:ss'); console.log('time_start: ' + time_start); console.log('time_end: ' + time_end); authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'equipment(id:"' + $scope.equipment.id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'id ' + 'name ' + 'status ' + 'company {' + 'id ' + '}' + 'location {' + 'id ' + '}' + 'room {' + 'id ' + '}' + 'type { ' + 'id ' + 'name ' + '} ' + 'brand { ' + 'id ' + 'name ' + '} ' + 'model { ' + 'id ' + 'name ' + '} ' + 'groups { ' + 'id ' + 'name ' + '} ' + 'request { ' + 'id ' + '} ' + '... on EquipmentSocket { ' + 'mac ' + 'ip ' + 'firmware ' + 'serial ' + 'state { ' + 'updated ' + 'status ' + 'power ' + 'current ' + 'voltage ' + 'temperature ' + 'rssi ' + '} ' + '} ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve if(response.data.data.equipment.length){ $scope.equipment = response.data.data.equipment[0]; console.log($scope.equipment); $scope.equipment.isvisible = true; console.log($scope.equipment); console.log('state:', $scope.equipment.state); $scope.equipment.breaker = { panel:'PANEL 1', number:'BREAKER 8' }; var last = moment.utc($scope.equipment.state.last_data).format('X'); var now = moment.utc(new Date()).format('X'); var duration = now - last; // diff in seconds $scope.equipment.state.outdated = false; if(duration > 3600) { // more than 1 hour $scope.equipment.state.outdated = true; // whenever there is no fresh data available } /* if(!$scope.equipment.state.status) { $scope.equipment.state.status_display = 'NO DATA'; $scope.equipment.state.status_type = 'nodata'; } else { switch($scope.equipment.state.status.toLowerCase()) { case 'true': // On Line $scope.equipment.state.status_display = 'ON'; $scope.equipment.state.status_type = 'normal'; $scope.settings = $filter('filter')(modes, {id: 1})[0]; // ON MODE break; case 'false': // Off $scope.equipment.state.status_display = 'OFF'; $scope.equipment.state.status_type = 'normal'; $scope.settings = $filter('filter')(modes, {id: 0})[0];// OFF MODE break; } } if($scope.equipment.state.rssi) { var strength = Math.abs(parseInt($scope.equipment.state.rssi)); if(strength<=55) { $scope.equipment.state.rssi_level = 'excellent'; } else if(55 { // create chart object resolve(null); }); } else { $scope.isLoading = false; // hide ajax loader } },function(error) { // failure console.error(error); reject(error); } ); }); }); } $timeout(function() { load_data().then(() => { // nothing }).catch(error => { console.warn(error); }); }, 100); //////////////////////////////////////////////////////////////////////////////////////////////////////////// // CHART: POWER CONSUMPTION DATA var chart_consumption = null; // chart object $scope.chart_consumption_loading = true; // turn on and off AJAX loader and show graph $scope.chart_consumption_drilldown = false; // false: graph shows data aggregated to 1d, true: aggregation to 1h $scope.chart_consumption_drilldown_timestamp = null; // if 1h aggregation is shown then this variable holds information about which day $scope.chart_drillup = () => { // exit from drilldown (back to 1d aggregation) chart_load_daily_data(); } const chart_clean = () => { // remove data servies (empty graph) while(chart_consumption.series.length) { chart_consumption.series[0].remove(); } } const chart_load_daily_data = () => { // prepare loading 1d aggregation data to graph $scope.chart_consumption_drilldown = false; // mark drolldown false $scope.chart_consumption_drilldown_timestamp = null; // remove drilldown timestamp chart_clean(); // remove data servies (empty graph) $scope.chart_consumption_title = 'Last ' + $sessionStorage.currentUser.timerange + ' Days Power Consumption'; // set graph title let tickInterval = 86400000; // 3600 * 24 * 1000 = 1 day (tickInterval = 1d in milliseconds) chart_consumption.xAxis.forEach((xaxis, i) => { // set graph X Axis (time:days) display range xaxis.options.tickInterval = tickInterval; xaxis.setExtremes(time.start, time.today, true); }); let time_start = moment.utc(time.start).format('YYYY-MM-DDTHH:mm:ss'); let time_end = moment.utc(time.today).format('YYYY-MM-DDTHH:mm:ss'); chart_add_power_daily(time_start, time_end); // execute data request and series prepare chart_add_temperature_daily(time_start, time_end); // execute data request and series prepare } const chart_load_hourly_data = (timestamp) => { // drilldown: 24h data $scope.chart_consumption_drilldown = true; // mark drolldown true $scope.chart_consumption_drilldown_timestamp = timestamp; // remember for which day we show drilldown chart_clean(); // remove data servies (empty graph) $scope.chart_consumption_title = moment.utc(timestamp).format('ll') + ' Power Consumption'; // set graph title let tickInterval = 3600000; // 3600 * 1000 = 1 hour chart_consumption.xAxis.forEach((xaxis, i) => { // set graph X Axis (time:hours) display range xaxis.options.tickInterval = tickInterval; xaxis.setExtremes(timestamp, (timestamp + (tickInterval * 23)), true); }); let time_start = moment.utc(timestamp).format('YYYY-MM-DDTHH:mm:ss'); let time_end = moment.utc(timestamp + (tickInterval * 24)).format('YYYY-MM-DDTHH:mm:ss'); chart_add_power_hourly(time_start, time_end); // execute data request and series prepare chart_add_temperature_hourly(time_start, time_end); // execute data request and series prepare } const chart_add_power_daily = (time_start, time_end) => { let power_series = { name: "Energy Consumption", type: "column", marker: { symbol: 'circle' }, yAxis: 0, zIndex: 1, tooltip: { valueSuffix: 'kWh' }, color: '#51c4d3', data: [] }; authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'power_daily(object:"' + $stateParams.equipment_id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'step ' + 'epoch ' + 'p_avg ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( (response) => { // resolve response.data.data.power_daily.forEach(day => { power_series.data.push({ x: parseInt(day.epoch, 10), y: day.p_avg }); }); chart_consumption.addSeries(power_series); },(error) => { // failure console.error(error); } ); }); }; const chart_add_temperature_daily = (time_start, time_end) => { let temperature_series = { name: "Device Temperature", type: "spline", marker: { symbol: 'circle' }, yAxis: 1, zIndex: 2, tooltip: { xDateFormat: '%b %e, %Y %l %p %Z', valueSuffix: '°' + $sessionStorage.currentUser.temperature }, color: '#d35151', data: [] }; authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'temperature_daily(object:"' + $stateParams.equipment_id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'step ' + 'epoch ' + 't_avg ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( (response) => { // resolve response.data.data.temperature_daily.forEach(day => { temperature_series.data.push({ x: parseInt(day.epoch, 10), y: ($sessionStorage.currentUser.temperature === 'F') ? tempConvert(day.t_avg) : day.t_avg }); }); chart_consumption.addSeries(temperature_series); },(error) => { // failure console.error(error); } ); }); }; const chart_add_power_hourly = (time_start, time_end) => { let power_series = { name: "Energy Consumption", type: "column", marker: { symbol: 'circle' }, yAxis: 0, zIndex: 1, tooltip: { xDateFormat: '%b %e, %Y %l %p %Z', valueSuffix: 'kWh' }, color: '#51c4d3', data: [], }; authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'power_hourly(object:"' + $stateParams.equipment_id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'step ' + 'epoch ' + 'p_avg ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( (response) => { // resolve response.data.data.power_hourly.forEach(hour => { power_series.data.push({ x: parseInt(hour.epoch, 10), y: hour.p_avg }); }); chart_consumption.addSeries(power_series); },(error) => { // failure console.error(error); } ); }); }; const chart_add_temperature_hourly = (time_start, time_end) => { let temperature_series = { name: "Device Temperature", type: "spline", marker: { symbol: 'circle' }, yAxis: 1, zIndex: 2, tooltip: { xDateFormat: '%b %e, %Y %l %p %Z', valueSuffix: '°' + $sessionStorage.currentUser.temperature }, color: '#d35151', data: [] }; authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'temperature_hourly(object:"' + $stateParams.equipment_id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'step ' + 'epoch ' + 't_avg ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( (response) => { // resolve response.data.data.temperature_hourly.forEach(hour => { temperature_series.data.push({ x: parseInt(hour.epoch, 10), y: ($sessionStorage.currentUser.temperature === 'F') ? tempConvert(hour.t_avg) : hour.t_avg }); }); chart_consumption.addSeries(temperature_series); },(error) => { // failure console.error(error); } ); }); }; //////////////////////////////////////////////////////////////////////////////////////////////////////////// // CHART: POWER CONSUMPTION OBJECT Highcharts.setOptions({ colors: ['#192a56', '#00b894', '#ff4757'], time: { timezone: sysClock.getTimeZone() } }); Highcharts.dateFormats = { 'Z': () => { return moment.tz(sysClock.getTimeZone()).format('Z'); } } const loadChart = () => { return new Promise((resolve, reject) => { new Highcharts.Chart({ chart: { renderTo: 'dashboard_consumption', height: 200, borderWidth: 0 }, time: { useUTC: true }, title: { text: null }, subtitle: { text: null }, exporting: { enabled: false }, credits: { enabled: false }, accessibility: { announceNewData: { enabled: true } }, xAxis: { type: 'datetime', tickInterval: 86400000, // 24 * 3600 * 1000 = 1 day }, yAxis: [{ // Power Consumption labels: { format: '{value}kWh', style: { color: Highcharts.getOptions().colors[0] } }, title: { text: 'Power Consumption', style: { color: Highcharts.getOptions().colors[0] } } },{ // Device Temperature labels: { format: (($sessionStorage.currentUser.temperature === 'F') ? '{value}°F' : '{value}°C'), style: { color: Highcharts.getOptions().colors[0] } }, title: { text: 'Device Temperature °' + $sessionStorage.currentUser.temperature, style: { color: Highcharts.getOptions().colors[0], whiteSpace: 'nowrap', } }, opposite: true }], tooltip: { xDateFormat: '%b %e, %Y', shared: true }, legend: { enabled: false }, plotOptions: { column: { stacking: 'normal', dataLabels: { enabled: false } }, series: { cursor: 'pointer', pointPadding: 0.01, groupPadding: 0.01, borderWidth: 0.01, shadow: false, dataLabels: { enabled: false }, marker: { enabled: false }, point: { events: { click: function() { //console.log('drilldown: ' + this.series.name + ' ['+this.x+'] -> ' + $scope.chart_consumption_drilldown); if(!$scope.chart_consumption_drilldown) { $scope.chart_consumption_drilldown = true; chart_load_hourly_data(this.x); } } } } } }, drilldown: { activeAxisLabelStyle: { cursor: 'default', color: '#3E576F', fontWeight: 'normal', textDecoration: 'none' }, activeDataLabelStyle: { cursor: 'default', color: '#3E576F', fontWeight: 'normal', textDecoration: 'none' } } }, (chart) => { chart_consumption = chart; chart_load_daily_data(); // dafault $scope.chart_consumption_loading = false; resolve(); }); }); } }]) .controller('EquipmentGenericController', ['$rootScope', '$state', '$scope', '$http', '$location', '$timeout', '$filter', '$sessionStorage', '$mdPanel', '$mdToast', 'timerange', 'modalDialog', 'authService', 'CONFIG', '$stateParams', 'debounce', 'sysClock', function($rootScope, $state, $scope, $http, $location, $timeout, $filter, $sessionStorage, $mdPanel, $mdToast, timerange, modalDialog, authService, CONFIG, $stateParams, debounce, sysClock, wiz){ $scope.generic = true; $scope.issuper = $sessionStorage.currentUser.issuper; const modes = [ {id:0, name:'OFF', icon:'power_off'}, {id:1, name:'ON', icon:'power'} ]; /////////////////////////////////////////////////////////////////////////////////////// $scope.tempscale = $sessionStorage.currentUser.temperature; function F2C(temp) { return temp !== null ? (parseFloat(((temp - 32)*(5/9)).toFixed(2))) : temp; } // function covering Fahrenheit to Celsius value function C2F(temp) { return temp !== null ? (parseFloat(((temp * (9/5))+32).toFixed(2))) : temp; } // function covering Celsius to Fahrenheit value const tempConvert = temp => { switch($sessionStorage.currentUser.temperature) { case 'F': return (temp !== null) ? C2F(temp) : null; case 'C': return (temp !== null) ? F2C(temp) : null; } }; $scope.$on('tempUpdate', function(event, payload) { // time range update event console.log('tempUpdate event: ' + $sessionStorage.currentUser.temperature); $scope.tempscale = $sessionStorage.currentUser.temperature; if(chart_consumption) { // check if cart object is created // recalculate tempetarure series data const oldSeriesData = []; chart_consumption.series.forEach(series => { if(series.name === 'Device Temperature') { oldSeriesData.push(series.userOptions.data); const newSeriesData = oldSeriesData[0].map(point => { return { x: point.x, y: tempConvert(point.y) } }); series.update({ tooltip: { valueSuffix: '°' + $sessionStorage.currentUser.temperature }, data: newSeriesData }); } }); // change Temperature yAxis title & tooltip suffix chart_consumption.yAxis[1].update({ title: { text: 'Device Temperature °' + $sessionStorage.currentUser.temperature }, labels: { format: (($sessionStorage.currentUser.temperature === 'F') ? '{value}°F' : '{value}°C') } }); } }); /////////////////////////////////////////////////////////////////////////////////////// var time = timerange.calc($sessionStorage.currentUser.timerange); $scope.$on('trUpdate', function(event, payload) { // time range update event console.log('trUpdate event: ' + $sessionStorage.currentUser.timerange); time = timerange.calc($sessionStorage.currentUser.timerange); load_data().catch(error => { console.warn(error); }); }); /////////////////////////////////////////////////////////////////////////////////////// $scope.$on('clockUpdate', function(event, payload) { // time range update event console.log('clockUpdate event: ' + sysClock.getTimeZone()); Highcharts.setOptions({ colors: ['#192a56', '#00b894', '#ff4757'], time: { timezone: sysClock.getTimeZone() } }); if($scope.chart_consumption_drilldown) { // if data is shown in hourly graph chart_load_hourly_data($scope.chart_consumption_drilldown_timestamp); } else { // if data is shown in daily graph chart_load_daily_data(); } }); /////////////////////////////////////////////////////////////////////////////////////// $scope.loadEquipmentSocket = () => { authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'equipment_index(location_id:"' + $scope.equipment.location.id + '", type_id:7) { ' + 'id ' + 'name ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve $scope.socket = response.data.data.equipment_index; },function(error) { // failure console.error(error); } ); }); }; $scope.selectEquipmentSocket = () => { if($scope.equipment.socket) { var request = {'query': 'mutation { ' + 'equipmentsocketattach ( ' + 'company_id: "' + $scope.equipment.company.id + '", ' + 'equipment_id: "' + $scope.equipment.id + '", ' + 'socket_id: "' + $scope.equipment.socket.id + '" ' + ')' + '}' }; } else { var request = {'query': 'mutation { ' + 'equipmentsocketdetach ( ' + 'company_id: "' + $scope.equipment.company.id + '", ' + 'equipment_id: "' + $scope.equipment.id + '"' + ')' + '}' }; } authService.getJWTAuth().then(authHeader => { $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve console.log(response); $state.reload(); },function(error) { // failure console.error(error); } ); }); }; /////////////////////////////////////////////////////////////////////////////////////// $scope.loadModes = () => { let modeList = modes; return modeList; }; $scope.setMode = (mode_id) => { $scope.settings = $filter('filter')(modes, {id: mode_id})[0]; $scope.changeSettings(); }; $rootScope.generic_settings_changed = false; $scope.changeSettings = () => { console.log('changeSettings...'); $rootScope.generic_settings_changed = true; }; $scope.$on('GenericChangeSettingsApply', (payload) => { applyChangeSettings(); }); $scope.$on('GenericChangeSettingsCancel', (payload) => { cancelChangeSettings(); }); $rootScope.request_ongoing = false; var applyChangeSettings = () => { console.log('applyChangeSettings...'); $rootScope.request_ongoing = true; console.log('request equipment.id: ', $scope.equipment.id); console.log('request type: ', $scope.settings.name.toLowerCase()); modalDialog.showDialog({ 'template':'modal.codeconfirmation.html', 'controller':'modalCodeConfirmationController', 'subtitle': ("TURNING APPLIANCE " + $scope.settings.name.toUpperCase()), 'message': [] }).then( // acknowledge function(confirmationcode){ if(confirmationcode!==null) { authService.getJWTAuth().then(authHeader => { var request = {'query': 'mutation { ' + 'equipmentsocketchange ( ' + 'passcode: "' + confirmationcode + '", ' + 'equipment_id: "' + $scope.equipment.socket.id + '", ' + 'type: "' + $scope.settings.name.toLowerCase() + '" ' + '){id, created, status}' + '}' }; console.log('request:', request); $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve console.log(response); if(response.data.data.equipmentsocketchange.status != 'failed') { $rootScope.request_ongoing = true; let request = response.data.data.equipmentsocketchange; $scope.equipment.request = request; $scope.equipment.requested = { id: request.id, settings: $scope.settings }; console.log('request pending:', $scope.equipment); setTimeout(() => { // check status after 1min - if still ongoing then mark as failed and unlock UI if($scope.equipment.request.status === 'new' || $scope.equipment.request.status === 'waiting') { console.log('request failed', $scope.equipment.request); $scope.equipment.request.status = 'failed'; } $rootScope.request_ongoing = false; }, 60000); } else { $scope.equipment.request.status = 'failed'; $rootScope.request_ongoing = false; $mdToast.show( $mdToast.simple() .toastClass('md-toast-error') .textContent('Request Failed') .position('bottom right') .hideDelay(3000) ) .then(function() { console.log('Toast dismissed.'); }).catch(function() { console.log('Toast failed or was forced to close early by another toast.'); }); } },function(error) { // failure console.error(error); } ); }); } } ); $rootScope.generic_settings_changed = false; }; var cancelChangeSettings = () => { console.log('cancelChangeSettings...'); $rootScope.generic_settings_changed = false; $rootScope.request_ongoing = false; }; $scope.$on('socket:state', function(event, payload) { // socket state update event if($scope.equipment.socket.id === payload.id) { $scope.equipment.socket.state.updated = payload.state.updated; $scope.equipment.socket.state.status = payload.state.status; $scope.equipment.socket.state.power = payload.state.power; $scope.equipment.socket.state.current = payload.state.current; $scope.equipment.socket.state.voltage = payload.state.voltage; $scope.equipment.socket.state.temperature = payload.state.temperature; $scope.equipment.socket.state.rssi = payload.state.rssi; } }); $scope.$on('socket:request', function(event, payload) { // socket request update event if($scope.equipment.socket.id === payload.id) { if($scope.equipment.request.id === payload.request.request) { if(payload.request.status) { $scope.equipment.request.status = payload.request.status; if($scope.equipment.request.status !== 'new' && $scope.equipment.request.status !== 'waiting') { $rootScope.request_ongoing = false; $scope.equipment.socket.state.status = ($scope.equipment.requested.settings.name.toLowerCase()=='on'?'true':'false'); } } } } }); $scope.status = { display:'NO DATA', type:'nodata', voltage:null, current:null, power:null }; $scope.$watch('equipment.socket.state', function() { if($scope.equipment.socket) { console.log("**** equipment socket state changed ****", $scope.equipment.socket.state); // STATUS if($scope.equipment.socket.state) { switch($scope.equipment.socket.state.status.toLowerCase()) { case 'true': // On Line $scope.status.display = 'ON'; $scope.status.type = 'normal'; $scope.settings = $filter('filter')(modes, {id: 1})[0]; // ON MODE break; case 'false': // Off $scope.status.display = 'OFF'; $scope.status.type = 'warning'; $scope.settings = $filter('filter')(modes, {id: 0})[0];// OFF MODE break; default: $scope.status.display = 'NO DATA'; $scope.status.type = 'nodata'; } $scope.status.voltage = $scope.equipment.socket.state.voltage?$scope.equipment.socket.state.voltage:0; $scope.status.current = $scope.equipment.socket.state.current?$scope.equipment.socket.state.current:0; $scope.status.power = $scope.equipment.socket.state.power?$scope.equipment.socket.state.power:0; } } }, true); function load_data() { return new Promise((resolve, reject) => { let time_start = moment(time.start).format('YYYY-MM-DDTHH:mm:ss'); let time_end = moment(time.today).format('YYYY-MM-DDTHH:mm:ss'); console.log('time_start: ' + time_start); console.log('time_end: ' + time_end); authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'equipment(id:"' + $scope.equipment.id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'id ' + 'name ' + 'status ' + 'company {' + 'id ' + '}' + 'location {' + 'id ' + '}' + 'room {' + 'id ' + '}' + 'type { ' + 'id ' + 'name ' + '} ' + 'brand { ' + 'id ' + 'name ' + '} ' + 'model { ' + 'id ' + 'name ' + '} ' + 'groups { ' + 'id ' + 'name ' + '} ' + 'request { ' + 'id ' + '} ' + '... on EquipmentGeneric { ' + 'serial ' + 'socket { ' + 'id ' + 'name ' + 'state { ' + 'updated ' + 'status ' + 'power ' + 'current ' + 'voltage ' + '} ' + '} ' + '} ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve if(response.data.data.equipment.length){ $scope.equipment = response.data.data.equipment[0]; $scope.equipment.isvisible = true; console.log($scope.equipment); console.log('state:', $scope.equipment.socket.state); // HARDCODE //////////////////////////////////////// $scope.equipment.breaker = { panel:'PANEL 1', number:'BREAKER 8' }; $scope.equipment.barcode = 'LF00100019'; //////////////////////////////////////////////////// var last = moment.utc($scope.equipment.socket.state.last_data).format('X'); var now = moment.utc(new Date()).format('X'); var duration = now - last; // diff in seconds if($scope.equipment.socket) { $scope.socket = new Array($scope.equipment.socket); } else { $scope.socket = new Array({id:null, name:'NONE'}); } $scope.equipment.socket.state.outdated = false; if(duration > 3600) { // more than 1 hour $scope.equipment.socket.state.outdated = true; // whenever there is no fresh data available } /* if(!$scope.equipment.socket.state.status) { $scope.equipment.socket.state.status_display = 'NO DATA'; $scope.equipment.socket.state.status_type = 'nodata'; } else { switch($scope.equipment.socket.state.status.toLowerCase()) { case 'true': // On Line $scope.equipment.socket.state.status_display = 'ON'; $scope.equipment.socket.state.status_type = 'normal'; $scope.settings = $filter('filter')(modes, {id: 1})[0]; // ON MODE break; case 'false': // Off $scope.equipment.socket.state.status_display = 'OFF'; $scope.equipment.socket.state.status_type = 'normal'; $scope.settings = $filter('filter')(modes, {id: 0})[0];// OFF MODE break; } } $scope.equipment.socket.state.voltage = $scope.equipment.socket.state.voltage?$scope.equipment.socket.state.voltage:0; $scope.equipment.socket.state.current = $scope.equipment.socket.state.current?$scope.equipment.socket.state.current:0; $scope.equipment.socket.state.power = $scope.equipment.socket.state.power?$scope.equipment.socket.state.power:0; */ $scope.isLoading = false; // hide ajax loader loadChart().then(() => { // create chart object resolve(null); }); } else { $scope.isLoading = false; // hide ajax loader } },function(error) { // failure console.error(error); reject(error); } ); }); }); } $timeout(function() { load_data().then(() => { // nothing }).catch(error => { console.warn(error); }); }, 100); //////////////////////////////////////////////////////////////////////////////////////////////////////////// // CHART: POWER CONSUMPTION DATA var chart_consumption = null; // chart object $scope.chart_consumption_loading = true; // turn on and off AJAX loader and show graph $scope.chart_consumption_drilldown = false; // false: graph shows data aggregated to 1d, true: aggregation to 1h $scope.chart_consumption_drilldown_timestamp = null; // if 1h aggregation is shown then this variable holds information about which day $scope.chart_drillup = () => { // exit from drilldown (back to 1d aggregation) chart_load_daily_data(); } const chart_clean = () => { // remove data servies (empty graph) while(chart_consumption.series.length) { chart_consumption.series[0].remove(); } } const chart_load_daily_data = () => { // prepare loading 1d aggregation data to graph $scope.chart_consumption_drilldown = false; // mark drolldown false $scope.chart_consumption_drilldown_timestamp = null; // remove drilldown timestamp chart_clean(); // remove data servies (empty graph) $scope.chart_consumption_title = 'Last ' + $sessionStorage.currentUser.timerange + ' Days Power Consumption'; // set graph title let tickInterval = 86400000; // 3600 * 24 * 1000 = 1 day (tickInterval = 1d in milliseconds) chart_consumption.xAxis.forEach((xaxis, i) => { // set graph X Axis (time:days) display range xaxis.options.tickInterval = tickInterval; xaxis.setExtremes(time.start, time.today, true); }); let time_start = moment.utc(time.start).format('YYYY-MM-DDTHH:mm:ss'); let time_end = moment.utc(time.today).format('YYYY-MM-DDTHH:mm:ss'); chart_add_power_daily(time_start, time_end); // execute data request and series prepare } const chart_load_hourly_data = (timestamp) => { // drilldown: 24h data $scope.chart_consumption_drilldown = true; // mark drolldown true $scope.chart_consumption_drilldown_timestamp = timestamp; // remember for which day we show drilldown chart_clean(); // remove data servies (empty graph) $scope.chart_consumption_title = moment.utc(timestamp).format('ll') + ' Power Consumption'; // set graph title let tickInterval = 3600000; // 3600 * 1000 = 1 hour chart_consumption.xAxis.forEach((xaxis, i) => { // set graph X Axis (time:hours) display range xaxis.options.tickInterval = tickInterval; xaxis.setExtremes(timestamp, (timestamp + (tickInterval * 23)), true); }); let time_start = moment.utc(timestamp).format('YYYY-MM-DDTHH:mm:ss'); let time_end = moment.utc(timestamp + (tickInterval * 24)).format('YYYY-MM-DDTHH:mm:ss'); chart_add_power_hourly(time_start, time_end); // execute data request and series prepare } const chart_add_power_daily = (time_start, time_end) => { let power_series = { name: "Energy Consumption", type: "column", marker: { symbol: 'circle' }, yAxis: 0, zIndex: 1, tooltip: { valueSuffix: 'kWh' }, color: '#51c4d3', data: [] }; authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'power_daily(object:"' + $scope.equipment.socket.id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'step ' + 'epoch ' + 'p_avg ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( (response) => { // resolve response.data.data.power_daily.forEach(day => { power_series.data.push({ x: parseInt(day.epoch, 10), y: day.p_avg }); }); chart_consumption.addSeries(power_series); },(error) => { // failure console.error(error); } ); }); }; const chart_add_power_hourly = (time_start, time_end) => { let power_series = { name: "Energy Consumption", type: "column", marker: { symbol: 'circle' }, yAxis: 0, zIndex: 1, tooltip: { xDateFormat: '%b %e, %Y %l %p %Z', valueSuffix: 'kWh' }, color: '#51c4d3', data: [], }; authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'power_hourly(object:"' + $scope.equipment.socket.id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'step ' + 'epoch ' + 'p_avg ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( (response) => { // resolve response.data.data.power_hourly.forEach(hour => { power_series.data.push({ x: parseInt(hour.epoch, 10), y: hour.p_avg }); }); chart_consumption.addSeries(power_series); },(error) => { // failure console.error(error); } ); }); }; //////////////////////////////////////////////////////////////////////////////////////////////////////////// // CHART: POWER CONSUMPTION OBJECT Highcharts.setOptions({ colors: ['#192a56', '#00b894', '#ff4757'], time: { timezone: sysClock.getTimeZone() } }); Highcharts.dateFormats = { 'Z': () => { return moment.tz(sysClock.getTimeZone()).format('Z'); } } const loadChart = () => { return new Promise((resolve, reject) => { new Highcharts.Chart({ chart: { renderTo: 'dashboard_consumption', height: 200, borderWidth: 0 }, time: { useUTC: true }, title: { text: null }, subtitle: { text: null }, exporting: { enabled: false }, credits: { enabled: false }, accessibility: { announceNewData: { enabled: true } }, xAxis: { type: 'datetime', tickInterval: 86400000, // 24 * 3600 * 1000 = 1 day }, yAxis: [{ // Power Consumption labels: { format: '{value}kWh', style: { color: Highcharts.getOptions().colors[0] } }, title: { text: 'Power Consumption', style: { color: Highcharts.getOptions().colors[0] } } }], tooltip: { xDateFormat: '%b %e, %Y', shared: true }, legend: { enabled: false }, plotOptions: { column: { stacking: 'normal', dataLabels: { enabled: false } }, series: { cursor: 'pointer', pointPadding: 0.01, groupPadding: 0.01, borderWidth: 0.01, shadow: false, dataLabels: { enabled: false }, marker: { enabled: false }, point: { events: { click: function() { //console.log('drilldown: ' + this.series.name + ' ['+this.x+'] -> ' + $scope.chart_consumption_drilldown); if(!$scope.chart_consumption_drilldown) { $scope.chart_consumption_drilldown = true; chart_load_hourly_data(this.x); } } } } } }, drilldown: { activeAxisLabelStyle: { cursor: 'default', color: '#3E576F', fontWeight: 'normal', textDecoration: 'none' }, activeDataLabelStyle: { cursor: 'default', color: '#3E576F', fontWeight: 'normal', textDecoration: 'none' } } }, (chart) => { chart_consumption = chart; chart_load_daily_data(); // dafault $scope.chart_consumption_loading = false; resolve(); }); }); } }]) .controller('system.EquipmentController.view', ['$window', '$rootScope', '$state', '$scope', '$http', '$location', '$timeout', '$filter', '$sessionStorage', '$mdPanel', '$mdDialog', '$mdMenu', 'timerange', 'modalDialog', 'authService', 'CONFIG', '$stateParams', 'debounce', 'sysClock', function($window, $rootScope, $state, $scope, $http, $location, $timeout, $filter, $sessionStorage, $mdPanel, $mdDialog, $mdMenu, timerange, modalDialog, authService, CONFIG, $stateParams, debounce, sysClock, wiz){ // set #content div margin if($sessionStorage.currentUser !== undefined) { if($sessionStorage.currentUser.smallMenu) { $("#content").css('margin-left', 50); } } $scope.isLoading = true; // show ajax loader $scope.ismanager = $sessionStorage.currentUser.ismanager; $scope.issuper = $sessionStorage.currentUser.issuper; let time = timerange.calc($sessionStorage.currentUser.timerange); let time_start = moment(time.start).format('YYYY-MM-DDTHH:mm:ss'); let time_end = moment(time.today).format('YYYY-MM-DDTHH:mm:ss'); console.log('time_start: ' + time_start); console.log('time_end: ' + time_end); authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'equipment(id:"' + $stateParams.equipment_id + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'id ' + 'name ' + 'brand { ' + 'id ' + 'name ' + '} ' + 'model { ' + 'id ' + 'name ' + '} ' + 'type { ' + 'id ' + 'name ' + '} ' + 'room { ' + 'id ' + '} ' + '... on EquipmentUPS { ' + 'serial ' + 'ip ' + 'mac ' + '} ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve console.log(response); if(response.data.data.equipment.length){ $scope.equipment = response.data.data.equipment[0]; $scope.equipment.isvisible = true; $scope.isLoading = false; if(parseInt($scope.equipment.type.id)>7) { $scope.equipment.type.generic = true; } else { $scope.equipment.type.generic = false; } } },function(error) { // failure console.error(error); reject(error); } ); }); $scope.getRequestOngoing = () => { return $rootScope.request_ongoing; }; $scope.getSettingsChanged = () => { return $rootScope.light_settings_changed||$rootScope.nt_settings_changed||$rootScope.facp_settings_changed||$rootScope.socket_settings_changed||$rootScope.generic_settings_changed; }; $scope.applyChangeSettings = () => { if($rootScope.light_settings_changed) { $rootScope.$broadcast('LightChangeSettingsApply', {}); } if($rootScope.nt_settings_changed) { $rootScope.$broadcast('NTChangeSettingsApply', {}); } if($rootScope.facp_settings_changed) { $rootScope.$broadcast('FACPChangeSettingsApply', {}); } if($rootScope.socket_settings_changed) { $rootScope.$broadcast('PowerSocketChangeSettingsApply', {}); } if($rootScope.generic_settings_changed) { $rootScope.$broadcast('GenericChangeSettingsApply', {}); } }; $scope.cancelChangeSettings = () => { if($rootScope.light_settings_changed) { $rootScope.$broadcast('LightChangeSettingsCancel', {}); } if($rootScope.nt_settings_changed) { $rootScope.$broadcast('NTChangeSettingsCancel', {}); } if($rootScope.facp_settings_changed) { $rootScope.$broadcast('FACPChangeSettingsCancel', {}); } if($rootScope.socket_settings_changed) { $rootScope.$broadcast('PowerSocketChangeSettingsCancel', {}); } if($rootScope.generic_settings_changed) { $rootScope.$broadcast('GenericChangeSettingsCancel', {}); } }; $scope.back = () => { //$location.path('/system/rooms/' + $scope.equipment.room.id); $window.history.back(); }; $scope.editDevice = () => { console.log('editDevice', $scope.equipment); modalDialog.showDialog({ 'template':'modal.editdevice.html', 'controller':'modalEditEquipmentController', 'id': $scope.equipment.id, 'name': $scope.equipment.name, 'serial': $scope.equipment.serial, 'ip': $scope.equipment.ip, 'mac': $scope.equipment.mac }).then( // acknowledge function(device){ if(device!==null) { authService.getJWTAuth().then(authHeader => { var request = {'query': 'mutation { ' + 'equipmentupdate ( ' + 'equipment_id: "' + device.id + '", ' + 'name: "' + device.name + '", ' + 'serial: "' + device.serial + '" ' + 'ip: "' + device.ip + '" ' + 'mac: "' + device.mac + '" ' + ') { ... on Device { id } }' + '}' }; console.log('request', request); $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(device){ // resolve $state.reload(); },function(error) { // failure console.error(error); } ); }); } } ) }; $scope.runUPSTest = () => { console.log('runUPSTest'); modalDialog.showDialog({ 'template':'modal.codeconfirmation.html', 'controller':'modalCodeConfirmationController', 'subtitle': "UPS DISCHARGE TEST", 'message': ["Frequent UPS discharge tests can degrade the battery rapidly!","Limit tests to extend battery life and ensure optimal performance"] }).then( function(confirmationcode){ console.log('confirmationcode', confirmationcode); if(confirmationcode!==null) { } } ) /* if(parseInt($scope.equipment.state.battery_charge)===100) { authService.getJWTAuth().then(authHeader => { var request = {'query': 'mutation { ' + 'equipmentupsreport( ' + 'equipment_id: "' + $scope.equipment.id + '", ' + 'type: "bms.ups.discharge"' + '){id, created, status}' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve console.log(response); $scope.equipment.state.test_running = true; },function(error) { // failure console.error(error); } ); }); } */ }; $scope.abortUPSTest = () => { console.log('aborttest'); /* authService.getJWTAuth().then(authHeader => { var request = {'query': 'mutation { ' + 'equipmentupsrequestabort( ' + 'equipment_id: "' + $scope.equipment.id + '", ' + 'request_id: "' + $scope.equipment.request.id + '"' + '){id, created, status}' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve console.log(response); // now new request should be blocked by 5 min.. till we get update on UPS charge level $scope.equipment.state.test_running = false; },function(error) { // failure console.error(error); } ); }); */ }; $scope.showUPSReports = () => { console.log('reports'); modalDialog.showDialog({ 'template':'modal.files.html', 'controller':'modalReportController', 'device': $scope.equipment.id }).then( function(){ } ) }; }]); })();