Commit e9240700843dc127c83731b65cf970268f294269

Authored by Igor Kulikov
Committed by GitHub
2 parents ded769a3 3f0d947f

Merge pull request #211 from thingsboard/feature/TB-70

TB-70: RPC widgets - knob control
... ... @@ -36,6 +36,22 @@
36 36 "dataKeySettingsSchema": "{}\n",
37 37 "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":true,\"backgroundColor\":\"#010101\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"parseGpioStatusFunction\":\"return body[pin] === true;\",\"gpioStatusChangeRequest\":{\"method\":\"setGpioStatus\",\"paramsBody\":\"{\\n \\\"pin\\\": \\\"{$pin}\\\",\\n \\\"enabled\\\": \\\"{$enabled}\\\"\\n}\"},\"requestTimeout\":500,\"switchPanelBackgroundColor\":\"#b71c1c\",\"gpioStatusRequest\":{\"method\":\"getGpioStatus\",\"paramsBody\":\"{}\"},\"gpioList\":[{\"pin\":1,\"label\":\"GPIO 1\",\"row\":0,\"col\":0,\"_uniqueKey\":0},{\"pin\":2,\"label\":\"GPIO 2\",\"row\":0,\"col\":1,\"_uniqueKey\":1},{\"pin\":3,\"label\":\"GPIO 3\",\"row\":1,\"col\":0,\"_uniqueKey\":2}]},\"title\":\"RPC remote shell\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
38 38 }
  39 + },
  40 + {
  41 + "alias": "knob_control",
  42 + "name": "Knob Control",
  43 + "descriptor": {
  44 + "type": "rpc",
  45 + "sizeX": 5,
  46 + "sizeY": 4.5,
  47 + "resources": [],
  48 + "templateHtml": "<tb-knob ctx='ctx'></tb-knob>",
  49 + "templateCss": "",
  50 + "controllerScript": "self.onInit = function() {\n var scope = self.ctx.$scope;\n scope.ctx = self.ctx;\n}\n\nself.onResize = function() {\n if (self.ctx.resize) {\n self.ctx.resize();\n }\n}\n\nself.onDestroy = function() {\n}\n",
  51 + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"minValue\": {\n \"title\": \"Minimum value\",\n \"type\": \"number\",\n \"default\": 0\n },\n \"maxValue\": {\n \"title\": \"Maximum value\",\n \"type\": \"number\",\n \"default\": 100\n },\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"number\",\n \"default\": 50\n },\n \"title\": {\n \"title\": \"Knob title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"getValueMethod\": {\n \"title\": \"Get value method\",\n \"type\": \"string\",\n \"default\": \"getValue\"\n },\n \"setValueMethod\": {\n \"title\": \"Set value method\",\n \"type\": \"string\",\n \"default\": \"setValue\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout\",\n \"type\": \"number\",\n \"default\": 500\n }\n },\n \"required\": [\"minValue\", \"maxValue\", \"getValueMethod\", \"setValueMethod\", \"requestTimeout\"]\n },\n \"form\": [\n \"minValue\",\n \"maxValue\",\n \"initialValue\",\n \"getValueMethod\",\n \"setValueMethod\",\n \"title\",\n \"requestTimeout\"\n ]\n}",
  52 + "dataKeySettingsSchema": "{}\n",
  53 + "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"maxValue\":100,\"initialValue\":50,\"minValue\":0,\"title\":\"Knob control\"},\"title\":\"Knob Control\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}"
  54 + }
39 55 }
40 56 ]
41 57 }
\ No newline at end of file
... ...
... ... @@ -22,6 +22,8 @@ import thingsboardTimeseriesTableWidget from '../widget/lib/timeseries-table-wid
22 22 import thingsboardAlarmsTableWidget from '../widget/lib/alarms-table-widget';
23 23 import thingsboardEntitiesTableWidget from '../widget/lib/entities-table-widget';
24 24
  25 +import thingsboardRpcWidgets from '../widget/lib/rpc';
  26 +
25 27 import TbFlot from '../widget/lib/flot-widget';
26 28 import TbAnalogueLinearGauge from '../widget/lib/analogue-linear-gauge';
27 29 import TbAnalogueRadialGauge from '../widget/lib/analogue-radial-gauge';
... ... @@ -39,7 +41,7 @@ import thingsboardTypes from '../common/types.constant';
39 41 import thingsboardUtils from '../common/utils.service';
40 42
41 43 export default angular.module('thingsboard.api.widget', ['oc.lazyLoad', thingsboardLedLight, thingsboardTimeseriesTableWidget,
42   - thingsboardAlarmsTableWidget, thingsboardEntitiesTableWidget, thingsboardTypes, thingsboardUtils])
  44 + thingsboardAlarmsTableWidget, thingsboardEntitiesTableWidget, thingsboardRpcWidgets, thingsboardTypes, thingsboardUtils])
