diff options
author | Trygve Laugstøl <trygvis@inamo.no> | 2016-03-05 16:47:32 +0100 |
---|---|---|
committer | Trygve Laugstøl <trygvis@inamo.no> | 2016-03-05 16:47:32 +0100 |
commit | 7ca173de3de046501d79164da0c8c8871a03089b (patch) | |
tree | 16d857cf2ab7fd8b7b3c29efbacd6b01c2eacec7 /web/static/app/diller | |
parent | dda9ef2ae7971bceaa792e328c8489cb0695b77e (diff) | |
download | diller-server-7ca173de3de046501d79164da0c8c8871a03089b.tar.gz diller-server-7ca173de3de046501d79164da0c8c8871a03089b.tar.bz2 diller-server-7ca173de3de046501d79164da0c8c8871a03089b.tar.xz diller-server-7ca173de3de046501d79164da0c8c8871a03089b.zip |
web:
o Adding an API method to get per-hour aggregate values. Doesn't use the by_hour table yet.
o Adding a simple line graph component that can graph a single property's value.
Diffstat (limited to 'web/static/app/diller')
-rw-r--r-- | web/static/app/diller/client.js | 72 | ||||
-rw-r--r-- | web/static/app/diller/global.js | 60 | ||||
-rw-r--r-- | web/static/app/diller/line-chart.js | 62 | ||||
-rw-r--r-- | web/static/app/diller/web.js | 236 |
4 files changed, 430 insertions, 0 deletions
diff --git a/web/static/app/diller/client.js b/web/static/app/diller/client.js new file mode 100644 index 0000000..6641ffb --- /dev/null +++ b/web/static/app/diller/client.js @@ -0,0 +1,72 @@ +(function () { + function extractData(res) { + return res && res.data; + } + + function Property($http, DillerRpc, propertyId) { + function getInterval(interval) { + + // moment().subtract(24, 'hour') + var req = DillerRpc.getValuesReq(propertyId); + req.params = { + from: interval.getFrom().toISOString() + }; + return $http(req).then(extractData); + } + + /** @lends Property.prototype */ + return { + getInterval: getInterval + }; + } + + function Device($http, DillerRpc, deviceId) { + var properties = {}; + + /** + * @param propertyId + * @returns {Property} + */ + function getProperty(propertyId) { + var p = properties[propertyId]; + + if (!p) { + p = new Property($http, DillerRpc, propertyId); + properties[propertyId] = p; + } + + return p; + } + + /** @lends Device.prototype */ + return { + getProperty: getProperty + }; + } + + function DillerClient($timeout, $http, DillerRpc) { + + var devices = {}; + + function getDevice(deviceId) { + var d = devices[deviceId]; + + if (!d) { + d = new Device($http, DillerRpc, deviceId); + devices[deviceId] = d; + } + + return d; + } + + /** @lends DillerClient.prototype */ + return { + getDevice: getDevice + }; + } + + angular + .module('diller.client', []) + .service('DillerClient', DillerClient) + .service('DillerRpc', window.DillerRpc); +})(); diff --git a/web/static/app/diller/global.js b/web/static/app/diller/global.js new file mode 100644 index 0000000..0da8da7 --- /dev/null +++ b/web/static/app/diller/global.js @@ -0,0 +1,60 @@ +(function () { + var Diller = window.Diller = window.Diller || {}; + + function toDate(unknown) { + if (!unknown) { + return undefined; + } else if (moment.isMoment(unknown)) { + return unknown; + } else if (typeof unknown === 'string') { + unknown = unknown.trim(); + + if (unknown == '') { + return moment(); + } + + return moment(unknown, 'YYYY-MM-DDTHH:mm.sssZ'); + } else if (typeof unknown === 'string' || moment.isDate(unknown)) { + return moment(unknown); + } else { + return undefined; + } + } + + Diller.Interval = function (from, to) { + var f = toDate(from), + t = toDate(to); + + if (f.isAfter(t)) { + var tmp = f; + f = t; + t = tmp; + } + + return { + getFrom: function () { + return f || moment(); + }, + getTo: function () { + return t || moment(); + }, + toString: function () { + return 'yo' + } + }; + }; + + Diller.Interval.create = function (value) { + if (value instanceof Diller.Interval) { + return value; + } + + return new Diller.Interval.hours(24); + }; + + Diller.Interval.hours = function (hours) { + var to = moment(); + var from = to.subtract(hours, 'hours'); + return new Diller.Interval(from, to); + }; +})(); diff --git a/web/static/app/diller/line-chart.js b/web/static/app/diller/line-chart.js new file mode 100644 index 0000000..69b6f77 --- /dev/null +++ b/web/static/app/diller/line-chart.js @@ -0,0 +1,62 @@ +(function () { + + var isoFormat = 'YYYY-MM-DDTHH:mm:ss'; + + function DlLineChartDirective($timeout, DillerClient) { + var id_seq = 0; + + return { + restrict: 'E', + scope: { + device: '=', + property: '=', + value: '=', + interval: '=' + }, + replace: true, + template: '<div/>', + link: function (scope, element, attrs) { + var elementId = element.attr('id'); + if (!elementId) { + elementId = 'dl-line-chart-' + id_seq++; + element.attr('id', elementId); + } + + var deviceId = scope.device; + var propertyId = scope.property; + var interval = Diller.Interval.create(scope.interval); + var property = DillerClient.getDevice(deviceId).getProperty(propertyId); + + var options = { + axisX: { + showLabel: true, + showGrid: true, + labelInterpolationFnc: function (value, index) { + return index % 4 === 0 ? value.format('HH:mm') : null; + } + } + }; + + property.getInterval(interval).then(function (data) { + var avgs = _.pluck(data.values, 'avg'); + var timestamps = _.map(data.values, function (row) { + return moment(row.timestamp, isoFormat); + }); + + var chartData = { + labels: timestamps, + series: [ + avgs + ] + }; + + var chart = new Chartist.Line('#' + elementId, chartData, options); + }); + } + }; + } + + angular + .module('diller.line-chart', ['diller.client']) + .directive('dlLineChart', DlLineChartDirective); +})(); diff --git a/web/static/app/diller/web.js b/web/static/app/diller/web.js new file mode 100644 index 0000000..4b173ad --- /dev/null +++ b/web/static/app/diller/web.js @@ -0,0 +1,236 @@ +(function () { + var isoFormat = 'YYYY-MM-DDTHH:mm:ss'; + + function FrontPageController(devices) { + var ctrl = this; + + ctrl.devices = devices.data.devices; + } + + function DeviceController($uibModal, device, DillerRpc) { + var ctrl = this; + + ctrl.device = device.data.device; + + ctrl.propertyChunks = _(ctrl.device.properties).sortByAll(['name', 'key']).chunk(3).value(); + + ctrl.editDeviceAttribute = function (attributeName) { + var outer = ctrl; + $uibModal.open({ + controller: function ($uibModalInstance) { + var ctrl = this; + + ctrl.title = 'Edit device ' + attributeName; + ctrl.label = attributeName.substr(0, 1).toUpperCase() + attributeName.substr(1); + ctrl.value = outer.device[attributeName]; + + ctrl.update = function () { + DillerRpc.patchDevice(outer.device.id, {attribute: attributeName, value: ctrl.value}) + .then(function (res) { + outer.device = res.data.device; + $uibModalInstance.close({}); + }, function (res) { + ctrl.error = res.data.message; + }); + }; + }, + controllerAs: 'ctrl', + bindToController: true, + templateUrl: 'app/templates/edit-attribute.modal.html' + }); + } + } + + function PropertyController($timeout, $route, $uibModal, DillerRpc, device, values) { + var ctrl = this; + + function updateData(device) { + ctrl.device = device.data.device; + ctrl.property = _.find(ctrl.device.properties, {id: $route.current.params.propertyId}); + } + updateData(device); + ctrl.values = values.data.values; + + var refreshPromise; + ctrl.refresh = function () { + $timeout.cancel(refreshPromise); + refreshPromise = $timeout(function () { + ctrl.loading = true; + }, 200); + + DillerRpc.getValues($route.current.params.propertyId).then(function (res) { + ctrl.values = res.data.values; + ctrl.loading = false; + $timeout.cancel(refreshPromise); + }) + }; + + ctrl.editPropertyAttribute = function (attributeName) { + var outer = ctrl; + $uibModal.open({ + controller: function ($uibModalInstance) { + var ctrl = this; + + ctrl.title = 'Edit property ' + attributeName; + ctrl.label = attributeName.substr(0, 1).toUpperCase() + attributeName.substr(1); + ctrl.value = outer.property[attributeName]; + + ctrl.update = function () { + DillerRpc.patchProperty(outer.property.id, {attribute: attributeName, value: ctrl.value}) + .then(function (res) { + updateData(res); + $uibModalInstance.close({}); + }, function (res) { + ctrl.error = res.data.message; + }); + }; + }, + controllerAs: 'ctrl', + bindToController: true, + templateUrl: 'app/templates/edit-attribute.modal.html' + }); + }; + + ctrl.interval = Diller.Interval.hours(5); + + // ctrl.values24h = values24h.data.values; + + function chartist() { + var avgs = _.pluck(ctrl.values24h, 'avg'); + var timestamps = _.map(ctrl.values24h, function(row) { + var m = moment(row.timestamp, isoFormat); + return m.format('HH:mm'); + }); + + var data = { + labels: timestamps, + series: [ + avgs + ] + }; + + var options = { + axisX: { + showLabel: false, + showGrid: false + } + }; + +// options.lineSmooth = Chartist.Interpolation.cardinal({ +// fillHoles: true, +// }) + + options.axisX = { + showLabel: true, + showGrid: true, + labelInterpolationFnc: function(value, index) { + return index % 4 === 0 ? value : null; + } + }; + + var chartData = { + series: [[]], + labels: [] + }; + + // var chart = new Chartist.Line('#values-chart', chartData, options); + // console.log('chart', chart); +/* + $timeout(function() { + chartData = data; + // console.log(data); + var chart = new Chartist.Line('#values-chart', chartData, options); + console.log('chart', chart); + }); +*/ + } + + // chartist(); + } + + function TimestampFilter() { + return function (value) { + if (!value) { + return; + } + + return moment(value).startOf('second').fromNow(); + } + } + + function DlTimestampDirective() { + return { + restrict: 'E', + scope: { + value: '=' + }, + replace: true, + template: '<span title="{{value|date:\'medium\'}}">{{value|timestamp}}</span>' + }; + } + + function DlDotsDirective() { + return { + restrict: 'E', + scope: { + value: '=' + }, + replace: true, + template: '<span class="dl-dots"><span>.</span><span>.</span><span>.</span></span>\n' + }; + } + + function config($routeProvider) { + $routeProvider + .when('/', { + controller: FrontPageController, + controllerAs: 'ctrl', + templateUrl: 'app/templates/front-page.html', + resolve: { + devices: DillerRpcResolve.getDevices + } + }) + .when('/device/:deviceId', { + controller: DeviceController, + controllerAs: 'ctrl', + templateUrl: 'app/templates/device.html', + resolve: { + device: DillerRpcResolve.getDevice + } + }) + .when('/device/:deviceId/property/:propertyId', { + controller: PropertyController, + controllerAs: 'ctrl', + templateUrl: 'app/templates/property.html', + resolve: { + device: DillerRpcResolve.getDevice, + values: DillerRpcResolve.getValues + } + }) + .otherwise({ + redirectTo: '/' + }); + } + + function run($log) { + window.console = $log; + } + + function DillerConfig() { + var head = document.getElementsByTagName('head')[0]; + var base = head.getElementsByTagName('base')[0]; + var baseUrl = base.href.replace(/\/$/, ''); + return { + baseUrl: baseUrl + }; + } + + angular + .module('diller.web', ['ngRoute', 'ui.bootstrap', 'diller.line-chart']) + .config(config) + .run(run) + .filter('timestamp', TimestampFilter) + .directive('dlTimestamp', DlTimestampDirective) + .directive('dlDots', DlDotsDirective) + .service('DillerConfig', DillerConfig); +})(); |