aboutsummaryrefslogtreecommitdiff
path: root/web/static/app/diller
diff options
context:
space:
mode:
authorTrygve Laugstøl <trygvis@inamo.no>2016-03-05 16:47:32 +0100
committerTrygve Laugstøl <trygvis@inamo.no>2016-03-05 16:47:32 +0100
commit7ca173de3de046501d79164da0c8c8871a03089b (patch)
tree16d857cf2ab7fd8b7b3c29efbacd6b01c2eacec7 /web/static/app/diller
parentdda9ef2ae7971bceaa792e328c8489cb0695b77e (diff)
downloaddiller-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.js72
-rw-r--r--web/static/app/diller/global.js60
-rw-r--r--web/static/app/diller/line-chart.js62
-rw-r--r--web/static/app/diller/web.js236
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);
+})();