43 45 .factory('widgetService', WidgetService)
44 46 .name;
45 47
... ...
... ... @@ -811,14 +811,17 @@ export default function WidgetController($scope, $state, $timeout, $window, $ele
811 811 return (val - parseFloat( val ) + 1) >= 0;
812 812 }
813 813
814   - function formatValue(value, dec, units) {
  814 + function formatValue(value, dec, units, showZeroDecimals) {
815 815 if (angular.isDefined(value) &&
816 816 value !== null && isNumeric(value)) {
817 817 var formatted = Number(value);
818 818 if (angular.isDefined(dec)) {
819 819 formatted = formatted.toFixed(dec);
820 820 }
821   - formatted = (formatted * 1).toString();
  821 + if (!showZeroDecimals) {
  822 + formatted = (formatted * 1);
  823 + }
  824 + formatted = formatted.toString();
822 825 if (angular.isDefined(units) && units.length > 0) {
823 826 formatted += ' ' + units;
824 827 }
... ...
... ... @@ -95,6 +95,15 @@ export default class CanvasDigitalGauge extends canvasGauges.BaseGauge {
95 95 options.value = options.minValue;
96 96 }
97 97
  98 + if (options.gaugeType === 'donut') {
  99 + if (!options.donutStartAngle) {
  100 + options.donutStartAngle = 1.5 * Math.PI;
  101 + }
  102 + if (!options.donutEndAngle) {
  103 + options.donutEndAngle = options.donutStartAngle + 2 * Math.PI;
  104 + }
  105 + }
  106 +
98 107 var colorsCount = options.levelColors.length;
99 108 var inc = colorsCount > 1 ? (1 / (colorsCount - 1)) : 1;
100 109 options.colorsRange = [];
... ... @@ -232,6 +241,21 @@ export default class CanvasDigitalGauge extends canvasGauges.BaseGauge {
232 241 return this;
233 242 }
234 243
  244 + getValueColor() {
  245 + if (this.contextProgressClone) {
  246 + var color = this.contextProgressClone.currentColor;
  247 + if (!color) {
  248 + if (this.options.neonGlowBrightness) {
  249 + color = getProgressColor(0, this.options.neonColorsRange);
  250 + } else {
  251 + color = getProgressColor(0, this.options.colorsRange);
  252 + }
  253 + }
  254 + return color;
  255 + } else {
  256 + return '#000';
  257 + }
  258 + }
235 259 }
236 260
237 261 /* eslint-disable angular/document-service */
... ... @@ -473,7 +497,7 @@ function drawBackground(context, options) {
473 497 context.lineCap = 'round';
474 498 }
475 499 if (options.gaugeType === 'donut') {
476   - context.arc(context.barDimensions.Cx, context.barDimensions.Cy, context.barDimensions.Rm, 1.5 * Math.PI, 3.5 * Math.PI);
  500 + context.arc(context.barDimensions.Cx, context.barDimensions.Cy, context.barDimensions.Rm, options.donutStartAngle, options.donutEndAngle);
477 501 context.stroke();
478 502 } else if (options.gaugeType === 'arc') {
479 503 context.arc(context.barDimensions.Cx, context.barDimensions.Cy, context.barDimensions.Rm, Math.PI, 2*Math.PI);
... ... @@ -605,7 +629,7 @@ function getProgressColor(progress, colorsRange) {
605 629 }
606 630 }
607 631
608   -function drawArcGlow(context, Cx, Cy, Ri, Rm, Ro, color, progress, isDonut) {
  632 +function drawArcGlow(context, Cx, Cy, Ri, Rm, Ro, color, progress, isDonut, donutStartAngle, donutEndAngle) {
609 633 context.setLineDash([]);
610 634 var strokeWidth = Ro - Ri;
611 635 var blur = 0.55;
... ... @@ -623,7 +647,7 @@ function drawArcGlow(context, Cx, Cy, Ri, Rm, Ro, color, progress, isDonut) {
623 647 context.beginPath();
624 648 var e = 0.01 * Math.PI;
625 649 if (isDonut) {
626   - context.arc(Cx, Cy, Rm, 1.5 * Math.PI - e, 1.5 * Math.PI + 2 * Math.PI * progress + e);
  650 + context.arc(Cx, Cy, Rm, donutStartAngle - e, donutStartAngle + (donutEndAngle - donutStartAngle) * progress + e);
627 651 } else {
628 652 context.arc(Cx, Cy, Rm, Math.PI - e, Math.PI + Math.PI * progress + e);
629 653 }
... ... @@ -660,9 +684,9 @@ function drawBarGlow(context, startX, startY, endX, endY, color, strokeWidth, is
660 684 function drawProgress(context, options, progress) {
661 685 var neonColor;
662 686 if (options.neonGlowBrightness) {
663   - neonColor = getProgressColor(progress, options.neonColorsRange);
  687 + context.currentColor = neonColor = getProgressColor(progress, options.neonColorsRange);
664 688 } else {
665   - context.strokeStyle = getProgressColor(progress, options.colorsRange);
  689 + context.currentColor = context.strokeStyle = getProgressColor(progress, options.colorsRange);
666 690 }
667 691
668 692 let {barLeft, barRight, barTop, baseX, width, barBottom, Cx, Cy, Rm, Ro, Ri, strokeWidth} =
... ... @@ -682,10 +706,10 @@ function drawProgress(context, options, progress) {
682 706 context.strokeStyle = neonColor;
683 707 }
684 708 context.beginPath();
685   - context.arc(Cx, Cy, Rm, 1.5 * Math.PI, 1.5 * Math.PI + 2 * Math.PI * progress);
  709 + context.arc(Cx, Cy, Rm, options.donutStartAngle, options.donutStartAngle + (options.donutEndAngle - options.donutStartAngle) * progress);
686 710 context.stroke();
687 711 if (options.neonGlowBrightness && !options.isMobile) {
688   - drawArcGlow(context, Cx, Cy, Ri, Rm, Ro, neonColor, progress, true);
  712 + drawArcGlow(context, Cx, Cy, Ri, Rm, Ro, neonColor, progress, true, options.donutStartAngle, options.donutEndAngle);
689 713 }
690 714 } else if (options.gaugeType === 'arc') {
691 715 if (options.neonGlowBrightness) {
... ...
  1 +/*
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +import tbKnob from './knob.directive';
  18 +
  19 +export default angular.module('thingsboard.widgets.rpc', [
  20 + tbKnob
  21 +]).name;
... ...
  1 +/*
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +import './knob.scss';
  18 +
  19 +import CanvasDigitalGauge from './../CanvasDigitalGauge';
  20 +import tinycolor from 'tinycolor2';
  21 +
  22 +import { isNumber } from '../widget-utils';
  23 +
  24 +/* eslint-disable import/no-unresolved, import/default */
  25 +
  26 +import knobTemplate from './knob.tpl.html';
  27 +
  28 +/* eslint-enable import/no-unresolved, import/default */
  29 +
  30 +export default angular.module('thingsboard.widgets.rpc.knob', [])
  31 + .directive('tbKnob', Knob)
  32 + .name;
  33 +
  34 +/*@ngInject*/
  35 +function Knob() {
  36 + return {
  37 + restrict: "E",
  38 + scope: true,
  39 + bindToController: {
  40 + ctx: '='
  41 + },
  42 + controller: KnobController,
  43 + controllerAs: 'vm',
  44 + templateUrl: knobTemplate
  45 + };
  46 +}
  47 +
  48 +/*@ngInject*/
  49 +function KnobController($element, $scope, $document) {
  50 + let vm = this;
  51 +
  52 + vm.value = 0;
  53 + vm.error = '';
  54 +
  55 + var knob = angular.element('.knob', $element),
  56 + knobContainer = angular.element('#knob-container', $element),
  57 + knobTopPointerContainer = knob.find('.top-pointer-container'),
  58 + knobTopPointer = knob.find('.top-pointer'),
  59 + knobValueContainer = knob.find('.value-container'),
  60 + knobValue = knob.find('.knob-value'),
  61 + knobErrorContainer = knob.find('.error-container'),
  62 + knobError = knob.find('.knob-error'),
  63 + knobTitleContainer = knob.find('.title-container'),
  64 + knobTitle = knob.find('.knob-title'),
  65 + knobMinmaxContainer = knob.find('.minmax-container'),
  66 + minmaxLanel = knob.find('.minmax-label'),
  67 + textMeasure = knob.find('#text-measure'),
  68 + startDeg = -1,
  69 + currentDeg = 0,
  70 + rotation = 0,
  71 + lastDeg = 0,
  72 + moving = false;
  73 +
  74 + var minDeg = -45;
  75 + var maxDeg = 225;
  76 +
  77 + var canvasBarElement = angular.element('#canvasBar', $element);
  78 +
  79 + var levelColors = ['#19ff4b', '#ffff19', '#ff3232'];
  80 +
  81 + var canvasBar;
  82 +
  83 + $scope.$watch('vm.ctx', () => {
  84 + if (vm.ctx) {
  85 + init();
  86 + }
  87 + });
  88 +
  89 + function init() {
  90 +
  91 + vm.minValue = angular.isDefined(vm.ctx.settings.minValue) ? vm.ctx.settings.minValue : 0;
  92 + vm.maxValue = angular.isDefined(vm.ctx.settings.maxValue) ? vm.ctx.settings.maxValue : 100;
  93 + vm.title = angular.isDefined(vm.ctx.settings.title) ? vm.ctx.settings.title : '';
  94 +
  95 + vm.darkTheme = vm.ctx.settings.theme == 'dark';
  96 +
  97 + var canvasBarData = {
  98 + renderTo: canvasBarElement[0],
  99 + hideValue: true,
  100 + neonGlowBrightness: 0,
  101 + gaugeWidthScale: 0.4,
  102 + gaugeColor: 'rgba(0, 0, 0, 0)',
  103 + levelColors: levelColors,
  104 + minValue: vm.minValue,
  105 + maxValue: vm.maxValue,
  106 + gaugeType: 'donut',
  107 + dashThickness: 2,
  108 + donutStartAngle: 3/4*Math.PI,
  109 + donutEndAngle: 9/4*Math.PI,
  110 + animation: false
  111 + };
  112 +
  113 + canvasBar = new CanvasDigitalGauge(canvasBarData).draw();
  114 +
  115 + knob.on('click', (e) => {
  116 + if (moving) {
  117 + moving = false;
  118 + return false;
  119 + }
  120 + e.preventDefault();
  121 +
  122 + var offset = knob.offset();
  123 + var center = {
  124 + y : offset.top + knob.height()/2,
  125 + x: offset.left + knob.width()/2
  126 + };
  127 + var a, b, deg,
  128 + rad2deg = 180/Math.PI;
  129 +
  130 + e = (e.originalEvent.touches) ? e.originalEvent.touches[0] : e;
  131 +
  132 + a = center.y - e.pageY;
  133 + b = center.x - e.pageX;
  134 + deg = Math.atan2(a,b)*rad2deg;
  135 + if(deg < 0){
  136 + deg = 360 + deg;
  137 + }
  138 + if (deg > maxDeg) {
  139 + if (deg - 360 > minDeg) {
  140 + deg = deg - 360;
  141 + } else {
  142 + return false;
  143 + }
  144 + }
  145 + currentDeg = deg;
  146 + lastDeg = deg;
  147 + knobTopPointerContainer.css('transform','rotate('+(currentDeg)+'deg)');
  148 + turn(degreeToRatio(currentDeg));
  149 + rotation = currentDeg;
  150 + startDeg = -1;
  151 + });
  152 +
  153 + knob.on('mousedown touchstart', (e) => {
  154 + e.preventDefault();
  155 + var offset = knob.offset();
  156 + var center = {
  157 + y : offset.top + knob.height()/2,
  158 + x: offset.left + knob.width()/2
  159 + };
  160 +
  161 + var a, b, deg, tmp,
  162 + rad2deg = 180/Math.PI;
  163 +
  164 + knob.on('mousemove.rem touchmove.rem', (e) => {
  165 + moving = true;
  166 + e = (e.originalEvent.touches) ? e.originalEvent.touches[0] : e;
  167 +
  168 + a = center.y - e.pageY;
  169 + b = center.x - e.pageX;
  170 + deg = Math.atan2(a,b)*rad2deg;
  171 + if(deg < 0){
  172 + deg = 360 + deg;
  173 + }
  174 +
  175 + if(startDeg == -1){
  176 + startDeg = deg;
  177 + }
  178 +
  179 + tmp = Math.floor((deg-startDeg) + rotation);
  180 +
  181 + if(tmp < 0){
  182 + tmp = 360 + tmp;
  183 + }
  184 + else if(tmp > 359){
  185 + tmp = tmp % 360;
  186 + }
  187 +
  188 + if (tmp > maxDeg) {
  189 + if (tmp - 360 > minDeg) {
  190 + tmp = tmp - 360;
  191 + } else {
  192 + var deltaMax = Math.abs(maxDeg - lastDeg);
  193 + var deltaMin = Math.abs(minDeg - lastDeg);
  194 + if (deltaMax < deltaMin) {
  195 + tmp = maxDeg;
  196 + } else {
  197 + tmp = minDeg;
  198 + }
  199 + }
  200 + }
  201 + if(Math.abs(tmp - lastDeg) > 180){
  202 + startDeg = deg;
  203 + rotation = currentDeg;
  204 + return false;
  205 + }
  206 +
  207 + currentDeg = tmp;
  208 + lastDeg = tmp;
  209 +
  210 + knobTopPointerContainer.css('transform','rotate('+(currentDeg)+'deg)');
  211 + turn(degreeToRatio(currentDeg));
  212 + });
  213 +
  214 + $document.on('mouseup.rem touchend.rem',() => {
  215 + knob.off('.rem');
  216 + $document.off('.rem');
  217 + rotation = currentDeg;
  218 + startDeg = -1;
  219 + });
  220 +
  221 + });
  222 + vm.ctx.resize = resize;
  223 + resize();
  224 + var initialValue = angular.isDefined(vm.ctx.settings.initialValue) ? vm.ctx.settings.initialValue : vm.minValue;
  225 + setValue(initialValue);
  226 +
  227 + var subscription = vm.ctx.defaultSubscription;
  228 + var rpcEnabled = subscription.rpcEnabled;
  229 +
  230 + vm.isSimulated = $scope.widgetEditMode;
  231 +
  232 + vm.requestTimeout = 500;
  233 + if (vm.ctx.settings.requestTimeout) {
  234 + vm.requestTimeout = vm.ctx.settings.requestTimeout;
  235 + }
  236 + vm.getValueMethod = 'getValue';
  237 + if (vm.ctx.settings.getValueMethod && vm.ctx.settings.getValueMethod.length) {
  238 + vm.getValueMethod = vm.ctx.settings.getValueMethod;
  239 + }
  240 + vm.setValueMethod = 'setValue';
  241 + if (vm.ctx.settings.setValueMethod && vm.ctx.settings.setValueMethod.length) {
  242 + vm.setValueMethod = vm.ctx.settings.setValueMethod;
  243 + }
  244 + if (!rpcEnabled) {
  245 + onError('Target device is not set!');
  246 + } else {
  247 + if (!vm.isSimulated) {
  248 + rpcRequestValue();
  249 + }
  250 + }
  251 + }
  252 +
  253 + function ratioToDegree(ratio) {
  254 + return minDeg + ratio*(maxDeg-minDeg);
  255 + }
  256 +
  257 + function degreeToRatio(degree) {
  258 + return (degree-minDeg)/(maxDeg-minDeg);
  259 + }
  260 +
  261 + function resize() {
  262 + var width = knobContainer.width();
  263 + var height = knobContainer.height();
  264 + var size = Math.min(width, height);
  265 + knob.css({width: size, height: size});
  266 + canvasBar.update({width: size, height: size});
  267 + setFontSize(knobTitle, vm.title, knobTitleContainer.height(), knobTitleContainer.width());
  268 + setFontSize(knobError, vm.error, knobErrorContainer.height(), knobErrorContainer.width());
  269 + var minmaxHeight = knobMinmaxContainer.height();
  270 + minmaxLanel.css({'fontSize': minmaxHeight+'px', 'lineHeight': minmaxHeight+'px'});
  271 + checkValueSize();
  272 + }
  273 +
  274 + function turn(ratio) {
  275 + var value = (vm.minValue + (vm.maxValue - vm.minValue)*ratio).toFixed(vm.ctx.decimals);
  276 + if (canvasBar.value != value) {
  277 + canvasBar.value = value;
  278 + }
  279 + updateColor(canvasBar.getValueColor());
  280 + onValue(value);
  281 + }
  282 +
  283 + function setValue(value) {
  284 + var ratio = (value-vm.minValue) / (vm.maxValue - vm.minValue);
  285 + rotation = lastDeg = currentDeg = ratioToDegree(ratio);
  286 + knobTopPointerContainer.css('transform','rotate('+(currentDeg)+'deg)');
  287 + if (canvasBar.value != value) {
  288 + canvasBar.value = value;
  289 + }
  290 + updateColor(canvasBar.getValueColor());
  291 + vm.value = formatValue(value);
  292 + checkValueSize();
  293 + }
  294 +
  295 + function updateColor(color) {
  296 + var glowColor = tinycolor(color).brighten(30).toHexString();
  297 + knobValue.css({'color': glowColor});
  298 + var textShadow = `${color} 1px 1px 10px, ${glowColor} 1px 1px 10px`;
  299 + knobValue.css({'textShadow': textShadow});
  300 + knobTopPointer.css({'backgroundColor': glowColor});
  301 + var boxShadow = `inset 1px 0 2px #040404, 1px 1px 8px 2px ${glowColor}`;
  302 + knobTopPointer.css({'boxShadow': boxShadow});
  303 + }
  304 +
  305 + function onValue(value) {
  306 + $scope.$applyAsync(() => {
  307 + vm.value = formatValue(value);
  308 + checkValueSize();
  309 + rpcUpdateValue(value);
  310 + });
  311 + }
  312 +
  313 + function onError(error) {
  314 + $scope.$applyAsync(() => {
  315 + vm.error = error;
  316 + setFontSize(knobError, vm.error, knobErrorContainer.height(), knobErrorContainer.width());
  317 + });
  318 + }
  319 +
  320 + function formatValue(value) {
  321 + return vm.ctx.utils.formatValue(value, vm.ctx.decimals, vm.ctx.units, true);
  322 + }
  323 +
  324 + function checkValueSize() {
  325 + var fontSize = knobValueContainer.height()/3.3;
  326 + var containerWidth = knobValueContainer.width();
  327 + setFontSize(knobValue, vm.value, fontSize, containerWidth);
  328 + }
  329 +
  330 + function setFontSize(element, text, fontSize, maxWidth) {
  331 + var textWidth = measureTextWidth(text, fontSize);
  332 + while (textWidth > maxWidth) {
  333 + fontSize--;
  334 + textWidth = measureTextWidth(text, fontSize);
  335 + }
  336 + element.css({'fontSize': fontSize+'px', 'lineHeight': fontSize+'px'});
  337 + }
  338 +
  339 + function measureTextWidth(text, fontSize) {
  340 + textMeasure.css({'fontSize': fontSize+'px', 'lineHeight': fontSize+'px'});
  341 + textMeasure.text(text);
  342 + return textMeasure.width();
  343 + }
  344 +
  345 + function rpcRequestValue() {
  346 + vm.error = '';
  347 + vm.ctx.controlApi.sendTwoWayCommand(vm.getValueMethod, null, vm.requestTimeout).then(
  348 + (responseBody) => {
  349 + if (isNumber(responseBody)) {
  350 + var numValue = Number(responseBody).toFixed(vm.ctx.decimals);
  351 + setValue(numValue);
  352 + } else {
  353 + var errorText = `Unable to parse response: ${responseBody}`;
  354 + onError(errorText);
  355 + }
  356 + },
  357 + () => {
  358 + var errorText = vm.ctx.defaultSubscription.rpcErrorText;
  359 + onError(errorText);
  360 + }
  361 + );
  362 + }
  363 +
  364 + function rpcUpdateValue(value) {
  365 + if (vm.executingUpdateValue) {
  366 + vm.scheduledValue = value;
  367 + return;
  368 + } else {
  369 + vm.scheduledValue = null;
  370 + vm.rpcValue = value;
  371 + vm.executingUpdateValue = true;
  372 + }
  373 + vm.error = '';
  374 + vm.ctx.controlApi.sendOneWayCommand(vm.setValueMethod, value, vm.requestTimeout).then(
  375 + () => {
  376 + vm.executingUpdateValue = false;
  377 + if (vm.scheduledValue != null && vm.scheduledValue != vm.rpcValue) {
  378 + rpcUpdateValue(vm.scheduledValue);
  379 + }
  380 + },
  381 + () => {
  382 + vm.executingUpdateValue = false;
  383 + var errorText = vm.ctx.defaultSubscription.rpcErrorText;
  384 + onError(errorText);
  385 + }
  386 + );
  387 + }
  388 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +$knob-img: url('./knob.svg');
  18 +
  19 +$bars-margin-pct: percentage(0.033);
  20 +$background-margin-pct: percentage(0.05);
  21 +$value-container-margin-pct: percentage(0.35);
  22 +$error-height: percentage(0.05);
  23 +$title-height: percentage(0.066);
  24 +$title-container-margin-pct: percentage(0.2);
  25 +$title-container-margin-bottom-pct: percentage(0.05);
  26 +$minmax-height: percentage(0.04);
  27 +$minmax-container-margin-pct: percentage(0.18);
  28 +$minmax-container-margin-bottom-pct: percentage(0.12);
  29 +
  30 +$background-color: #e6e7e8;
  31 +
  32 +.tb-knob {
  33 + width:100%;
  34 + height:100%;
  35 + background: $background-color;
  36 + &.dark {
  37 + background: #000;
  38 + }
  39 +
  40 + .knob {
  41 + position: relative;
  42 + &[draggable] {
  43 + -moz-user-select: none;
  44 + -webkit-user-select: none;
  45 + user-select: none;
  46 + }
  47 + #canvasBar {
  48 + position:absolute;
  49 + top:0;
  50 + left:0;
  51 + bottom: 0;
  52 + right: 0;
  53 + z-index: 2;
  54 + }
  55 + .canvas-background {
  56 + position:absolute;
  57 + top: $background-margin-pct;
  58 + left: $background-margin-pct;
  59 + bottom: $background-margin-pct;
  60 + right: $background-margin-pct;
  61 + border-radius: 50%;
  62 + background: #3f4346;
  63 + z-index:2;
  64 + }
  65 + .value-container {
  66 + position:absolute;
  67 + top: $value-container-margin-pct;
  68 + left: $value-container-margin-pct;
  69 + bottom: $value-container-margin-pct;
  70 + right: $value-container-margin-pct;
  71 + z-index:4;
  72 + .knob-value {
  73 + color: #fff;
  74 + font-weight: 500;
  75 + white-space: nowrap;
  76 + }
  77 + }
  78 + .error-container {
  79 + position:absolute;
  80 + top: 1%;
  81 + left: 0;
  82 + right: 0;
  83 + z-index:4;
  84 + height: $error-height;
  85 + .knob-error {
  86 + color: #ff3315;
  87 + white-space: nowrap;
  88 + }
  89 + }
  90 + .title-container {
  91 + position:absolute;
  92 + left: $title-container-margin-pct;
  93 + bottom: $title-container-margin-bottom-pct;
  94 + right: $title-container-margin-pct;
  95 + z-index:4;
  96 + height: $title-height;
  97 + .knob-title {
  98 + color: #757575;
  99 + font-weight: 500;
  100 + white-space: nowrap;
  101 + }
  102 + }
  103 + .minmax-container {
  104 + position:absolute;
  105 + left: $minmax-container-margin-pct;
  106 + bottom: $minmax-container-margin-bottom-pct;
  107 + right: $minmax-container-margin-pct;
  108 + z-index:4;
  109 + height: $minmax-height;
  110 + .minmax-label {
  111 + color: #757575;
  112 + font-weight: 500;
  113 + white-space: nowrap;
  114 + }
  115 + }
  116 + .top-pointer-container {
  117 + position:absolute;
  118 + top: $bars-margin-pct;
  119 + left: $bars-margin-pct;
  120 + bottom: $bars-margin-pct;
  121 + right: $bars-margin-pct;
  122 + z-index:3;
  123 + cursor:pointer !important;
  124 + .top-pointer {
  125 + content:'';
  126 + width:5%;
  127 + height:5%;
  128 + background-color:#b5b5b5;
  129 + position:absolute;
  130 + top:50%;
  131 + left:22%;
  132 + margin-top:-2.5%;
  133 + border-radius: 50%;
  134 + cursor:pointer !important;
  135 + box-shadow: 1px 0 2px #040404;
  136 + }
  137 + }
  138 + .top{
  139 + position:absolute;
  140 + top: $bars-margin-pct;
  141 + left: $bars-margin-pct;
  142 + bottom: $bars-margin-pct;
  143 + right: $bars-margin-pct;
  144 + background:$knob-img no-repeat;
  145 + background-size: contain;
  146 + z-index:2;
  147 + cursor:pointer !important;
  148 + }
  149 + #text-measure {
  150 + position: absolute;
  151 + visibility: hidden;
  152 + height: auto;
  153 + width: auto;
  154 + white-space: nowrap;
  155 + }
  156 + }
  157 +}
... ...
  1 +<svg id="svg4451" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="28.222mm" width="28.222mm" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100.00001 100.00001" xmlns:dc="http://purl.org/dc/elements/1.1/">
  2 + <defs id="defs4453">
  3 + <linearGradient id="linearGradient4197">
  4 + <stop id="stop4199" stop-color="#878990" offset="0"/>
  5 + <stop id="stop4201" stop-color="#f6f6f6" offset="1"/>
  6 + </linearGradient>
  7 + <radialGradient id="radialGradient52746-0-4-0" gradientUnits="userSpaceOnUse" cy="991.7" cx="1007" gradientTransform="matrix(-.0050064 -.58050 1.8349 -.015825 -2300 975.09)" r="86.621">
  8 + <stop id="stop4169" stop-color="#bcbdc1" offset="0"/>
  9 + <stop id="stop4171" stop-color="#dfdfdf" offset="1"/>
  10 + </radialGradient>
  11 + <radialGradient id="radialGradient52746-0-9-15-6" xlink:href="#linearGradient4197" gradientUnits="userSpaceOnUse" cy="991.7" cx="1007" gradientTransform="matrix(-.0053377 -.61891 1.9563 -.016872 -2420 1016.8)" r="86.621"/>
  12 + <radialGradient id="radialGradient52746-0-9-6-5-6" gradientUnits="userSpaceOnUse" cy="991.7" cx="1007" gradientTransform="matrix(.0058230 .80069 -2.1343 .015521 1622.1 -511.53)" r="86.621">
  13 + <stop id="stop4270" stop-color="#8c8d93" offset="0"/>
  14 + <stop id="stop4272" stop-color="#fff" offset="1"/>
  15 + </radialGradient>
  16 + <radialGradient id="radialGradient52746-0-9-1-0-9" xlink:href="#linearGradient4197" gradientUnits="userSpaceOnUse" cy="991.7" cx="1007" gradientTransform="matrix(.0031272 .36260 -1.1462 .0098849 645.7 -48.682)" r="86.621"/>
  17 + <linearGradient id="linearGradient4424" y2="392.18" gradientUnits="userSpaceOnUse" x2="-369.32" gradientTransform="translate(109.7 -2.7245)" y1="293.68" x1="-370.65">
  18 + <stop id="stop4418" stop-color="#a0a0a0" offset="0"/>
  19 + <stop id="stop4420" stop-color="#f9faff" offset="1"/>
  20 + </linearGradient>
  21 + </defs>
  22 + <g id="layer1" transform="translate(536.86 -294.93)">
  23 + <g id="g4501">
  24 + <g id="g4497" transform="translate(-226.72 4.7227)">
  25 + <path id="path4414" d="m-260.16 290.67c-19.168-0.22115-37.725 11.849-45.288 29.465-7.973 17.434-4.4546 39.289 8.5831 53.342 12.841 14.512 34.573 20.101 52.82 13.575 18.4-6.082 32.096-23.857 33.289-43.199 1.6579-19.306-9.2817-38.901-26.591-47.615-7.0119-3.6494-14.91-5.5689-22.813-5.5679zm-0.51367 1.6641c18.978-0.21524 37.342 12.022 44.334 29.683 7.0372 17.181 2.95 38.084-9.5553 51.571-0.23129 0.19903-0.58514 0.91551-1.3667 0.57275-0.78156-0.34279-1.868-1.5334-2.5677-2.2508-1.3313-1.365-2.7517-2.5318-1.8623-3.5091 12.362-14.017 15.466-38.947 4.3865-53.994-10.077-14.664-29.972-21.296-46.858-15.752-18.125 5.321-32.394 23.295-31.109 42.522 0.68609 11.361 4.9096 22.828 13.013 30.964-0.52732 1.3864-0.75912 1.4951-2.0893 0.0445-1.4184-1.5468-2.8292-3.0416-5.2228-6.3838-8.8447-13.499-10.547-31.498-3.5798-46.167 6.8006-15.182 22.122-26.317 38.859-27.17 1.2041-0.086 2.4112-0.12896 3.6183-0.13085zm0.43164 28.711c2.6603-0.033 5.7995 0.63181 8.3618 1.8928 2.8152 1.323 5.3991 3.5094 7.1657 5.9467 2.9779 3.9517 4.2346 9.3635 3.5431 13.916-0.63605 5.0851-3.5977 9.93-7.6444 12.856-3.9009 2.9596-9.319 4.2271-13.796 3.5723-4.2078-0.54147-8.0926-2.4558-11.106-5.4642-2.8389-2.8421-4.7656-6.5461-5.3997-10.683-0.41578-2.8552-0.25913-5.5483 0.6416-8.5235 1.1448-3.8517 3.7847-7.4885 6.9779-9.7904 3.1737-2.373 7.3574-3.7297 11.256-3.7236zm-1.7148 0.0859c-0.0536 0.002-0.008 0.009 0 0zm-1.3984 0.18359c-0.0331 0.002-0.002 0.009 0 0zm-13.438 9.377c-0.0247 0.0373 0.004 0.009 0 0zm-0.44727 0.82812c-0.0259 0.0476 0.005 0.009 0 0zm-0.76953 1.7168c-0.0202 0.0478 0.005 0.009 0 0zm-0.71875 2.2363c-0.0164 0.0555 0.006 0.0119 0 0zm-0.50977 6.5332c0.001 0.0536 0.009 0.008 0 0zm0.1836 1.3984c0.003 0.0311 0.008 0.005 0 0zm9.377 13.438c0.0373 0.0247 0.009-0.004 0 0zm0.82812 0.44726c0.0476 0.0259 0.009-0.005 0 0zm1.7168 0.76953c0.0478 0.0202 0.009-0.005 0 0zm2.2363 0.71875c0.0555 0.0164 0.012-0.006 0 0zm6.5332 0.50977c0.0536-0.002 0.008-0.009 0 0zm1.3984-0.1836c0.0331-0.002 0.002-0.009 0 0zm13.438-9.377c0.0247-0.0373-0.004-0.009 0 0zm0.44727-0.82812c0.0259-0.0476-0.005-0.009 0 0zm0.76953-1.7168c0.0202-0.0478-0.005-0.009 0 0zm0.71875-2.2363c0.0164-0.0555-0.006-0.012 0 0zm0.50976-6.5332c-0.002-0.0536-0.009-0.008 0 0zm-0.18359-1.3984c-0.003-0.0311-0.008-0.005 0 0zm-9.377-13.438c-0.0373-0.0247-0.009 0.004 0 0zm-0.82813-0.44726c-0.0476-0.026-0.009 0.005 0 0zm-1.7168-0.76954c-0.0478-0.0202-0.009 0.005 0 0zm-2.2363-0.71875c-0.0555-0.0164-0.012 0.006 0 0zm-20.057 7.7695c-0.0145 0.004 0.008 0.0219 0 0zm-2.6172 16.232 0.006 0.008zm33.26 5.3555 0.002 0.002zm-6.0859 5.5156h0.002zm-3.7949 1.627v0.002z" fill="url(#linearGradient4424)"/>
  26 + <path id="path4402" d="m-280.11 288.16c-6.4503 0.00002-12.898 1.2835-18.857 3.752-5.9593 2.4684-11.425 6.1206-15.986 10.682-4.5611 4.5611-8.2132 10.027-10.682 15.986-2.4684 5.9593-3.7519 12.407-3.752 18.857 0.00002 6.4503 1.2835 12.898 3.752 18.857 2.4684 5.9593 6.1206 11.425 10.682 15.986 4.5611 4.5611 10.027 8.2132 15.986 10.682 5.9593 2.4684 12.407 3.7519 18.857 3.752 6.4503-0.00002 12.898-1.2835 18.857-3.752 5.9593-2.4684 11.425-6.1206 15.986-10.682 4.5611-4.5611 8.2132-10.027 10.682-15.986 2.4684-5.9593 3.7519-12.407 3.752-18.857-0.00002-6.4503-1.2835-12.898-3.752-18.857-2.4684-5.9593-6.1206-11.425-10.682-15.986-4.5611-4.5611-10.027-8.2132-15.986-10.682-5.9593-2.4684-12.407-3.7519-18.857-3.752zm-0.51563 1.1406a0.72311 0.72311 0 0 1 0.002 0c12.714 0.0109 25.044 5.0665 34.031 14.064 8.987 8.9759 13.911 20.601 13.922 33.309-0.0101 13.081-5.1256 24.968-12.645 33.791-0.42485 0.49849-1.011 0.80473-1.6035 0.79101-0.59249-0.0137-1.1124-0.30623-1.5371-0.70703-0.49206-0.46434-1.272-1.1732-1.9512-1.8066-0.33958-0.31669-0.65482-0.61464-0.90234-0.86133-0.12376-0.12334-0.23034-0.23209-0.32031-0.33203-0.09-0.0999-0.15313-0.14986-0.25196-0.3418-0.23991-0.46586-0.19336-0.92164-0.11132-1.2578 0.041-0.16808 0.0951-0.31228 0.15234-0.43554 0.0572-0.12327 0.0585-0.18508 0.24023-0.36719l-0.0371 0.0391c1.8267-2.1277 3.2432-3.7411 4.7402-5.957 1.4986-2.218 2.7891-4.5865 3.8438-7.0801 0.52748-1.247 0.99838-2.5271 1.4043-3.832 0.40577-1.3048 0.74578-2.6349 1.0234-3.9922 0.27784-1.3572 0.49237-2.7395 0.63477-4.1406 0.14215-1.4016 0.21484-2.8241 0.21484-4.2637 0-1.4398-0.0722-2.8624-0.21484-4.2637-0.14239-1.401-0.35691-2.7814-0.63477-4.1387-0.27768-1.3574-0.61772-2.6895-1.0234-3.9941-0.40592-1.305-0.87682-2.585-1.4043-3.832-1.0547-2.4935-2.3451-4.8621-3.8438-7.0801-1.4984-2.2179-3.2054-4.2836-5.0918-6.1699-1.8864-1.8865-3.9502-3.5935-6.168-5.0918-2.218-1.4985-4.5864-2.7891-7.0801-3.8438-1.2464-0.52718-2.5264-0.99628-3.832-1.4023-1.3048-0.4059-2.6391-0.7477-3.9961-1.0254-1.3567-0.27766-2.7353-0.49049-4.1367-0.63281-1.4022-0.14241-2.8266-0.2168-4.2656-0.2168-1.4391 0-2.8677 0.0742-4.2832 0.21875-1.4151 0.14443-2.8162 0.36081-4.1992 0.64258-1.3824 0.28163-2.7461 0.63103-4.0879 1.043-1.3416 0.41186-2.658 0.8853-3.9492 1.4199-2.5825 1.0694-5.0584 2.3774-7.3867 3.8945-2.3284 1.5172-4.5127 3.2441-6.5156 5.1504-2.0029 1.9062-3.8231 3.9924-5.4297 6.2285-0.80355 1.1182-1.5552 2.274-2.2461 3.4629-0.69079 1.1888-1.3223 2.4124-1.8926 3.666-0.57036 1.2538-1.0792 2.5365-1.5195 3.8457-0.44052 1.3098-0.81488 2.6447-1.1172 4.0039-0.30227 1.359-0.532 2.7427-0.6875 4.1445-0.15574 1.4016-0.23633 2.8226-0.23632 4.2598-0.00001 1.4385 0.0756 2.9137 0.22265 4.4062 0.14704 1.4921 0.36785 3.0021 0.65625 4.5137 0.28842 1.5116 0.64471 3.0223 1.0684 4.5195 0.42365 1.4972 0.9143 2.9805 1.4668 4.4297 0.5524 1.4491 1.1672 2.8646 1.8418 4.2324 0.67463 1.3679 1.4079 2.6886 2.1973 3.9414 0.78962 1.2532 1.6328 2.4399 2.5293 3.5449 0.89618 1.1047 1.8475 2.127 2.8438 3.0527 0.17252 0.16076 0.23062 0.36547 0.24414 0.49219 0.0135 0.12672-0.001 0.20699-0.0137 0.27149-0.025 0.12898-0.0562 0.20192-0.0781 0.25976a0.72311 0.72311 0 0 1 -0.0215 0.0508s-0.12243 0.4885-0.74414 0.74024c0.0278-0.0113-0.12936 0.0761-0.35938 0.0742-0.23002-0.002-0.47421-0.12627-0.61523-0.26172-8.2317-7.9053-13.917-21.064-13.928-33.803a0.72311 0.72311 0 0 1 0 -0.002c0.0208-12.334 4.7929-24.185 13.326-33.092l0.002-0.002c9.0244-9.4611 20.692-14.256 33.754-14.277zm0.51563 29.682c-0.97685 0.00002 1.8819 0.0713 0.90625 0.0234-0.97567-0.0479 1.8745 0.16214 0.90234 0.0664-0.97214-0.0957 1.8647 0.25269 0.89844 0.10937-0.96628-0.14331 1.8507 0.34681 0.89258 0.15625-0.95808-0.19055 1.8323 0.43461 0.88476 0.19727-0.94757-0.23734 1.8078 0.52574 0.87305 0.24219-0.93479-0.28355 1.7791 0.61228 0.85938 0.2832-0.91975-0.32908 1.7482 0.69999 0.8457 0.32617-0.9025-0.37381 1.7112 0.78484 0.82812 0.36719-0.88306-0.41765 1.6701 0.86868 0.8086 0.4082-0.86151-0.46047 1.6269 0.94751 0.78906 0.44532-0.83787-0.5022 1.5778 1.029 0.76562 0.48632-0.81222-0.5427 1.5248 1.1034 0.74024 0.52149-0.78462-0.58191 1.468 1.1763 0.71289 0.55664-0.75512-0.61971 1.4093 1.2478 0.68555 0.5918-0.7238-0.65601 1.347 1.3157 0.65625 0.625-0.69074-0.69074 1.281 1.38 0.625 0.65625-0.65601-0.7238 1.2115 1.4407 0.59179 0.68554-0.6197-0.75511 1.1386 1.4975 0.55664 0.71289-0.5819-0.78461 1.0642 1.5525 0.52149 0.74024-0.54271-0.81222 0.98852 1.6035 0.48633 0.76562-0.5022-0.83787 0.90578 1.6506 0.44531 0.78907-0.46048-0.86151 0.82585 1.6916 0.4082 0.80859-0.41765-0.88307 0.741 1.7306 0.36719 0.82812-0.37381-0.90249 0.65525 1.7655 0.32617 0.84571-0.32908-0.91975 0.56676 1.7942 0.2832 0.85937-0.28355-0.93479 0.47953 1.8206 0.24219 0.87305-0.23734-0.94758 0.38783 1.8428 0.19727 0.88476-0.19056-0.95808 0.29956 1.8589 0.15625 0.89258-0.14332-0.96628 0.2051 1.8706 0.10937 0.89844-0.0957-0.97215 0.11432 1.878 0.0664 0.90234-0.0479-0.97567 0.0234 1.8831 0.0234 0.90625-0.00002-0.97684-0.0713 1.8819-0.0234 0.90625 0.0479-0.97567-0.16214 1.8745-0.0664 0.90235 0.0957-0.97215-0.25269 1.8647-0.10937 0.89843 0.14331-0.96627-0.34681 1.8507-0.15625 0.89258 0.19056-0.95808-0.43461 1.8324-0.19727 0.88477 0.23734-0.94758-0.52574 1.8078-0.24219 0.87305 0.28356-0.93479-0.61228 1.7791-0.2832 0.85937 0.32908-0.91975-0.69998 1.7482-0.32617 0.8457 0.37381-0.90249-0.78484 1.7112-0.36719 0.82813s-0.86868 1.6701-0.4082 0.80859c0.46047-0.8615-0.94751 1.6269-0.44531 0.78907 0.50219-0.83788-1.029 1.5778-0.48633 0.76562 0.5427-0.81222-1.1034 1.5248-0.52149 0.74023 0.58191-0.78461-1.1763 1.468-0.55664 0.71289 0.61971-0.75511-1.2478 1.4094-0.59179 0.68555 0.65601-0.7238-1.3157 1.347-0.625 0.65625 0.69073-0.69073-1.38 1.281-0.65625 0.625 0.72379-0.65601-1.4407 1.2115-0.68555 0.5918 0.75511-0.61971-1.4975 1.1385-0.71289 0.55664 0.78461-0.58191-1.5525 1.0642-0.74024 0.52148 0.81223-0.5427-1.6035 0.98853-0.76562 0.48633 0.83787-0.50219-1.6506 0.90579-0.78906 0.44531 0.8615-0.46047-1.6917 0.82586-0.8086 0.40821 0.88307-0.41765-1.7306 0.741-0.82812 0.36719 0.90249-0.37382-1.7654 0.65525-0.8457 0.32617 0.91974-0.32908-1.7942 0.56675-0.85938 0.2832 0.93479-0.28355-1.8206 0.47953-0.87305 0.24219 0.94758-0.23734-1.8428 0.38782-0.88476 0.19726 0.95808-0.19056-1.8589 0.29957-0.89258 0.15625 0.96628-0.14331-1.8706 0.20511-0.89844 0.10938 0.97215-0.0957-1.878 0.11432-0.90234 0.0664 0.97567-0.0479-1.8831 0.0234-0.90625 0.0234 0.97685-0.00002-1.8819-0.0713-0.90625-0.0234 0.97567 0.0479-1.8745-0.16213-0.90235-0.0664 0.97215 0.0957-1.8647-0.25269-0.89843-0.10938 0.96627 0.14332-1.8507-0.3468-0.89258-0.15625 0.95808 0.19056-1.8323-0.4346-0.88477-0.19726 0.94758 0.23734-1.8078-0.52574-0.87304-0.24219 0.93479 0.28355-1.7791-0.61228-0.85938-0.2832 0.91975 0.32908-1.7482-0.69999-0.8457-0.32617 0.90249 0.37381-1.7112-0.78484-0.82813-0.36719s-1.6701-0.86868-0.80859-0.40821c0.86151 0.46048-1.6269-0.9475-0.78906-0.44531 0.83787 0.5022-1.5778-1.029-0.76563-0.48633 0.81222 0.54271-1.5248-1.1034-0.74023-0.52148 0.78461 0.5819-1.468-1.1763-0.71289-0.55664 0.75511 0.6197-1.4094-1.2478-0.68555-0.5918 0.7238 0.65601-1.347-1.3157-0.65625-0.625 0.69074 0.69074-1.281-1.38-0.625-0.65625 0.65601 0.7238-1.2115-1.4407-0.5918-0.68555 0.61971 0.75512-1.1385-1.4975-0.55664-0.71289 0.58191 0.78462-1.0642-1.5524-0.52148-0.74023 0.5427 0.81222-0.98852-1.6035-0.48633-0.76562 0.50219 0.83787-0.90579-1.6506-0.44531-0.78907 0.46047 0.86151-0.82585-1.6917-0.40821-0.80859 0.41765 0.88306-0.741-1.7306-0.36718-0.82813 0.37381 0.9025-0.65525-1.7654-0.32618-0.8457 0.32908 0.91975-0.56675-1.7942-0.2832-0.85937 0.28355 0.93478-0.47953-1.8206-0.24219-0.87305 0.23734 0.94758-0.38782-1.8428-0.19726-0.88477 0.19056 0.95808-0.29957-1.8588-0.15625-0.89258 0.14332 0.96628-0.20511-1.8706-0.10938-0.89843 0.0957 0.97214-0.11431-1.878-0.0664-0.90235 0.0479 0.97568-0.0234-1.8831-0.0234-0.90625 0.00002 0.97685 0.0713-1.8819 0.0234-0.90625-0.0479 0.97568 0.16213-1.8745 0.0664-0.90234-0.0957 0.97215 0.2527-1.8647 0.10938-0.89844-0.14332 0.96628 0.34681-1.8507 0.15625-0.89258-0.19056 0.95809 0.43461-1.8323 0.19726-0.88476-0.23734 0.94758 0.52574-1.8078 0.24219-0.87305-0.28355 0.93479 0.61228-1.7791 0.2832-0.85937-0.32907 0.91975 0.69999-1.7482 0.32618-0.84571-0.37382 0.9025 0.78483-1.7112 0.36718-0.82812-0.41764 0.88306 0.86868-1.6701 0.40821-0.80859-0.46048 0.8615 0.9475-1.6269 0.44531-0.78907-0.50219 0.83788 1.029-1.5778 0.48633-0.76562-0.54271 0.81222 1.1034-1.5248 0.52148-0.74024-0.5819 0.78462 1.1764-1.468 0.55664-0.71289-0.6197 0.75512 1.2478-1.4093 0.5918-0.68554-0.65601 0.72379 1.3157-1.347 0.625-0.65625-0.69074 0.69073 1.38-1.281 0.65625-0.625-0.7238 0.65601 1.4407-1.2115 0.68555-0.5918-0.75512 0.6197 1.4975-1.1386 0.71289-0.55664-0.78462 0.5819 1.5524-1.0642 0.74023-0.52149-0.81222 0.54271 1.6035-0.98852 0.76563-0.48632-0.83788 0.50219 1.6506-0.90579 0.78906-0.44532-0.86151 0.46048 1.6917-0.82585 0.80859-0.4082-0.88306 0.41765 1.7306-0.741 0.82813-0.36719-0.9025 0.37382 1.7654-0.65525 0.8457-0.32617-0.91975 0.32908 1.7942-0.56675 0.85938-0.2832-0.93479 0.28355 1.8206-0.47953 0.87304-0.24219-0.94757 0.23734 1.8428-0.38782 0.88477-0.19727-0.95808 0.19056 1.8588-0.29956 0.89258-0.15625-0.96628 0.14332 1.8706-0.2051 0.89843-0.10937-0.97214 0.0957 1.878-0.11432 0.90235-0.0664-0.97567 0.0479 1.8831-0.0234 0.90625-0.0234z" transform="matrix(1.0147 0 0 1.0147 24.08 -2.1712)" fill="#e6e7e8"/>
  27 + </g>
  28 + <g id="g4957">
  29 + <path id="path52738-1-8-5-0-9" fill="url(#radialGradient52746-0-9-6-5-6)" d="m-486.86 309.22a35.709 35.709 0 0 0 -35.711 35.709 35.709 35.709 0 0 0 35.711 35.711 35.709 35.709 0 0 0 35.709 -35.711 35.709 35.709 0 0 0 -35.709 -35.709zm0 16.533a19.177 19.177 0 0 1 19.176 19.176 19.177 19.177 0 0 1 -19.176 19.178 19.177 19.177 0 0 1 -19.178 -19.178 19.177 19.177 0 0 1 19.178 -19.176z"/>
  30 + <path id="path52738-1-8-4-8" d="m-486.86 312.2a32.733 32.733 0 0 0 -32.732 32.734 32.733 32.733 0 0 0 32.732 32.732 32.733 32.733 0 0 0 32.732 -32.732 32.733 32.733 0 0 0 -32.732 -32.734zm0 13.557a19.177 19.177 0 0 1 19.178 19.178 19.177 19.177 0 0 1 -19.178 19.176 19.177 19.177 0 0 1 -19.178 -19.176 19.177 19.177 0 0 1 19.178 -19.178z" stroke="#8c8d93" stroke-width=".49597" fill="url(#radialGradient52746-0-9-15-6)"/>
  31 + <path id="path52738-1-4-6" fill="url(#radialGradient52746-0-4-0)" d="m-486.86 314.23a30.702 30.702 0 0 0 -30.701 30.701 30.702 30.702 0 0 0 30.701 30.701 30.702 30.702 0 0 0 30.701 -30.701 30.702 30.702 0 0 0 -30.701 -30.701zm0 11.523a19.177 19.177 0 0 1 19.178 19.178 19.177 19.177 0 0 1 -19.178 19.178 19.177 19.177 0 0 1 -19.178 -19.178 19.177 19.177 0 0 1 19.178 -19.178z"/>
  32 + <path id="path52738-1-8-9-4-0" fill="url(#radialGradient52746-0-9-1-0-9)" d="m-486.86 325.76a19.177 19.177 0 0 0 -19.178 19.178 19.177 19.177 0 0 0 19.178 19.178 19.177 19.177 0 0 0 19.178 -19.178 19.177 19.177 0 0 0 -19.178 -19.178zm0 1.4102a17.768 17.768 0 0 1 17.768 17.768 17.768 17.768 0 0 1 -17.768 17.768 17.768 17.768 0 0 1 -17.768 -17.768 17.768 17.768 0 0 1 17.768 -17.768z"/>
  33 + </g>
  34 + </g>
  35 + </g>
  36 +</svg>
... ...
  1 +<!--
  2 +
  3 + Copyright © 2016-2017 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +
  19 +<div class="tb-knob" layout="column" ng-class="{'dark': vm.darkTheme}" ng-style="{'pointerEvents': vm.ctx.isEdit ? 'none' : 'all'}">
  20 + <div id="knob-container" flex layout="column" layout-align="center center">
  21 + <div class="knob">
  22 + <div class="value-container" layout="row" layout-align="center center">
  23 + <span class="knob-value">{{ vm.value }}</span>
  24 + </div>
  25 + <div class="canvas-background"></div>
  26 + <canvas id="canvasBar"></canvas>
  27 + <div class="top-pointer-container">
  28 + <div class="top-pointer"></div>
  29 + </div>
  30 + <div class="top"></div>
  31 + <div class="error-container" ng-style="{'background': vm.error.length ? 'rgba(255,255,255,0.25)' : 'none'}"
  32 + layout="row" layout-align="center center">
  33 + <span class="knob-error">{{ vm.error }}</span>
  34 + </div>
  35 + <div class="title-container" layout="row" layout-align="center center">
  36 + <span class="knob-title">{{ vm.title }}</span>
  37 + </div>
  38 + <div class="minmax-container" layout="row" layout-align="start center">
  39 + <span class="minmax-label">min</span>
  40 + <span flex></span>
  41 + <span class="minmax-label">max</span>
  42 + </div>
  43 + <div id="text-measure"></div>
  44 + </div>
  45 + </div>
  46 +</div>
\ No newline at end of file
... ...