thingsboard-developers
Changes
common/transport/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java 2(+2 -0)
transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java 2(+1 -1)
transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java 116(+74 -42)
transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java 8(+5 -3)
transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java 10(+6 -4)
transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java 5(+4 -1)
transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttDeviceAwareSessionContext.java 57(+57 -0)
ui/.stylelintrc 6(+3 -3)
ui/package.json 16(+15 -1)
ui/src/app/components/dashboard.scss 4(+2 -2)
ui/src/app/components/grid.scss 4(+2 -2)
ui/src/app/components/menu-link.scss 6(+3 -3)
ui/src/app/components/side-menu.scss 2(+1 -1)
ui/src/app/dashboard/dashboard.scss 10(+5 -5)
ui/src/app/layout/home.scss 2(+1 -1)
ui/src/app/rulechain/rulechain.scss 16(+1 -15)
ui/src/app/widget/lib/flot-widget.js 458(+245 -213)
ui/src/app/widget/lib/rpc/knob.scss 2(+0 -2)
ui/src/app/widget/lib/rpc/round-switch.scss 95(+41 -54)
ui/src/app/widget/widget-editor.scss 2(+1 -1)
ui/src/scss/animations.scss 16(+8 -8)
ui/src/scss/main.scss 28(+11 -17)
ui/src/scss/mixins.scss 2(+2 -0)
Details
diff --git a/application/src/main/data/json/system/widget_bundles/cards.json b/application/src/main/data/json/system/widget_bundles/cards.json
index 803f1d3..1ccfdc9 100644
--- a/application/src/main/data/json/system/widget_bundles/cards.json
+++ b/application/src/main/data/json/system/widget_bundles/cards.json
@@ -15,7 +15,7 @@
"resources": [],
"templateHtml": "",
"templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n margin: 5px;\n padding: 8px;\n}\n\n.tbDatasource-title {\n font-size: 1.200rem;\n font-weight: 500;\n padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n width: 100%;\n box-shadow: 0 0 10px #ccc;\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 1.000rem;\n color: #757575;\n}\n\n.tbDatasource-table td {\n position: relative;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding: 0px 18px;\n box-sizing: border-box;\n}",
- "controllerScript": "self.onInit = function() {\n \n self.ctx.datasourceTitleCells = [];\n self.ctx.valueCells = [];\n self.ctx.labelCells = [];\n \n for (var i=0; i < self.ctx.datasources.length; i++) {\n var tbDatasource = self.ctx.datasources[i];\n\n var datasourceId = 'tbDatasource' + i;\n self.ctx.$container.append(\n \"<div id='\" + datasourceId +\n \"' class='tbDatasource-container'></div>\"\n );\n\n var datasourceContainer = $('#' + datasourceId,\n self.ctx.$container);\n\n datasourceContainer.append(\n \"<div class='tbDatasource-title'>\" +\n tbDatasource.name + \"</div>\"\n );\n \n var datasourceTitleCell = $('.tbDatasource-title', datasourceContainer);\n self.ctx.datasourceTitleCells.push(datasourceTitleCell);\n \n var tableId = 'table' + i;\n datasourceContainer.append(\n \"<table id='\" + tableId +\n \"' class='tbDatasource-table'><col width='30%'><col width='70%'></table>\"\n );\n var table = $('#' + tableId, self.ctx.$container);\n\n for (var a = 0; a < tbDatasource.dataKeys.length; a++) {\n var dataKey = tbDatasource.dataKeys[a];\n var labelCellId = 'labelCell' + a;\n var cellId = 'cell' + a;\n table.append(\"<tr><td id='\" + labelCellId + \"'>\" + dataKey.label +\n \"</td><td id='\" + cellId +\n \"'></td></tr>\");\n var labelCell = $('#' + labelCellId, table);\n self.ctx.labelCells.push(labelCell);\n var valueCell = $('#' + cellId, table);\n self.ctx.valueCells.push(valueCell);\n }\n } \n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n for (var i = 0; i < self.ctx.valueCells.length; i++) {\n var cellData = self.ctx.data[i];\n if (cellData && cellData.data && cellData.data.length > 0) {\n var tvPair = cellData.data[cellData.data.length -\n 1];\n var value = tvPair[1];\n self.ctx.valueCells[i].html(value);\n }\n } \n}\n\nself.onResize = function() {\n var datasoirceTitleFontSize = self.ctx.height/8;\n if (self.ctx.width/self.ctx.height <= 1.5) {\n datasoirceTitleFontSize = self.ctx.width/12;\n }\n datasoirceTitleFontSize = Math.min(datasoirceTitleFontSize, 20);\n for (var i = 0; i < self.ctx.datasourceTitleCells.length; i++) {\n self.ctx.datasourceTitleCells[i].css('font-size', datasoirceTitleFontSize+'px');\n }\n var valueFontSize = self.ctx.height/9;\n var labelFontSize = self.ctx.height/9;\n if (self.ctx.width/self.ctx.height <= 1.5) {\n valueFontSize = self.ctx.width/15;\n labelFontSize = self.ctx.width/15;\n }\n valueFontSize = Math.min(valueFontSize, 18);\n labelFontSize = Math.min(labelFontSize, 18);\n\n for (i = 0; i < self.ctx.valueCells; i++) {\n self.ctx.valueCells[i].css('font-size', valueFontSize+'px');\n self.ctx.valueCells[i].css('height', valueFontSize*2.5+'px');\n self.ctx.valueCells[i].css('padding', '0px ' + valueFontSize + 'px');\n self.ctx.labelCells[i].css('font-size', labelFontSize+'px');\n self.ctx.labelCells[i].css('height', labelFontSize*2.5+'px');\n self.ctx.labelCells[i].css('padding', '0px ' + labelFontSize + 'px');\n } \n}\n\nself.onDestroy = function() {\n}\n",
+ "controllerScript": "self.onInit = function() {\n \n self.ctx.datasourceTitleCells = [];\n self.ctx.valueCells = [];\n self.ctx.labelCells = [];\n \n for (var i=0; i < self.ctx.datasources.length; i++) {\n var tbDatasource = self.ctx.datasources[i];\n\n var datasourceId = 'tbDatasource' + i;\n self.ctx.$container.append(\n \"<div id='\" + datasourceId +\n \"' class='tbDatasource-container'></div>\"\n );\n\n var datasourceContainer = $('#' + datasourceId,\n self.ctx.$container);\n\n datasourceContainer.append(\n \"<div class='tbDatasource-title'>\" +\n tbDatasource.name + \"</div>\"\n );\n \n var datasourceTitleCell = $('.tbDatasource-title', datasourceContainer);\n self.ctx.datasourceTitleCells.push(datasourceTitleCell);\n \n var tableId = 'table' + i;\n datasourceContainer.append(\n \"<table id='\" + tableId +\n \"' class='tbDatasource-table'><col width='30%'><col width='70%'></table>\"\n );\n var table = $('#' + tableId, self.ctx.$container);\n\n for (var a = 0; a < tbDatasource.dataKeys.length; a++) {\n var dataKey = tbDatasource.dataKeys[a];\n var labelCellId = 'labelCell' + a;\n var cellId = 'cell' + a;\n table.append(\"<tr><td id='\" + labelCellId + \"'>\" + dataKey.label +\n \"</td><td id='\" + cellId +\n \"'></td></tr>\");\n var labelCell = $('#' + labelCellId, table);\n self.ctx.labelCells.push(labelCell);\n var valueCell = $('#' + cellId, table);\n self.ctx.valueCells.push(valueCell);\n }\n } \n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n for (var i = 0; i < self.ctx.valueCells.length; i++) {\n var cellData = self.ctx.data[i];\n console.log(self.ctx); //del\n if (cellData && cellData.data && cellData.data.length > 0) {\n var tvPair = cellData.data[cellData.data.length -\n 1];\n var value = tvPair[1];\n var textValue;\n //toDo -> + IsNumber\n \n if (isNumber(value)) {\n var decimals = self.ctx.decimals;\n var units = self.ctx.units;\n if (cellData.dataKey.decimals || cellData.dataKey.decimals === 0) {\n decimals = cellData.dataKey.decimals;\n }\n if (cellData.dataKey.units) {\n units = cellData.dataKey.units;\n }\n txtValue = self.ctx.utils.formatValue(value, decimals, units, true);\n } else {\n txtValue = value;\n }\n self.ctx.valueCells[i].html(txtValue);\n }\n }\n \n function isNumber(n) {\n return !isNaN(parseFloat(n)) && isFinite(n);\n }\n}\n\nself.onResize = function() {\n var datasoirceTitleFontSize = self.ctx.height/8;\n if (self.ctx.width/self.ctx.height <= 1.5) {\n datasoirceTitleFontSize = self.ctx.width/12;\n }\n datasoirceTitleFontSize = Math.min(datasoirceTitleFontSize, 20);\n for (var i = 0; i < self.ctx.datasourceTitleCells.length; i++) {\n self.ctx.datasourceTitleCells[i].css('font-size', datasoirceTitleFontSize+'px');\n }\n var valueFontSize = self.ctx.height/9;\n var labelFontSize = self.ctx.height/9;\n if (self.ctx.width/self.ctx.height <= 1.5) {\n valueFontSize = self.ctx.width/15;\n labelFontSize = self.ctx.width/15;\n }\n valueFontSize = Math.min(valueFontSize, 18);\n labelFontSize = Math.min(labelFontSize, 18);\n\n for (i = 0; i < self.ctx.valueCells; i++) {\n self.ctx.valueCells[i].css('font-size', valueFontSize+'px');\n self.ctx.valueCells[i].css('height', valueFontSize*2.5+'px');\n self.ctx.valueCells[i].css('padding', '0px ' + valueFontSize + 'px');\n self.ctx.labelCells[i].css('font-size', labelFontSize+'px');\n self.ctx.labelCells[i].css('height', labelFontSize*2.5+'px');\n self.ctx.labelCells[i].css('padding', '0px ' + labelFontSize + 'px');\n } \n}\n\nself.onDestroy = function() {\n}\n",
"settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Attributes card\"}"
@@ -95,7 +95,7 @@
"resources": [],
"templateHtml": "",
"templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n width: 100%;\n height: 100%;\n overflow: hidden;\n}\n\n.tbDatasource-table {\n width: 100%;\n height: 100%;\n border-collapse: collapse;\n white-space: nowrap;\n font-weight: 100;\n text-align: right;\n}\n\n.tbDatasource-table td {\n padding: 12px;\n position: relative;\n box-sizing: border-box;\n}\n\n.tbDatasource-data-key {\n opacity: 0.7;\n font-weight: 400;\n font-size: 3.500rem;\n}\n\n.tbDatasource-value {\n font-size: 5.000rem;\n}",
- "controllerScript": "self.onInit = function() {\n\n self.ctx.labelPosition = self.ctx.settings.labelPosition || 'left';\n \n if (self.ctx.datasources.length > 0) {\n var tbDatasource = self.ctx.datasources[0];\n var datasourceId = 'tbDatasource' + 0;\n self.ctx.$container.append(\n \"<div id='\" + datasourceId +\n \"' class='tbDatasource-container'></div>\"\n );\n \n self.ctx.datasourceContainer = $('#' + datasourceId,\n self.ctx.$container);\n \n var tableId = 'table' + 0;\n self.ctx.datasourceContainer.append(\n \"<table id='\" + tableId +\n \"' class='tbDatasource-table'><col width='30%'><col width='70%'></table>\"\n );\n var table = $('#' + tableId, self.ctx.$container);\n if (self.ctx.labelPosition === 'top') {\n table.css('text-align', 'left');\n }\n \n if (tbDatasource.dataKeys.length > 0) {\n var dataKey = tbDatasource.dataKeys[0];\n var labelCellId = 'labelCell' + 0;\n var cellId = 'cell' + 0;\n if (self.ctx.labelPosition === 'left') {\n table.append(\n \"<tr><td class='tbDatasource-data-key' id='\" + labelCellId +\"'>\" +\n dataKey.label +\n \"</td><td class='tbDatasource-value' id='\" +\n cellId +\n \"'></td></tr>\");\n } else {\n table.append(\n \"<tr style='vertical-align: bottom;'><td class='tbDatasource-data-key' id='\" + labelCellId +\"'>\" +\n dataKey.label +\n \"</td></tr><tr><td class='tbDatasource-value' id='\" +\n cellId +\n \"'></td></tr>\");\n }\n self.ctx.labelCell = $('#' + labelCellId, table);\n self.ctx.valueCell = $('#' + cellId, table);\n self.ctx.valueCell.html(0 + ' ' + self.ctx.units);\n }\n }\n \n $.fn.textWidth = function(){\n var html_org = $(this).html();\n var html_calc = '<span>' + html_org + '</span>';\n $(this).html(html_calc);\n var width = $(this).find('span:first').width();\n $(this).html(html_org);\n return width;\n }; \n \n self.onResize();\n};\n\nself.onDataUpdated = function() {\n \n function isNumber(n) {\n return !isNaN(parseFloat(n)) && isFinite(n);\n }\n\n if (self.ctx.valueCell && self.ctx.data.length > 0) {\n var cellData = self.ctx.data[0];\n if (cellData.data.length > 0) {\n var tvPair = cellData.data[cellData.data.length -\n 1];\n var value = tvPair[1];\n var txtValue;\n if (isNumber(value)) {\n txtValue = self.ctx.utils.formatValue(value, self.ctx.decimals, self.ctx.units);\n } else {\n txtValue = value;\n }\n self.ctx.valueCell.html(txtValue);\n var targetWidth;\n var minDelta;\n if (self.ctx.labelPosition === 'left') {\n targetWidth = self.ctx.datasourceContainer.width() - self.ctx.labelCell.width();\n minDelta = self.ctx.width/16 + self.ctx.padding;\n } else {\n targetWidth = self.ctx.datasourceContainer.width();\n minDelta = self.ctx.padding;\n }\n var delta = targetWidth - self.ctx.valueCell.textWidth();\n var fontSize = self.ctx.valueFontSize;\n if (targetWidth > minDelta) {\n while (delta < minDelta && fontSize > 6) {\n fontSize--;\n self.ctx.valueCell.css('font-size', fontSize+'px');\n delta = targetWidth - self.ctx.valueCell.textWidth();\n }\n }\n }\n } \n \n};\n\nself.onResize = function() {\n var labelFontSize;\n if (self.ctx.labelPosition === 'top') {\n self.ctx.padding = self.ctx.height/20;\n labelFontSize = self.ctx.height/4;\n self.ctx.valueFontSize = self.ctx.height/2;\n } else {\n self.ctx.padding = self.ctx.width/50;\n labelFontSize = self.ctx.height/2.5;\n self.ctx.valueFontSize = self.ctx.height/2;\n if (self.ctx.width/self.ctx.height <= 2.7) {\n labelFontSize = self.ctx.width/7;\n self.ctx.valueFontSize = self.ctx.width/6;\n }\n }\n self.ctx.padding = Math.min(12, self.ctx.padding);\n \n if (self.ctx.labelCell) {\n self.ctx.labelCell.css('font-size', labelFontSize+'px');\n self.ctx.labelCell.css('padding', self.ctx.padding+'px');\n }\n if (self.ctx.valueCell) {\n self.ctx.valueCell.css('font-size', self.ctx.valueFontSize+'px');\n self.ctx.valueCell.css('padding', self.ctx.padding+'px');\n } \n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1\n };\n};\n\n\nself.onDestroy = function() {\n};\n",
+ "controllerScript": "self.onInit = function() {\n\n self.ctx.labelPosition = self.ctx.settings.labelPosition || 'left';\n \n if (self.ctx.datasources.length > 0) {\n var tbDatasource = self.ctx.datasources[0];\n var datasourceId = 'tbDatasource' + 0;\n self.ctx.$container.append(\n \"<div id='\" + datasourceId +\n \"' class='tbDatasource-container'></div>\"\n );\n \n self.ctx.datasourceContainer = $('#' + datasourceId,\n self.ctx.$container);\n \n var tableId = 'table' + 0;\n self.ctx.datasourceContainer.append(\n \"<table id='\" + tableId +\n \"' class='tbDatasource-table'><col width='30%'><col width='70%'></table>\"\n );\n var table = $('#' + tableId, self.ctx.$container);\n if (self.ctx.labelPosition === 'top') {\n table.css('text-align', 'left');\n }\n \n if (tbDatasource.dataKeys.length > 0) {\n var dataKey = tbDatasource.dataKeys[0];\n var labelCellId = 'labelCell' + 0;\n var cellId = 'cell' + 0;\n if (self.ctx.labelPosition === 'left') {\n table.append(\n \"<tr><td class='tbDatasource-data-key' id='\" + labelCellId +\"'>\" +\n dataKey.label +\n \"</td><td class='tbDatasource-value' id='\" +\n cellId +\n \"'></td></tr>\");\n } else {\n table.append(\n \"<tr style='vertical-align: bottom;'><td class='tbDatasource-data-key' id='\" + labelCellId +\"'>\" +\n dataKey.label +\n \"</td></tr><tr><td class='tbDatasource-value' id='\" +\n cellId +\n \"'></td></tr>\");\n }\n self.ctx.labelCell = $('#' + labelCellId, table);\n self.ctx.valueCell = $('#' + cellId, table);\n self.ctx.valueCell.html(0 + ' ' + self.ctx.units);\n }\n }\n \n $.fn.textWidth = function(){\n var html_org = $(this).html();\n var html_calc = '<span>' + html_org + '</span>';\n $(this).html(html_calc);\n var width = $(this).find('span:first').width();\n $(this).html(html_org);\n return width;\n }; \n \n self.onResize();\n};\n\nself.onDataUpdated = function() {\n \n function isNumber(n) {\n return !isNaN(parseFloat(n)) && isFinite(n);\n }\n\n if (self.ctx.valueCell && self.ctx.data.length > 0) {\n var cellData = self.ctx.data[0];\n if (cellData.data.length > 0) {\n var tvPair = cellData.data[cellData.data.length -\n 1];\n var value = tvPair[1];\n var txtValue;\n if (isNumber(value)) {\n var decimals = self.ctx.decimals;\n var units = self.ctx.units;\n if (self.ctx.datasources.length > 0 && self.ctx.datasources[0].dataKeys.length > 0) {\n dataKey = self.ctx.datasources[0].dataKeys[0];\n if (dataKey.decimals || dataKey.decimals === 0) {\n decimals = dataKey.decimals;\n }\n if (dataKey.units) {\n units = dataKey.units;\n }\n }\n txtValue = self.ctx.utils.formatValue(value, decimals, units, true);\n } else {\n txtValue = value;\n }\n self.ctx.valueCell.html(txtValue);\n var targetWidth;\n var minDelta;\n if (self.ctx.labelPosition === 'left') {\n targetWidth = self.ctx.datasourceContainer.width() - self.ctx.labelCell.width();\n minDelta = self.ctx.width/16 + self.ctx.padding;\n } else {\n targetWidth = self.ctx.datasourceContainer.width();\n minDelta = self.ctx.padding;\n }\n var delta = targetWidth - self.ctx.valueCell.textWidth();\n var fontSize = self.ctx.valueFontSize;\n if (targetWidth > minDelta) {\n while (delta < minDelta && fontSize > 6) {\n fontSize--;\n self.ctx.valueCell.css('font-size', fontSize+'px');\n delta = targetWidth - self.ctx.valueCell.textWidth();\n }\n }\n }\n } \n \n};\n\nself.onResize = function() {\n var labelFontSize;\n if (self.ctx.labelPosition === 'top') {\n self.ctx.padding = self.ctx.height/20;\n labelFontSize = self.ctx.height/4;\n self.ctx.valueFontSize = self.ctx.height/2;\n } else {\n self.ctx.padding = self.ctx.width/50;\n labelFontSize = self.ctx.height/2.5;\n self.ctx.valueFontSize = self.ctx.height/2;\n if (self.ctx.width/self.ctx.height <= 2.7) {\n labelFontSize = self.ctx.width/7;\n self.ctx.valueFontSize = self.ctx.width/6;\n }\n }\n self.ctx.padding = Math.min(12, self.ctx.padding);\n \n if (self.ctx.labelCell) {\n self.ctx.labelCell.css('font-size', labelFontSize+'px');\n self.ctx.labelCell.css('padding', self.ctx.padding+'px');\n }\n if (self.ctx.valueCell) {\n self.ctx.valueCell.css('font-size', self.ctx.valueFontSize+'px');\n self.ctx.valueCell.css('padding', self.ctx.padding+'px');\n } \n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1\n };\n};\n\n\nself.onDestroy = function() {\n};\n",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"labelPosition\": {\n \"title\": \"Label position\",\n \"type\": \"string\",\n \"default\": \"left\"\n }\n },\n \"required\": []\n },\n \"form\": [\n {\n \"key\": \"labelPosition\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"left\",\n \"label\": \"Left\"\n },\n {\n \"value\": \"top\",\n \"label\": \"Top\"\n }\n ]\n }\n ]\n}",
"dataKeySettingsSchema": "{}\n",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#ff5722\",\"color\":\"rgba(255, 255, 255, 0.87)\",\"padding\":\"16px\",\"settings\":{\"labelPosition\":\"top\"},\"title\":\"Simple card\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"°C\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{}}"
diff --git a/application/src/main/data/json/system/widget_bundles/charts.json b/application/src/main/data/json/system/widget_bundles/charts.json
index a264ba6..71e9fa7 100644
--- a/application/src/main/data/json/system/widget_bundles/charts.json
+++ b/application/src/main/data/json/system/widget_bundles/charts.json
@@ -35,7 +35,7 @@
"resources": [],
"templateHtml": "",
"templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n",
- "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.settingsSchema;\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(true);\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n",
+ "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.settingsSchema('graph');\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(true);\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n",
"settingsSchema": "{}",
"dataKeySettingsSchema": "{}",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"legend\":{\"show\":true,\"position\":\"nw\",\"backgroundColor\":\"#f0f0f0\",\"backgroundOpacity\":0.85,\"labelBoxBorderColor\":\"rgba(1, 1, 1, 0.45)\"},\"decimals\":1,\"stack\":false,\"tooltipIndividual\":false},\"title\":\"Timeseries - Flot\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null}"
@@ -147,10 +147,10 @@
"resources": [],
"templateHtml": "",
"templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n",
- "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'bar'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.settingsSchema;\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(false);\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n",
+ "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'bar'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.settingsSchema('bar');\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(false);\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n",
"settingsSchema": "{}",
"dataKeySettingsSchema": "{}",
- "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":false,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":false,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000},\"aggregation\":{\"limit\":200,\"type\":\"AVG\"}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"legend\":{\"show\":true,\"position\":\"nw\",\"backgroundColor\":\"#f0f0f0\",\"backgroundOpacity\":0.85,\"labelBoxBorderColor\":\"rgba(1, 1, 1, 0.45)\"},\"decimals\":1,\"stack\":true,\"tooltipIndividual\":false},\"title\":\"Timeseries Bars - Flot\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null}"
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":false,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":false,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000},\"aggregation\":{\"limit\":200,\"type\":\"AVG\"}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"stack\":true,\"tooltipIndividual\":false,\"defaultBarWidth\":600},\"title\":\"Timeseries Bars - Flot\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":true,\"actions\":{}}"
}
},
{
@@ -163,7 +163,7 @@
"resources": [],
"templateHtml": "",
"templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n",
- "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'state'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.typeParameters = function() {\n return {\n stateData: true\n };\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.settingsSchema;\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(true);\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n",
+ "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'state'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.typeParameters = function() {\n return {\n stateData: true\n };\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.settingsSchema('graph');\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(true);\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n",
"settingsSchema": "{}",
"dataKeySettingsSchema": "{}",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 1\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false,\"axisPosition\":\"left\",\"showSeparateAxis\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"return Math.random() > 0.5 ? 1 : 0;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 2\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false,\"axisPosition\":\"left\"},\"_hash\":0.12775350966079668,\"funcBody\":\"return Math.random() <= 0.5 ? 1 : 0;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\",\"ticksFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"stack\":false,\"tooltipIndividual\":false,\"tooltipValueFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\",\"smoothLines\":false},\"title\":\"State Chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":true,\"actions\":{},\"legendConfig\":{\"position\":\"bottom\",\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false}}"
diff --git a/application/src/main/data/upgrade/2.1.1/schema_update.cql b/application/src/main/data/upgrade/2.1.1/schema_update.cql
index c477e8a..36ac8e4 100644
--- a/application/src/main/data/upgrade/2.1.1/schema_update.cql
+++ b/application/src/main/data/upgrade/2.1.1/schema_update.cql
@@ -20,7 +20,7 @@ DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_customer;
DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_entity_id;
DROP TABLE IF EXISTS thingsboard.entity_views;
-ControllerSqlTestSuite
+
CREATE TABLE IF NOT EXISTS thingsboard.entity_views (
id timeuuid,
entity_id timeuuid,
diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml
index 720bb9f..f35df67 100644
--- a/application/src/main/resources/thingsboard.yml
+++ b/application/src/main/resources/thingsboard.yml
@@ -391,6 +391,7 @@ audit_log:
"user": "${AUDIT_LOG_MASK_USER:W}"
"rule_chain": "${AUDIT_LOG_MASK_RULE_CHAIN:W}"
"alarm": "${AUDIT_LOG_MASK_ALARM:W}"
+ "entity_view": "${AUDIT_LOG_MASK_RULE_CHAIN:W}"
sink:
# Type of external sink. possible options: none, elasticsearch
type: "${AUDIT_LOG_SINK_TYPE:none}"
diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java
index f0fb51e..2e7b412 100644
--- a/common/transport/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java
+++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java
@@ -73,4 +73,6 @@ public abstract class DeviceAwareSessionContext implements SessionContext {
public Device getDevice() {
return device;
}
+
+
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java
index f35b890..ebd0560 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java
@@ -54,6 +54,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
+import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import static org.thingsboard.server.common.data.CacheConstants.ASSET_CACHE;
@@ -141,7 +142,7 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
if (entityViews != null && !entityViews.isEmpty()) {
throw new DataValidationException("Can't delete asset that is assigned to entity views!");
}
- } catch (Exception e) {
+ } catch (ExecutionException | InterruptedException e) {
log.error("Exception while finding entity views for assetId [{}]", assetId, e);
throw new RuntimeException("Exception while finding entity views for assetId [" + assetId + "]", e);
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
index 6f9ea62..2be5ba4 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
@@ -58,6 +58,7 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
+import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import static org.thingsboard.server.common.data.CacheConstants.DEVICE_CACHE;
@@ -158,7 +159,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
if (entityViews != null && !entityViews.isEmpty()) {
throw new DataValidationException("Can't delete device that is assigned to entity views!");
}
- } catch (Exception e) {
+ } catch (ExecutionException | InterruptedException e) {
log.error("Exception while finding entity views for deviceId [{}]", deviceId, e);
throw new RuntimeException("Exception while finding entity views for deviceId [" + deviceId + "]", e);
}
diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java
index f0b29cb..c8baaf7 100644
--- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java
+++ b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java
@@ -170,7 +170,7 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor {
private MqttPublishMessage createMqttPublishMsg(DeviceSessionCtx ctx, String topic, JsonElement json) {
MqttFixedHeader mqttFixedHeader =
- new MqttFixedHeader(MqttMessageType.PUBLISH, false, MqttQoS.AT_LEAST_ONCE, false, 0);
+ new MqttFixedHeader(MqttMessageType.PUBLISH, false, ctx.getQoSForTopic(topic), false, 0);
MqttPublishVariableHeader header = new MqttPublishVariableHeader(topic, ctx.nextMsgId());
ByteBuf payload = ALLOCATOR.buffer();
payload.writeBytes(GSON.toJson(json).getBytes(UTF8));
diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java
index 185b7a8..ed0c8c6 100644
--- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java
+++ b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java
@@ -51,6 +51,9 @@ import javax.security.cert.X509Certificate;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.*;
import static io.netty.handler.codec.mqtt.MqttMessageType.*;
@@ -75,6 +78,8 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
private final RelationService relationService;
private final QuotaService quotaService;
private final SslHandler sslHandler;
+ private final ConcurrentMap<String, Integer> mqttQoSMap;
+
private volatile boolean connected;
private volatile InetSocketAddress address;
private volatile GatewaySessionCtx gatewaySessionCtx;
@@ -86,7 +91,8 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
this.relationService = relationService;
this.authService = authService;
this.adaptor = adaptor;
- this.deviceSessionCtx = new DeviceSessionCtx(processor, authService, adaptor);
+ this.mqttQoSMap = new ConcurrentHashMap<>();
+ this.deviceSessionCtx = new DeviceSessionCtx(processor, authService, adaptor, mqttQoSMap);
this.sessionId = deviceSessionCtx.getSessionId().toUidStr();
this.sslHandler = sslHandler;
this.quotaService = quotaService;
@@ -166,18 +172,25 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
private void handleMqttPublishMsg(String topicName, int msgId, MqttPublishMessage mqttMsg) {
try {
- if (topicName.equals(GATEWAY_TELEMETRY_TOPIC)) {
- gatewaySessionCtx.onDeviceTelemetry(mqttMsg);
- } else if (topicName.equals(GATEWAY_ATTRIBUTES_TOPIC)) {
- gatewaySessionCtx.onDeviceAttributes(mqttMsg);
- } else if (topicName.equals(GATEWAY_ATTRIBUTES_REQUEST_TOPIC)) {
- gatewaySessionCtx.onDeviceAttributesRequest(mqttMsg);
- } else if (topicName.equals(GATEWAY_RPC_TOPIC)) {
- gatewaySessionCtx.onDeviceRpcResponse(mqttMsg);
- } else if (topicName.equals(GATEWAY_CONNECT_TOPIC)) {
- gatewaySessionCtx.onDeviceConnect(mqttMsg);
- } else if (topicName.equals(GATEWAY_DISCONNECT_TOPIC)) {
- gatewaySessionCtx.onDeviceDisconnect(mqttMsg);
+ switch (topicName) {
+ case GATEWAY_TELEMETRY_TOPIC:
+ gatewaySessionCtx.onDeviceTelemetry(mqttMsg);
+ break;
+ case GATEWAY_ATTRIBUTES_TOPIC:
+ gatewaySessionCtx.onDeviceAttributes(mqttMsg);
+ break;
+ case GATEWAY_ATTRIBUTES_REQUEST_TOPIC:
+ gatewaySessionCtx.onDeviceAttributesRequest(mqttMsg);
+ break;
+ case GATEWAY_RPC_TOPIC:
+ gatewaySessionCtx.onDeviceRpcResponse(mqttMsg);
+ break;
+ case GATEWAY_CONNECT_TOPIC:
+ gatewaySessionCtx.onDeviceConnect(mqttMsg);
+ break;
+ case GATEWAY_DISCONNECT_TOPIC:
+ gatewaySessionCtx.onDeviceDisconnect(mqttMsg);
+ break;
}
} catch (RuntimeException | AdaptorException e) {
log.warn("[{}] Failed to process publish msg [{}][{}]", sessionId, topicName, msgId, e);
@@ -225,52 +238,71 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
log.trace("[{}] Processing subscription [{}]!", sessionId, mqttMsg.variableHeader().messageId());
List<Integer> grantedQoSList = new ArrayList<>();
for (MqttTopicSubscription subscription : mqttMsg.payload().topicSubscriptions()) {
- String topicName = subscription.topicName();
- //TODO: handle this qos level.
+ String topic = subscription.topicName();
MqttQoS reqQoS = subscription.qualityOfService();
try {
- if (topicName.equals(DEVICE_ATTRIBUTES_TOPIC)) {
- AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, SUBSCRIBE_ATTRIBUTES_REQUEST, mqttMsg);
- processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg));
- grantedQoSList.add(getMinSupportedQos(reqQoS));
- } else if (topicName.equals(DEVICE_RPC_REQUESTS_SUB_TOPIC)) {
- AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, SUBSCRIBE_RPC_COMMANDS_REQUEST, mqttMsg);
- processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg));
- grantedQoSList.add(getMinSupportedQos(reqQoS));
- } else if (topicName.equals(DEVICE_RPC_RESPONSE_SUB_TOPIC)) {
- grantedQoSList.add(getMinSupportedQos(reqQoS));
- } else if (topicName.equals(DEVICE_ATTRIBUTES_RESPONSES_TOPIC)) {
- deviceSessionCtx.setAllowAttributeResponses();
- grantedQoSList.add(getMinSupportedQos(reqQoS));
- } else if (topicName.equals(GATEWAY_ATTRIBUTES_TOPIC)) {
- grantedQoSList.add(getMinSupportedQos(reqQoS));
- } else {
- log.warn("[{}] Failed to subscribe to [{}][{}]", sessionId, topicName, reqQoS);
- grantedQoSList.add(FAILURE.value());
+ switch (topic) {
+ case DEVICE_ATTRIBUTES_TOPIC: {
+ AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, SUBSCRIBE_ATTRIBUTES_REQUEST, mqttMsg);
+ processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg));
+ registerSubQoS(topic, grantedQoSList, reqQoS);
+ break;
+ }
+ case DEVICE_RPC_REQUESTS_SUB_TOPIC: {
+ AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, SUBSCRIBE_RPC_COMMANDS_REQUEST, mqttMsg);
+ processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg));
+ registerSubQoS(topic, grantedQoSList, reqQoS);
+ break;
+ }
+ case DEVICE_RPC_RESPONSE_SUB_TOPIC:
+ case GATEWAY_ATTRIBUTES_TOPIC:
+ case GATEWAY_RPC_TOPIC:
+ registerSubQoS(topic, grantedQoSList, reqQoS);
+ break;
+ case DEVICE_ATTRIBUTES_RESPONSES_TOPIC:
+ deviceSessionCtx.setAllowAttributeResponses();
+ registerSubQoS(topic, grantedQoSList, reqQoS);
+ break;
+ default:
+ log.warn("[{}] Failed to subscribe to [{}][{}]", sessionId, topic, reqQoS);
+ grantedQoSList.add(FAILURE.value());
+ break;
}
} catch (AdaptorException e) {
- log.warn("[{}] Failed to subscribe to [{}][{}]", sessionId, topicName, reqQoS);
+ log.warn("[{}] Failed to subscribe to [{}][{}]", sessionId, topic, reqQoS);
grantedQoSList.add(FAILURE.value());
}
}
ctx.writeAndFlush(createSubAckMessage(mqttMsg.variableHeader().messageId(), grantedQoSList));
}
+ private void registerSubQoS(String topic, List<Integer> grantedQoSList, MqttQoS reqQoS) {
+ grantedQoSList.add(getMinSupportedQos(reqQoS));
+ mqttQoSMap.put(topic, getMinSupportedQos(reqQoS));
+ }
+
private void processUnsubscribe(ChannelHandlerContext ctx, MqttUnsubscribeMessage mqttMsg) {
if (!checkConnected(ctx)) {
return;
}
log.trace("[{}] Processing subscription [{}]!", sessionId, mqttMsg.variableHeader().messageId());
for (String topicName : mqttMsg.payload().topics()) {
+ mqttQoSMap.remove(topicName);
try {
- if (topicName.equals(DEVICE_ATTRIBUTES_TOPIC)) {
- AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, UNSUBSCRIBE_ATTRIBUTES_REQUEST, mqttMsg);
- processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg));
- } else if (topicName.equals(DEVICE_RPC_REQUESTS_SUB_TOPIC)) {
- AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, UNSUBSCRIBE_RPC_COMMANDS_REQUEST, mqttMsg);
- processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg));
- } else if (topicName.equals(DEVICE_ATTRIBUTES_RESPONSES_TOPIC)) {
- deviceSessionCtx.setDisallowAttributeResponses();
+ switch (topicName) {
+ case DEVICE_ATTRIBUTES_TOPIC: {
+ AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, UNSUBSCRIBE_ATTRIBUTES_REQUEST, mqttMsg);
+ processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg));
+ break;
+ }
+ case DEVICE_RPC_REQUESTS_SUB_TOPIC: {
+ AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, UNSUBSCRIBE_RPC_COMMANDS_REQUEST, mqttMsg);
+ processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg));
+ break;
+ }
+ case DEVICE_ATTRIBUTES_RESPONSES_TOPIC:
+ deviceSessionCtx.setDisallowAttributeResponses();
+ break;
}
} catch (AdaptorException e) {
log.warn("[{}] Failed to process unsubscription [{}] to [{}]", sessionId, mqttMsg.variableHeader().messageId(), topicName);
diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java
index 9367a04..3dbb3ef 100644
--- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java
+++ b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java
@@ -30,13 +30,15 @@ import org.thingsboard.server.common.transport.auth.DeviceAuthService;
import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext;
import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor;
+import java.util.Map;
+import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author Andrew Shvayka
*/
@Slf4j
-public class DeviceSessionCtx extends DeviceAwareSessionContext {
+public class DeviceSessionCtx extends MqttDeviceAwareSessionContext {
private final MqttTransportAdaptor adaptor;
private final MqttSessionId sessionId;
@@ -44,8 +46,8 @@ public class DeviceSessionCtx extends DeviceAwareSessionContext {
private volatile boolean allowAttributeResponses;
private AtomicInteger msgIdSeq = new AtomicInteger(0);
- public DeviceSessionCtx(SessionMsgProcessor processor, DeviceAuthService authService, MqttTransportAdaptor adaptor) {
- super(processor, authService);
+ public DeviceSessionCtx(SessionMsgProcessor processor, DeviceAuthService authService, MqttTransportAdaptor adaptor, ConcurrentMap<String, Integer> mqttQoSMap) {
+ super(processor, authService, mqttQoSMap);
this.adaptor = adaptor;
this.sessionId = new MqttSessionId();
}
diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java
index dd9c921..e5be5c7 100644
--- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java
+++ b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java
@@ -38,13 +38,15 @@ import org.thingsboard.server.transport.mqtt.MqttTransportHandler;
import java.nio.charset.Charset;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
+import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Created by ashvayka on 19.01.17.
*/
-public class GatewayDeviceSessionCtx extends DeviceAwareSessionContext {
+public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext {
private static final Gson GSON = new Gson();
private static final Charset UTF8 = Charset.forName("UTF-8");
@@ -56,8 +58,8 @@ public class GatewayDeviceSessionCtx extends DeviceAwareSessionContext {
private volatile boolean closed;
private AtomicInteger msgIdSeq = new AtomicInteger(0);
- public GatewayDeviceSessionCtx(GatewaySessionCtx parent, Device device) {
- super(parent.getProcessor(), parent.getAuthService(), device);
+ public GatewayDeviceSessionCtx(GatewaySessionCtx parent, Device device, ConcurrentMap<String, Integer> mqttQoSMap) {
+ super(parent.getProcessor(), parent.getAuthService(), device, mqttQoSMap);
this.parent = parent;
this.sessionId = new MqttSessionId();
}
@@ -195,7 +197,7 @@ public class GatewayDeviceSessionCtx extends DeviceAwareSessionContext {
private MqttPublishMessage createMqttPublishMsg(String topic, JsonElement json) {
MqttFixedHeader mqttFixedHeader =
- new MqttFixedHeader(MqttMessageType.PUBLISH, false, MqttQoS.AT_LEAST_ONCE, false, 0);
+ new MqttFixedHeader(MqttMessageType.PUBLISH, false, getQoSForTopic(topic), false, 0);
MqttPublishVariableHeader header = new MqttPublishVariableHeader(topic, msgIdSeq.incrementAndGet());
ByteBuf payload = ALLOCATOR.buffer();
payload.writeBytes(GSON.toJson(json).getBytes(UTF8));
diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java
index 69ed17f..98ad6d2 100644
--- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java
+++ b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java
@@ -43,6 +43,7 @@ import org.thingsboard.server.transport.mqtt.MqttTransportHandler;
import org.thingsboard.server.transport.mqtt.adaptors.JsonMqttAdaptor;
import java.util.*;
+import java.util.concurrent.ConcurrentMap;
import java.util.stream.Collectors;
import static org.thingsboard.server.transport.mqtt.adaptors.JsonMqttAdaptor.validateJsonPayload;
@@ -63,6 +64,7 @@ public class GatewaySessionCtx {
private final DeviceAuthService authService;
private final RelationService relationService;
private final Map<String, GatewayDeviceSessionCtx> devices;
+ private final ConcurrentMap<String, Integer> mqttQoSMap;
private ChannelHandlerContext channel;
public GatewaySessionCtx(SessionMsgProcessor processor, DeviceService deviceService, DeviceAuthService authService, RelationService relationService, DeviceSessionCtx gatewaySessionCtx) {
@@ -73,6 +75,7 @@ public class GatewaySessionCtx {
this.gateway = gatewaySessionCtx.getDevice();
this.gatewaySessionId = gatewaySessionCtx.getSessionId();
this.devices = new HashMap<>();
+ this.mqttQoSMap = gatewaySessionCtx.getMqttQoSMap();
}
public void onDeviceConnect(MqttPublishMessage msg) throws AdaptorException {
@@ -96,7 +99,7 @@ public class GatewaySessionCtx {
relationService.saveRelationAsync(new EntityRelation(gateway.getId(), device.getId(), "Created"));
processor.onDeviceAdded(device);
}
- GatewayDeviceSessionCtx ctx = new GatewayDeviceSessionCtx(this, device);
+ GatewayDeviceSessionCtx ctx = new GatewayDeviceSessionCtx(this, device, mqttQoSMap);
devices.put(deviceName, ctx);
log.debug("[{}] Added device [{}] to the gateway session", gatewaySessionId, deviceName);
processor.process(new BasicTransportToDeviceSessionActorMsg(device, new BasicAdaptorToSessionActorMsg(ctx, new AttributesSubscribeMsg())));
diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttDeviceAwareSessionContext.java b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttDeviceAwareSessionContext.java
new file mode 100644
index 0000000..f085064
--- /dev/null
+++ b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttDeviceAwareSessionContext.java
@@ -0,0 +1,57 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.transport.mqtt.session;
+
+import io.netty.handler.codec.mqtt.MqttQoS;
+import org.thingsboard.server.common.data.Device;
+import org.thingsboard.server.common.transport.SessionMsgProcessor;
+import org.thingsboard.server.common.transport.auth.DeviceAuthService;
+import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * Created by ashvayka on 30.08.18.
+ */
+public abstract class MqttDeviceAwareSessionContext extends DeviceAwareSessionContext {
+
+ private final ConcurrentMap<String, Integer> mqttQoSMap;
+
+ public MqttDeviceAwareSessionContext(SessionMsgProcessor processor, DeviceAuthService authService, ConcurrentMap<String, Integer> mqttQoSMap) {
+ super(processor, authService);
+ this.mqttQoSMap = mqttQoSMap;
+ }
+
+ public MqttDeviceAwareSessionContext(SessionMsgProcessor processor, DeviceAuthService authService, Device device, ConcurrentMap<String, Integer> mqttQoSMap) {
+ super(processor, authService, device);
+ this.mqttQoSMap = mqttQoSMap;
+ }
+
+ public ConcurrentMap<String, Integer> getMqttQoSMap() {
+ return mqttQoSMap;
+ }
+
+ public MqttQoS getQoSForTopic(String topic) {
+ Integer qos = mqttQoSMap.get(topic);
+ if (qos != null) {
+ return MqttQoS.valueOf(qos);
+ } else {
+ return MqttQoS.AT_LEAST_ONCE;
+ }
+ }
+
+}
ui/.stylelintrc 6(+3 -3)
diff --git a/ui/.stylelintrc b/ui/.stylelintrc
index c145c8b..e878729 100644
--- a/ui/.stylelintrc
+++ b/ui/.stylelintrc
@@ -251,7 +251,7 @@
"fill",
"stroke"
],
- "property-no-vendor-prefix": null,
+ "property-no-vendor-prefix": true,
"rule-empty-line-before": ["always", {
"except": ["first-nested"],
"ignore": ["after-comment"]
@@ -272,7 +272,7 @@
"selector-max-type": 5,
"selector-max-universal": 1,
"selector-no-qualifying-type": null,
- "selector-no-vendor-prefix": null,
+ "selector-no-vendor-prefix": true,
"selector-type-no-unknown": [true, {
"ignoreTypes": [
"/^md-/",
@@ -287,6 +287,6 @@
"value-list-comma-newline-after": "always-multi-line",
"value-list-comma-newline-before": "never-multi-line",
"value-list-comma-space-after": "always-single-line",
- "value-no-vendor-prefix": null
+ "value-no-vendor-prefix": true
}
}
ui/package.json 16(+15 -1)
diff --git a/ui/package.json b/ui/package.json
index 417948e..7874170 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -119,7 +119,7 @@
"ng-annotate-loader": "^0.1.1",
"ngtemplate-loader": "^1.3.1",
"node-sass": "^4.5.3",
- "postcss-loader": "^0.13.0",
+ "postcss-loader": "^3.0.0",
"raw-loader": "^0.5.1",
"react-hot-loader": "^3.0.0-beta.6",
"sass-loader": "^4.0.2",
@@ -145,5 +145,19 @@
"node_modules",
"target"
]
+ },
+ "browserslist": [
+ "> 0.5%",
+ "last 2 versions",
+ "Firefox ESR",
+ "not ie <= 10",
+ "not ie_mob <= 10",
+ "not bb <= 10",
+ "not op_mob <= 12.1"
+ ],
+ "postcss": {
+ "plugins": {
+ "autoprefixer": true
+ }
}
}
ui/src/app/components/dashboard.scss 4(+2 -2)
diff --git a/ui/src/app/components/dashboard.scss b/ui/src/app/components/dashboard.scss
index e0a9afd..b4a696a 100644
--- a/ui/src/app/components/dashboard.scss
+++ b/ui/src/app/components/dashboard.scss
@@ -22,7 +22,7 @@ div.tb-widget {
overflow: hidden;
outline: none;
- @include transition(all .2s ease-in-out);
+ transition: all .2s ease-in-out;
.tb-widget-title {
max-height: 60px;
@@ -99,7 +99,7 @@ md-content.tb-dashboard-content {
outline: none;
.gridster-item {
- @include transition(none);
+ transition: none;
}
}
ui/src/app/components/grid.scss 4(+2 -2)
diff --git a/ui/src/app/components/grid.scss b/ui/src/app/components/grid.scss
index 4313848..50e110b 100644
--- a/ui/src/app/components/grid.scss
+++ b/ui/src/app/components/grid.scss
@@ -20,7 +20,7 @@
}
.tb-card-item {
- @include transition(all .2s ease-in-out);
+ transition: all .2s ease-in-out;
md-card-content {
max-height: 53px;
@@ -46,7 +46,7 @@
.tb-current-item {
opacity: .5;
- @include transform(scale(1.05));
+ transform: scale(1.05);
}
#tb-vertical-container {
ui/src/app/components/menu-link.scss 6(+3 -3)
diff --git a/ui/src/app/components/menu-link.scss b/ui/src/app/components/menu-link.scss
index 299797c..be274d3 100644
--- a/ui/src/app/components/menu-link.scss
+++ b/ui/src/app/components/menu-link.scss
@@ -16,7 +16,7 @@
@import "~compass-sass-mixins/lib/compass";
.md-button-toggle .md-toggle-icon.tb-toggled {
- @include transform(rotateZ(180deg));
+ transform: rotateZ(180deg);
}
.tb-menu-toggle-list.ng-hide {
@@ -28,7 +28,7 @@
z-index: 1;
overflow: hidden;
- @include transition(.75s cubic-bezier(.35, 0, .25, 1));
+ transition: .75s cubic-bezier(.35, 0, .25, 1);
- @include transition-property(height);
+ transition-property: height;
}
diff --git a/ui/src/app/components/react/json-form.scss b/ui/src/app/components/react/json-form.scss
index cca9d07..d324abe 100644
--- a/ui/src/app/components/react/json-form.scss
+++ b/ui/src/app/components/react/json-form.scss
@@ -28,7 +28,7 @@ $input-label-float-scale: .75 !default;
line-height: 12px;
color: rgb(244, 67, 54);
- @include transition(all 450ms cubic-bezier(.23, 1, .32, 1) 0ms);
+ transition: all 450ms cubic-bezier(.23, 1, .32, 1) 0ms;
}
.tb-container {
@@ -77,13 +77,12 @@ label.tb-label {
bottom: 100%;
left: 0;
color: rgba(0, 0, 0, .54);
- transform-origin: left top;
- -webkit-font-smoothing: antialiased;
- @include transform(translate3d(0, $input-label-float-offset, 0) scale($input-label-float-scale));
+ transition: transform $swift-ease-out-timing-function $swift-ease-out-duration, width $swift-ease-out-timing-function $swift-ease-out-duration;
- @include transition(transform $swift-ease-out-timing-function $swift-ease-out-duration,
- width $swift-ease-out-timing-function $swift-ease-out-duration);
+ transform: translate3d(0, $input-label-float-offset, 0) scale($input-label-float-scale);
+ transform-origin: left top;
+ -webkit-font-smoothing: antialiased;
&.tb-focused {
color: rgb(96, 125, 139);
ui/src/app/components/side-menu.scss 2(+1 -1)
diff --git a/ui/src/app/components/side-menu.scss b/ui/src/app/components/side-menu.scss
index 46da8db..f6efcb9 100644
--- a/ui/src/app/components/side-menu.scss
+++ b/ui/src/app/components/side-menu.scss
@@ -53,7 +53,7 @@
margin: auto 0 auto auto;
background-size: 100% auto;
- @include transition(transform .3s, ease-in-out);
+ transition: transform .3s, ease-in-out;
}
.tb-side-menu .md-button {
diff --git a/ui/src/app/components/widget/action/manage-widget-actions.directive.js b/ui/src/app/components/widget/action/manage-widget-actions.directive.js
index 88a37a8..8110322 100644
--- a/ui/src/app/components/widget/action/manage-widget-actions.directive.js
+++ b/ui/src/app/components/widget/action/manage-widget-actions.directive.js
@@ -244,13 +244,18 @@ function ManageWidgetActionsController($rootScope, $scope, $document, $mdDialog,
vm.widgetActions[actionSourceId] = targetActions;
}
if (prevActionId) {
- var index = getActionIndex(prevActionId, vm.allActions);
- if (index > -1) {
- vm.allActions[index] = action;
+ const indexInTarget = getActionIndex(prevActionId, targetActions);
+ const indexInAllActions = getActionIndex(prevActionId, vm.allActions);
+ if (indexInTarget > -1) {
+ targetActions[indexInTarget] = widgetAction;
+ } else if (indexInAllActions > -1) {
+ const prevActionSourceId = vm.allActions[indexInAllActions].actionSourceId;
+ const index = getActionIndex(prevActionId,vm.widgetActions[prevActionSourceId]);
+ vm.widgetActions[prevActionSourceId].splice(index,1);
+ targetActions.push(widgetAction);
}
- index = getActionIndex(prevActionId, targetActions);
- if (index > -1) {
- targetActions[index] = widgetAction;
+ if (indexInAllActions > -1) {
+ vm.allActions[indexInAllActions] = action;
}
} else {
vm.allActions.push(action);
ui/src/app/dashboard/dashboard.scss 10(+5 -5)
diff --git a/ui/src/app/dashboard/dashboard.scss b/ui/src/app/dashboard/dashboard.scss
index 00a0653..715c4da 100644
--- a/ui/src/app/dashboard/dashboard.scss
+++ b/ui/src/app/dashboard/dashboard.scss
@@ -75,13 +75,13 @@ section.tb-dashboard-toolbar {
&.tb-dashboard-toolbar-opened {
right: 0;
- // @include transition(right .3s cubic-bezier(.55,0,.55,.2));
+ // transition: right .3s cubic-bezier(.55, 0, .55, .2);
}
&.tb-dashboard-toolbar-closed {
right: 18px;
- @include transition(right .3s cubic-bezier(.55,0,.55,.2) .2s);
+ transition: right .3s cubic-bezier(.55, 0, .55, .2) .2s;
}
}
@@ -102,14 +102,14 @@ section.tb-dashboard-toolbar {
margin-top: $toolbar-height;
}
- @include transition(margin-top .3s cubic-bezier(.55,0,.55,.2));
+ transition: margin-top .3s cubic-bezier(.55, 0, .55, .2);
}
}
&.tb-dashboard-toolbar-closed {
margin-top: 0;
- @include transition(margin-top .3s cubic-bezier(.55,0,.55,.2) .2s);
+ transition: margin-top .3s cubic-bezier(.55, 0, .55, .2) .2s;
}
.tb-dashboard-layouts {
@@ -133,7 +133,7 @@ section.tb-powered-by-footer {
position: absolute;
right: 25px;
bottom: 5px;
- z-index: 3;
+ z-index: 30;
pointer-events: none;
span {
diff --git a/ui/src/app/dashboard/dashboard.tpl.html b/ui/src/app/dashboard/dashboard.tpl.html
index 829f174..79653a4 100644
--- a/ui/src/app/dashboard/dashboard.tpl.html
+++ b/ui/src/app/dashboard/dashboard.tpl.html
@@ -146,7 +146,7 @@
ng-style="{minWidth: vm.rightLayoutWidth(),
maxWidth: vm.rightLayoutWidth(),
height: vm.rightLayoutHeight(),
- zIndex: 12}"
+ zIndex: 25}"
md-component-id="right-dashboard-layout"
aria-label="Right dashboard layout"
md-is-open="vm.rightLayoutOpened"
diff --git a/ui/src/app/dashboard/dashboard-card.scss b/ui/src/app/dashboard/dashboard-card.scss
index 8e6626c..111d67a 100644
--- a/ui/src/app/dashboard/dashboard-card.scss
+++ b/ui/src/app/dashboard/dashboard-card.scss
@@ -16,12 +16,12 @@
.tb-dashboard-assigned-customers {
display: block;
- display: -webkit-box;
+ display: -webkit-box; /* stylelint-disable-line value-no-vendor-prefix */
height: 34px;
margin-bottom: 4px;
overflow: hidden;
text-overflow: ellipsis;
-webkit-line-clamp: 2;
- -webkit-box-orient: vertical;
+ -webkit-box-orient: vertical; /* stylelint-disable-line property-no-vendor-prefix */
}
diff --git a/ui/src/app/dashboard/dashboard-toolbar.scss b/ui/src/app/dashboard/dashboard-toolbar.scss
index 80fd876..c247634 100644
--- a/ui/src/app/dashboard/dashboard-toolbar.scss
+++ b/ui/src/app/dashboard/dashboard-toolbar.scss
@@ -31,7 +31,7 @@ tb-dashboard-toolbar {
&.md-fab {
opacity: 1;
- @include transition(opacity .3s cubic-bezier(.55,0,.55,.2));
+ transition: opacity .3s cubic-bezier(.55, 0, .55, .2);
.md-fab-toolbar-background {
background-color: $primary-default !important;
@@ -50,7 +50,7 @@ tb-dashboard-toolbar {
line-height: 36px;
opacity: .5;
- @include transition(opacity .3s cubic-bezier(.55,0,.55,.2) .2s);
+ transition: opacity .3s cubic-bezier(.55, 0, .55, .2) .2s;
md-icon {
position: absolute;
ui/src/app/layout/home.scss 2(+1 -1)
diff --git a/ui/src/app/layout/home.scss b/ui/src/app/layout/home.scss
index 48c1553..43b8329 100644
--- a/ui/src/app/layout/home.scss
+++ b/ui/src/app/layout/home.scss
@@ -42,7 +42,7 @@
border: none;
opacity: .75;
- @include transition(opacity .35s);
+ transition: opacity .35s;
}
a:hover,
ui/src/app/rulechain/rulechain.scss 16(+1 -15)
diff --git a/ui/src/app/rulechain/rulechain.scss b/ui/src/app/rulechain/rulechain.scss
index 8f77300..d6d264d 100644
--- a/ui/src/app/rulechain/rulechain.scss
+++ b/ui/src/app/rulechain/rulechain.scss
@@ -84,9 +84,6 @@
.tb-panel-title {
min-width: 150px;
- -webkit-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
user-select: none;
}
@@ -163,10 +160,6 @@
.fc-canvas {
min-width: 100%;
min-height: 100%;
- -webkit-user-select: none;
- -khtml-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
user-select: none;
outline: none;
-webkit-touch-callout: none;
@@ -441,13 +434,7 @@
}
.fc-noselect {
- -webkit-touch-callout: none; /* iOS Safari */
- -webkit-user-select: none; /* Safari */
- -khtml-user-select: none; /* Konqueror HTML */
- -moz-user-select: none; /* Firefox */
- -ms-user-select: none; /* Internet Explorer/Edge */
- user-select: none; /* Non-prefixed version, currently
- supported by Chrome and Opera */
+ user-select: none;
}
.fc-edge-label {
@@ -495,7 +482,6 @@
font-weight: 600;
text-align: center;
white-space: nowrap;
- -webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
span {
diff --git a/ui/src/app/rulechain/script/node-script-test.scss b/ui/src/app/rulechain/script/node-script-test.scss
index 1dd174d..e68691d 100644
--- a/ui/src/app/rulechain/script/node-script-test.scss
+++ b/ui/src/app/rulechain/script/node-script-test.scss
@@ -29,7 +29,7 @@ md-dialog.tb-node-script-test-dialog {
}
.tb-split {
- @include box-sizing(border-box);
+ box-sizing: border-box;
overflow-x: hidden;
overflow-y: auto;
}
diff --git a/ui/src/app/widget/lib/entities-table-widget.js b/ui/src/app/widget/lib/entities-table-widget.js
index 620f313..6cfe7a0 100644
--- a/ui/src/app/widget/lib/entities-table-widget.js
+++ b/ui/src/app/widget/lib/entities-table-widget.js
@@ -371,7 +371,9 @@ function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $mdP
content = strContent;
}
} else {
- content = defaultContent(key, value);
+ var decimals = (contentInfo.decimals || contentInfo.decimals === 0) ? contentInfo.decimals : vm.widgetConfig.decimals;
+ var units = contentInfo.units || vm.widgetConfig.units;
+ content = vm.ctx.utils.formatValue(value, decimals, units, true);
}
return content;
} else {
@@ -379,14 +381,6 @@ function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $mdP
}
}
- function defaultContent(key, value) {
- if (angular.isDefined(value)) {
- return value;
- } else {
- return '';
- }
- }
-
function defaultStyle(/*key, value*/) {
return {};
}
@@ -542,7 +536,9 @@ function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $mdP
vm.contentsInfo[dataKey.label] = {
useCellContentFunction: useCellContentFunction,
- cellContentFunction: cellContentFunction
+ cellContentFunction: cellContentFunction,
+ units: dataKey.units,
+ decimals: dataKey.decimals
};
var columnWidth = angular.isDefined(keySettings.columnWidth) ? keySettings.columnWidth : '0px';
ui/src/app/widget/lib/flot-widget.js 458(+245 -213)
diff --git a/ui/src/app/widget/lib/flot-widget.js b/ui/src/app/widget/lib/flot-widget.js
index 1f363d6..acaaf10 100644
--- a/ui/src/app/widget/lib/flot-widget.js
+++ b/ui/src/app/widget/lib/flot-widget.js
@@ -333,6 +333,7 @@ export default class TbFlot {
lineWidth: 0,
fill: 0.9
}
+ ctx.defaultBarWidth = settings.defaultBarWidth || 600;
}
if (this.chartType === 'state') {
@@ -476,7 +477,11 @@ export default class TbFlot {
this.options.yaxes = angular.copy(this.yaxes);
if (this.chartType === 'line' || this.chartType === 'bar' || this.chartType === 'state') {
if (this.chartType === 'bar') {
- this.options.series.bars.barWidth = this.subscription.timeWindow.interval * 0.6;
+ if (this.subscription.timeWindowConfig.aggregation && this.subscription.timeWindowConfig.aggregation.type === 'NONE') {
+ this.options.series.bars.barWidth = this.ctx.defaultBarWidth;
+ } else {
+ this.options.series.bars.barWidth = this.subscription.timeWindow.interval * 0.6;
+ }
}
this.options.xaxis.min = this.subscription.timeWindow.minTime;
this.options.xaxis.max = this.subscription.timeWindow.maxTime;
@@ -594,7 +599,11 @@ export default class TbFlot {
this.options.xaxis.min = this.subscription.timeWindow.minTime;
this.options.xaxis.max = this.subscription.timeWindow.maxTime;
if (this.chartType === 'bar') {
- this.options.series.bars.barWidth = this.subscription.timeWindow.interval * 0.6;
+ if (this.subscription.timeWindowConfig.aggregation && this.subscription.timeWindowConfig.aggregation.type === 'NONE') {
+ this.options.series.bars.barWidth = this.ctx.defaultBarWidth;
+ } else {
+ this.options.series.bars.barWidth = this.subscription.timeWindow.interval * 0.6;
+ }
}
if (axisVisibilityChanged) {
@@ -603,7 +612,11 @@ export default class TbFlot {
this.ctx.plot.getOptions().xaxes[0].min = this.subscription.timeWindow.minTime;
this.ctx.plot.getOptions().xaxes[0].max = this.subscription.timeWindow.maxTime;
if (this.chartType === 'bar') {
- this.ctx.plot.getOptions().series.bars.barWidth = this.subscription.timeWindow.interval * 0.6;
+ if (this.subscription.timeWindowConfig.aggregation && this.subscription.timeWindowConfig.aggregation.type === 'NONE') {
+ this.ctx.plot.getOptions().series.bars.barWidth = this.ctx.defaultBarWidth;
+ } else {
+ this.ctx.plot.getOptions().series.bars.barWidth = this.subscription.timeWindow.interval * 0.6;
+ }
}
this.updateData();
}
@@ -810,238 +823,257 @@ export default class TbFlot {
}
}
- static get settingsSchema() {
- return {
+ static settingsSchema(chartType) {
+
+ var schema = {
"schema": {
"type": "object",
"title": "Settings",
"properties": {
- "stack": {
- "title": "Stacking",
- "type": "boolean",
- "default": false
- },
- "smoothLines": {
- "title": "Display smooth (curved) lines",
- "type": "boolean",
- "default": false
- },
- "shadowSize": {
- "title": "Shadow size",
- "type": "number",
- "default": 4
- },
- "fontColor": {
- "title": "Font color",
+ }
+ }
+ };
+
+ var properties = schema["schema"]["properties"];
+ properties["stack"] = {
+ "title": "Stacking",
+ "type": "boolean",
+ "default": false
+ };
+ if (chartType === 'graph') {
+ properties["smoothLines"] = {
+ "title": "Display smooth (curved) lines",
+ "type": "boolean",
+ "default": false
+ };
+ }
+ if (chartType === 'bar') {
+ properties["defaultBarWidth"] = {
+ "title": "Default bar width for non-aggregated data (milliseconds)",
+ "type": "number",
+ "default": 600
+ };
+ }
+ properties["shadowSize"] = {
+ "title": "Shadow size",
+ "type": "number",
+ "default": 4
+ };
+ properties["fontColor"] = {
+ "title": "Font color",
+ "type": "string",
+ "default": "#545454"
+ };
+ properties["fontSize"] = {
+ "title": "Font size",
+ "type": "number",
+ "default": 10
+ };
+ properties["tooltipIndividual"] = {
+ "title": "Hover individual points",
+ "type": "boolean",
+ "default": false
+ };
+ properties["tooltipCumulative"] = {
+ "title": "Show cumulative values in stacking mode",
+ "type": "boolean",
+ "default": false
+ };
+ properties["tooltipValueFormatter"] = {
+ "title": "Tooltip value format function, f(value)",
+ "type": "string",
+ "default": ""
+ };
+
+ properties["grid"] = {
+ "title": "Grid settings",
+ "type": "object",
+ "properties": {
+ "color": {
+ "title": "Primary color",
"type": "string",
"default": "#545454"
- },
- "fontSize": {
- "title": "Font size",
+ },
+ "backgroundColor": {
+ "title": "Background color",
+ "type": "string",
+ "default": null
+ },
+ "tickColor": {
+ "title": "Ticks color",
+ "type": "string",
+ "default": "#DDDDDD"
+ },
+ "outlineWidth": {
+ "title": "Grid outline/border width (px)",
"type": "number",
- "default": 10
- },
- "tooltipIndividual": {
- "title": "Hover individual points",
+ "default": 1
+ },
+ "verticalLines": {
+ "title": "Show vertical lines",
"type": "boolean",
- "default": false
- },
- "tooltipCumulative": {
- "title": "Show cumulative values in stacking mode",
+ "default": true
+ },
+ "horizontalLines": {
+ "title": "Show horizontal lines",
"type": "boolean",
- "default": false
- },
- "tooltipValueFormatter": {
- "title": "Tooltip value format function, f(value)",
- "type": "string",
- "default": ""
- },
- "grid": {
- "title": "Grid settings",
- "type": "object",
- "properties": {
- "color": {
- "title": "Primary color",
- "type": "string",
- "default": "#545454"
- },
- "backgroundColor": {
- "title": "Background color",
- "type": "string",
- "default": null
- },
- "tickColor": {
- "title": "Ticks color",
- "type": "string",
- "default": "#DDDDDD"
- },
- "outlineWidth": {
- "title": "Grid outline/border width (px)",
- "type": "number",
- "default": 1
- },
- "verticalLines": {
- "title": "Show vertical lines",
- "type": "boolean",
- "default": true
- },
- "horizontalLines": {
- "title": "Show horizontal lines",
- "type": "boolean",
- "default": true
- }
- }
- },
- "xaxis": {
- "title": "X axis settings",
- "type": "object",
- "properties": {
- "showLabels": {
- "title": "Show labels",
- "type": "boolean",
- "default": true
- },
- "title": {
- "title": "Axis title",
- "type": "string",
- "default": null
- },
- "titleAngle": {
- "title": "Axis title's angle in degrees",
- "type": "number",
- "default": 0
- },
- "color": {
- "title": "Ticks color",
- "type": "string",
- "default": null
- }
- }
- },
- "yaxis": {
- "title": "Y axis settings",
- "type": "object",
- "properties": {
- "min": {
- "title": "Minimum value on the scale",
- "type": "number",
- "default": null
- },
- "max": {
- "title": "Maximum value on the scale",
- "type": "number",
- "default": null
- },
- "showLabels": {
- "title": "Show labels",
- "type": "boolean",
- "default": true
- },
- "title": {
- "title": "Axis title",
- "type": "string",
- "default": null
- },
- "titleAngle": {
- "title": "Axis title's angle in degrees",
- "type": "number",
- "default": 0
- },
- "color": {
- "title": "Ticks color",
- "type": "string",
- "default": null
- },
- "ticksFormatter": {
- "title": "Ticks formatter function, f(value)",
- "type": "string",
- "default": ""
- },
- "tickDecimals": {
- "title": "The number of decimals to display",
- "type": "number",
- "default": 0
- },
- "tickSize": {
- "title": "Step size between ticks",
- "type": "number",
- "default": null
- }
- }
- }
+ "default": true
+ }
+ }
+ };
+
+ properties["xaxis"] = {
+ "title": "X axis settings",
+ "type": "object",
+ "properties": {
+ "showLabels": {
+ "title": "Show labels",
+ "type": "boolean",
+ "default": true
},
- "required": []
- },
- "form": [
- "stack",
- "smoothLines",
- "shadowSize",
+ "title": {
+ "title": "Axis title",
+ "type": "string",
+ "default": null
+ },
+ "titleAngle": {
+ "title": "Axis title's angle in degrees",
+ "type": "number",
+ "default": 0
+ },
+ "color": {
+ "title": "Ticks color",
+ "type": "string",
+ "default": null
+ }
+ }
+ };
+
+ properties["yaxis"] = {
+ "title": "Y axis settings",
+ "type": "object",
+ "properties": {
+ "min": {
+ "title": "Minimum value on the scale",
+ "type": "number",
+ "default": null
+ },
+ "max": {
+ "title": "Maximum value on the scale",
+ "type": "number",
+ "default": null
+ },
+ "showLabels": {
+ "title": "Show labels",
+ "type": "boolean",
+ "default": true
+ },
+ "title": {
+ "title": "Axis title",
+ "type": "string",
+ "default": null
+ },
+ "titleAngle": {
+ "title": "Axis title's angle in degrees",
+ "type": "number",
+ "default": 0
+ },
+ "color": {
+ "title": "Ticks color",
+ "type": "string",
+ "default": null
+ },
+ "ticksFormatter": {
+ "title": "Ticks formatter function, f(value)",
+ "type": "string",
+ "default": ""
+ },
+ "tickDecimals": {
+ "title": "The number of decimals to display",
+ "type": "number",
+ "default": 0
+ },
+ "tickSize": {
+ "title": "Step size between ticks",
+ "type": "number",
+ "default": null
+ }
+ }
+ };
+
+ schema["schema"]["required"] = [];
+ schema["form"] = ["stack"];
+ if (chartType === 'graph') {
+ schema["form"].push("smoothLines");
+ }
+ if (chartType === 'bar') {
+ schema["form"].push("defaultBarWidth");
+ }
+ schema["form"].push("shadowSize");
+ schema["form"].push({
+ "key": "fontColor",
+ "type": "color"
+ });
+ schema["form"].push("fontSize");
+ schema["form"].push("tooltipIndividual");
+ schema["form"].push("tooltipCumulative");
+ schema["form"].push({
+ "key": "tooltipValueFormatter",
+ "type": "javascript"
+ });
+ schema["form"].push({
+ "key": "grid",
+ "items": [
{
- "key": "fontColor",
+ "key": "grid.color",
"type": "color"
},
- "fontSize",
- "tooltipIndividual",
- "tooltipCumulative",
{
- "key": "tooltipValueFormatter",
- "type": "javascript"
+ "key": "grid.backgroundColor",
+ "type": "color"
},
{
- "key": "grid",
- "items": [
- {
- "key": "grid.color",
- "type": "color"
- },
- {
- "key": "grid.backgroundColor",
- "type": "color"
- },
- {
- "key": "grid.tickColor",
- "type": "color"
- },
- "grid.outlineWidth",
- "grid.verticalLines",
- "grid.horizontalLines"
- ]
+ "key": "grid.tickColor",
+ "type": "color"
},
+ "grid.outlineWidth",
+ "grid.verticalLines",
+ "grid.horizontalLines"
+ ]
+ });
+ schema["form"].push({
+ "key": "xaxis",
+ "items": [
+ "xaxis.showLabels",
+ "xaxis.title",
+ "xaxis.titleAngle",
{
- "key": "xaxis",
- "items": [
- "xaxis.showLabels",
- "xaxis.title",
- "xaxis.titleAngle",
- {
- "key": "xaxis.color",
- "type": "color"
- }
- ]
+ "key": "xaxis.color",
+ "type": "color"
+ }
+ ]
+ });
+ schema["form"].push({
+ "key": "yaxis",
+ "items": [
+ "yaxis.min",
+ "yaxis.max",
+ "yaxis.tickDecimals",
+ "yaxis.tickSize",
+ "yaxis.showLabels",
+ "yaxis.title",
+ "yaxis.titleAngle",
+ {
+ "key": "yaxis.color",
+ "type": "color"
},
{
- "key": "yaxis",
- "items": [
- "yaxis.min",
- "yaxis.max",
- "yaxis.tickDecimals",
- "yaxis.tickSize",
- "yaxis.showLabels",
- "yaxis.title",
- "yaxis.titleAngle",
- {
- "key": "yaxis.color",
- "type": "color"
- },
- {
- "key": "yaxis.ticksFormatter",
- "type": "javascript"
- }
- ]
+ "key": "yaxis.ticksFormatter",
+ "type": "javascript"
}
-
]
- }
+ });
+ return schema;
}
static get pieDatakeySettingsSchema() {
ui/src/app/widget/lib/rpc/knob.scss 2(+0 -2)
diff --git a/ui/src/app/widget/lib/rpc/knob.scss b/ui/src/app/widget/lib/rpc/knob.scss
index bf2d3e0..722275f 100644
--- a/ui/src/app/widget/lib/rpc/knob.scss
+++ b/ui/src/app/widget/lib/rpc/knob.scss
@@ -37,8 +37,6 @@ $background-color: #e6e7e8 !default;
position: relative;
&[draggable] {
- -moz-user-select: none;
- -webkit-user-select: none;
user-select: none;
}
diff --git a/ui/src/app/widget/lib/rpc/led-indicator.scss b/ui/src/app/widget/lib/rpc/led-indicator.scss
index b1da5c6..3083e72 100644
--- a/ui/src/app/widget/lib/rpc/led-indicator.scss
+++ b/ui/src/app/widget/lib/rpc/led-indicator.scss
@@ -60,19 +60,11 @@ $background-color: #e6e7e8 !default;
.led {
position: relative;
cursor: pointer;
- background-image: -owg-radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, .25));
- background-image: -webkit-radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, .25));
- background-image: -moz-radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, .25));
- background-image: -o-radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, .25));
background-image: radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, .25));
border-radius: 50%;
transition: background-color .5s, box-shadow .5s;
&.disabled {
- background-image: -owg-radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, .5), rgba(0, 0, 0, .1));
- background-image: -webkit-radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, .5), rgba(0, 0, 0, .1));
- background-image: -moz-radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, .5), rgba(0, 0, 0, .1));
- background-image: -o-radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, .5), rgba(0, 0, 0, .1));
background-image: radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, .5), rgba(0, 0, 0, .1));
}
}
ui/src/app/widget/lib/rpc/round-switch.scss 95(+41 -54)
diff --git a/ui/src/app/widget/lib/rpc/round-switch.scss b/ui/src/app/widget/lib/rpc/round-switch.scss
index 2e37f63..c89b26b 100644
--- a/ui/src/app/widget/lib/rpc/round-switch.scss
+++ b/ui/src/app/widget/lib/rpc/round-switch.scss
@@ -59,6 +59,8 @@ $background-color: #e6e7e8 !default;
.switch {
position: relative;
+
+ box-sizing: border-box;
width: 260px;
min-width: 260px;
height: 260px;
@@ -69,21 +71,14 @@ $background-color: #e6e7e8 !default;
color: #424242;
cursor: pointer;
- -pie-background: -pie-linear-gradient(270deg, #bbb, #ddd);
background: #ddd;
- background: -owg-linear-gradient(270deg, #bbb, #ddd);
- background: -webkit-linear-gradient(270deg, #bbb, #ddd);
- background: -moz-linear-gradient(270deg, #bbb, #ddd);
- background: -o-linear-gradient(270deg, #bbb, #ddd);
background: linear-gradient(180deg, #bbb, #ddd);
border-radius: 130px;
- @include box-sizing(border-box);
-
- @include box-shadow(
- 0 0 0 8px rgba(0,0,0,.1)
- ,0 0 3px 1px rgba(0,0,0,.1)
- ,inset 0 8px 3px -8px rgba(255,255,255,.4));
+ box-shadow:
+ 0 0 0 8px rgba(0, 0, 0, .1),
+ 0 0 3px 1px rgba(0, 0, 0, .1),
+ inset 0 8px 3px -8px rgba(255, 255, 255, .4);
input {
display: none;
@@ -95,7 +90,7 @@ $background-color: #e6e7e8 !default;
width: 100%;
text-align: center;
- @include text-shadow(1px 1px 4px #4a4a4a);
+ text-shadow: 1px 1px 4px #4a4a4a;
}
.on {
@@ -103,15 +98,15 @@ $background-color: #e6e7e8 !default;
font-family: sans-serif;
color: #444;
- @include transition(all .1s);
+ transition: all .1s;
}
.off {
bottom: 5px;
- @include transition(all .1s);
+ transition: all .1s;
- @include transform(scaleY(.85));
+ transform: scaleY(.85);
}
.but {
@@ -125,90 +120,82 @@ $background-color: #e6e7e8 !default;
border-bottom-width: 0;
border-radius: 400px 400px 400px 400px / 400px 400px 300px 300px;
- @include box-shadow(inset 8px 6px 5px -7px #a2a2a2,
- inset -8px 6px 5px -7px #a2a2a2,
- inset 0 -3px 2px -2px rgba(200, 200, 200, .5),
- 0 3px 3px -2px #fff,
- inset 0 -230px 60px -200px rgba(255, 255, 255, .2),
- inset 0 220px 40px -200px rgba(0, 0, 0, .3));
+ box-shadow:
+ inset 8px 6px 5px -7px #a2a2a2,
+ inset -8px 6px 5px -7px #a2a2a2,
+ inset 0 -3px 2px -2px rgba(200, 200, 200, .5),
+ 0 3px 3px -2px #fff,
+ inset 0 -230px 60px -200px rgba(255, 255, 255, .2),
+ inset 0 220px 40px -200px rgba(0, 0, 0, .3);
- @include transition(all .2s);
+ transition: all .2s;
}
.back {
+
+ box-sizing: border-box;
width: 210px;
height: 210px;
padding: 4px 4px;
cursor: pointer;
background-color: #888787;
- background-image: -owg-linear-gradient(0deg, transparent 30%, transparent 70%), -owg-linear-gradient(90deg, rgba(150, 150, 150, 0) 30%, rgba(150, 150, 150, .2) 50%, rgba(150, 150, 150, 0) 70%);
- background-image: -webkit-linear-gradient(0deg, transparent 30%, transparent 70%), -webkit-linear-gradient(90deg, rgba(150, 150, 150, 0) 30%, rgba(150, 150, 150, .2) 50%, rgba(150, 150, 150, 0) 70%);
- background-image: -moz-linear-gradient(0deg, transparent 30%, transparent 70%), -moz-linear-gradient(90deg, rgba(150, 150, 150, 0) 30%, rgba(150, 150, 150, .2) 50%, rgba(150, 150, 150, 0) 70%);
- background-image: -o-linear-gradient(0deg, transparent 30%, transparent 70%), -o-linear-gradient(90deg, rgba(150, 150, 150, 0) 30%, rgba(150, 150, 150, .2) 50%, rgba(150, 150, 150, 0) 70%);
background-image: linear-gradient(-90deg, transparent 30%, transparent 70%), linear-gradient(0deg, rgba(150, 150, 150, 0) 30%, rgba(150, 150, 150, .2) 50%, rgba(150, 150, 150, 0) 70%);
border-radius: 105px;
- @include box-shadow(30px 30px 30px -20px rgba(58, 58, 58, .3),
- -30px 30px 30px -20px rgba(58, 58, 58, .3),
- 0 30px 30px 0 rgba(16, 16, 16, .3),
- inset 0 -1px 0 0 #484848);
-
- @include box-sizing(border-box);
+ box-shadow:
+ 30px 30px 30px -20px rgba(58, 58, 58, .3),
+ -30px 30px 30px -20px rgba(58, 58, 58, .3),
+ 0 30px 30px 0 rgba(16, 16, 16, .3),
+ inset 0 -1px 0 0 #484848;
- @include transition(all .2s);
+ transition: all .2s;
}
input:checked + .back .on,
input:checked + .back .off{
- @include text-shadow(1px 1px 4px #4a4a4a);
+ text-shadow: 1px 1px 4px #4a4a4a;
}
input:checked + .back .on{
top: 10px;
color: #4c4c4c;
- @include transform(scaleY(.85));
+ transform: scaleY(.85);
}
input:checked + .back .off{
bottom: 5px;
color: #444;
- @include transform(scaleY(1));
+ transform: scaleY(1);
}
input:checked + .back .but{
margin-top: 20px;
background: #dcdcdc;
- background-image: -owg-radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, .3), transparent);
- background-image: -webkit-radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, .3), transparent);
- background-image: -moz-radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, .3), transparent);
- background-image: -o-radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, .3), transparent);
background-image: radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, .3), transparent);
border-radius: 400px 400px 400px 400px / 300px 300px 400px 400px;
- @include box-shadow(inset 8px -4px 5px -7px #a9a9a9,
- inset -8px -4px 5px -7px #808080,
- 0 -3px 8px -4px rgba(50, 50, 50, .4),
- inset 0 3px 4px -2px #9c9c9c,
- inset 0 280px 40px -200px rgba(0, 0, 0, .2),
- inset 0 -200px 40px -200px rgba(180, 180, 180, .2));
+ box-shadow:
+ inset 8px -4px 5px -7px #a9a9a9,
+ inset -8px -4px 5px -7px #808080,
+ 0 -3px 8px -4px rgba(50, 50, 50, .4),
+ inset 0 3px 4px -2px #9c9c9c,
+ inset 0 280px 40px -200px rgba(0, 0, 0, .2),
+ inset 0 -200px 40px -200px rgba(180, 180, 180, .2);
}
input:checked + .back{
padding: 2px 4px;
- background-image: -owg-linear-gradient(90deg, #868686 30%, transparent 70%), -owg-linear-gradient(180deg, rgba(115, 115, 115, 0) 0%, rgba(255, 255, 255, .74) 50%, rgba(105, 105, 105, 0) 100%);
- background-image: -webkit-linear-gradient(90deg, #868686 30%, transparent 70%), -webkit-linear-gradient(180deg, rgba(115, 115, 115, 0) 0%, rgba(255, 255, 255, .74) 50%, rgba(105, 105, 105, 0) 100%);
- background-image: -moz-linear-gradient(90deg, #868686 30%, transparent 70%), -moz-linear-gradient(180deg, rgba(115, 115, 115, 0) 0%, rgba(255, 255, 255, .74) 50%, rgba(105, 105, 105, 0) 100%);
- background-image: -o-linear-gradient(90deg, #868686 30%, transparent 70%), -o-linear-gradient(180deg, rgba(115, 115, 115, 0) 0%, rgba(255, 255, 255, .74) 50%, rgba(105, 105, 105, 0) 100%);
background-image: linear-gradient(0deg, #868686 30%, transparent 70%), linear-gradient(90deg, rgba(115, 115, 115, 0) 0%, rgba(255, 255, 255, .74) 50%, rgba(105, 105, 105, 0) 100%);
- @include box-shadow(30px 30px 30px -20px rgba(49, 49, 49, .1),
- -30px 30px 30px -20px rgba(111, 111, 111, .1),
- 0 30px 30px 0 rgba(0, 0, 0, .2),
- inset 0 1px 2px 0 rgba(167, 167, 167, .6));
+ box-shadow:
+ 30px 30px 30px -20px rgba(49, 49, 49, .1),
+ -30px 30px 30px -20px rgba(111, 111, 111, .1),
+ 0 30px 30px 0 rgba(0, 0, 0, .2),
+ inset 0 1px 2px 0 rgba(167, 167, 167, .6);
}
}
}
diff --git a/ui/src/app/widget/lib/timeseries-table-widget.js b/ui/src/app/widget/lib/timeseries-table-widget.js
index bca7956..d025c1e 100644
--- a/ui/src/app/widget/lib/timeseries-table-widget.js
+++ b/ui/src/app/widget/lib/timeseries-table-widget.js
@@ -217,7 +217,9 @@ function TimeseriesTableWidgetController($element, $scope, $filter, $timeout) {
content = strContent;
}
} else {
- content = vm.ctx.utils.formatValue(value, contentInfo.decimals, contentInfo.units);
+ var decimals = (contentInfo.decimals || contentInfo.decimals === 0) ? contentInfo.decimals : vm.widgetConfig.decimals;
+ var units = contentInfo.units || vm.widgetConfig.units;
+ content = vm.ctx.utils.formatValue(value, decimals, units, true);
}
return content;
}
ui/src/app/widget/widget-editor.scss 2(+1 -1)
diff --git a/ui/src/app/widget/widget-editor.scss b/ui/src/app/widget/widget-editor.scss
index cd374e3..0eb37af 100644
--- a/ui/src/app/widget/widget-editor.scss
+++ b/ui/src/app/widget/widget-editor.scss
@@ -19,7 +19,7 @@ $edit-toolbar-height: 40px !default;
.tb-editor {
.tb-split {
- @include box-sizing(border-box);
+ box-sizing: border-box;
overflow-x: hidden;
overflow-y: auto;
}
ui/src/scss/animations.scss 16(+8 -8)
diff --git a/ui/src/scss/animations.scss b/ui/src/scss/animations.scss
index 4ecfc34..61b9bac 100644
--- a/ui/src/scss/animations.scss
+++ b/ui/src/scss/animations.scss
@@ -15,34 +15,34 @@
*/
@import "~compass-sass-mixins/lib/animate";
-@include keyframes(tbMoveFromTopFade) {
+@keyframes tbMoveFromTopFade {
from {
opacity: 0;
- @include transform(translate(0, -100%));
+ transform: translate(0, -100%);
}
}
-@include keyframes(tbMoveToTopFade) {
+@keyframes tbMoveToTopFade {
to {
opacity: 0;
- @include transform(translate(0, -100%));
+ transform: translate(0, -100%);
}
}
-@include keyframes(tbMoveFromBottomFade) {
+@keyframes tbMoveFromBottomFade {
from {
opacity: 0;
- @include transform(translate(0, 100%));
+ transform: translate(0, 100%);
}
}
-@include keyframes(tbMoveToBottomFade) {
+@keyframes tbMoveToBottomFade {
to {
opacity: 0;
- @include transform(translate(0, 150%));
+ transform: translate(0, 150%);
}
}
ui/src/scss/main.scss 28(+11 -17)
diff --git a/ui/src/scss/main.scss b/ui/src/scss/main.scss
index 27c2d3b..c1a437a 100644
--- a/ui/src/scss/main.scss
+++ b/ui/src/scss/main.scss
@@ -42,7 +42,7 @@ textarea {
word-wrap: normal;
white-space: nowrap;
direction: ltr;
- -webkit-font-feature-settings: "liga";
+ -webkit-font-feature-settings: "liga"; /* stylelint-disable-line property-no-vendor-prefix */
}
a {
@@ -51,7 +51,7 @@ a {
text-decoration: none;
border-bottom: 1px solid rgba(64, 84, 178, .25);
- @include transition(border-bottom .35s);
+ transition: border-bottom .35s;
}
a:hover,
@@ -258,13 +258,7 @@ label {
}
.tb-noselect {
- -webkit-touch-callout: none; /* iOS Safari */
- -webkit-user-select: none; /* Safari */
- -khtml-user-select: none; /* Konqueror HTML */
- -moz-user-select: none; /* Firefox */
- -ms-user-select: none; /* Internet Explorer/Edge */
- user-select: none; /* Non-prefixed version, currently
- supported by Chrome and Opera */
+ user-select: none;
}
.tb-readonly-label {
@@ -556,7 +550,7 @@ $previewSize: 100px !default;
}
.tb-error-message.ng-animate {
- @include transition(all .3s cubic-bezier(.55, 0, .55, .2));
+ transition: all .3s cubic-bezier(.55, 0, .55, .2);
}
.tb-error-message.ng-enter-prepare,
@@ -652,13 +646,13 @@ section.tb-top-header-buttons {
}
.tb-header-buttons .tb-btn-header {
- @include animation(tbMoveFromTopFade .3s ease both);
position: relative !important;
display: inline-block !important;
+ animation: tbMoveFromTopFade .3s ease both;
}
.tb-header-buttons .tb-btn-header.ng-hide {
- @include animation(tbMoveToTopFade .3s ease both);
+ animation: tbMoveToTopFade .3s ease both;
}
/***********************
@@ -669,24 +663,24 @@ section.tb-footer-buttons {
position: fixed;
right: 20px;
bottom: 20px;
- z-index: 13;
+ z-index: 30;
pointer-events: none;
}
.tb-footer-buttons .tb-btn-footer {
- @include animation(tbMoveFromBottomFade .3s ease both);
position: relative !important;
display: inline-block !important;
+ animation: tbMoveFromBottomFade .3s ease both;
}
.tb-footer-buttons .tb-btn-footer.ng-hide {
- @include animation(tbMoveToBottomFade .3s ease both);
+ animation: tbMoveToBottomFade .3s ease both;
}
._md-toast-open-bottom .tb-footer-buttons {
- @include transition(all .4s cubic-bezier(.25, .8, .25, 1));
+ transition: all .4s cubic-bezier(.25, .8, .25, 1);
- @include transform(translate3d(0, -42px, 0));
+ transform: translate3d(0, -42px, 0);
}
/***********************
ui/src/scss/mixins.scss 2(+2 -0)
diff --git a/ui/src/scss/mixins.scss b/ui/src/scss/mixins.scss
index a754538..4d2c604 100644
--- a/ui/src/scss/mixins.scss
+++ b/ui/src/scss/mixins.scss
@@ -15,6 +15,7 @@
*/
@import "~compass-sass-mixins/lib/compass";
+/* stylelint-disable selector-no-vendor-prefix */
@mixin input-placeholder {
// replaces compass/css/user-interface/input-placeholder()
@@ -36,6 +37,7 @@
@content;
}
}
+/* stylelint-enable selector-no-vendor-prefix */
@mixin line-clamp($numLines: 1, $lineHeight: 1.412) {
position: relative;