Merge branch 'master' into develop/2.2

This commit is contained in:
Igor Kulikov 2018-10-04 17:20:33 +03:00
commit d92b06584a
38 changed files with 540 additions and 432 deletions

File diff suppressed because one or more lines are too long

View File

@ -35,7 +35,7 @@
"resources": [], "resources": [],
"templateHtml": "", "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", "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": "{}", "settingsSchema": "{}",
"dataKeySettingsSchema": "{}", "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}" "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": [], "resources": [],
"templateHtml": "", "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", "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": "{}", "settingsSchema": "{}",
"dataKeySettingsSchema": "{}", "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": [], "resources": [],
"templateHtml": "", "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", "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": "{}", "settingsSchema": "{}",
"dataKeySettingsSchema": "{}", "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}}" "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}}"

View File

@ -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 MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_entity_id;
DROP TABLE IF EXISTS thingsboard.entity_views; DROP TABLE IF EXISTS thingsboard.entity_views;
ControllerSqlTestSuite
CREATE TABLE IF NOT EXISTS thingsboard.entity_views ( CREATE TABLE IF NOT EXISTS thingsboard.entity_views (
id timeuuid, id timeuuid,
entity_id timeuuid, entity_id timeuuid,

View File

@ -391,6 +391,7 @@ audit_log:
"user": "${AUDIT_LOG_MASK_USER:W}" "user": "${AUDIT_LOG_MASK_USER:W}"
"rule_chain": "${AUDIT_LOG_MASK_RULE_CHAIN:W}" "rule_chain": "${AUDIT_LOG_MASK_RULE_CHAIN:W}"
"alarm": "${AUDIT_LOG_MASK_ALARM:W}" "alarm": "${AUDIT_LOG_MASK_ALARM:W}"
"entity_view": "${AUDIT_LOG_MASK_RULE_CHAIN:W}"
sink: sink:
# Type of external sink. possible options: none, elasticsearch # Type of external sink. possible options: none, elasticsearch
type: "${AUDIT_LOG_SINK_TYPE:none}" type: "${AUDIT_LOG_SINK_TYPE:none}"

View File

@ -73,4 +73,6 @@ public abstract class DeviceAwareSessionContext implements SessionContext {
public Device getDevice() { public Device getDevice() {
return device; return device;
} }
} }

View File

@ -54,6 +54,7 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.thingsboard.server.common.data.CacheConstants.ASSET_CACHE; 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()) { if (entityViews != null && !entityViews.isEmpty()) {
throw new DataValidationException("Can't delete asset that is assigned to entity views!"); 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); log.error("Exception while finding entity views for assetId [{}]", assetId, e);
throw new RuntimeException("Exception while finding entity views for assetId [" + assetId + "]", e); throw new RuntimeException("Exception while finding entity views for assetId [" + assetId + "]", e);
} }

View File

@ -58,6 +58,7 @@ import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.thingsboard.server.common.data.CacheConstants.DEVICE_CACHE; 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()) { if (entityViews != null && !entityViews.isEmpty()) {
throw new DataValidationException("Can't delete device that is assigned to entity views!"); 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); log.error("Exception while finding entity views for deviceId [{}]", deviceId, e);
throw new RuntimeException("Exception while finding entity views for deviceId [" + deviceId + "]", e); throw new RuntimeException("Exception while finding entity views for deviceId [" + deviceId + "]", e);
} }

View File

@ -51,6 +51,9 @@ import javax.security.cert.X509Certificate;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; 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.MqttConnectReturnCode.*;
import static io.netty.handler.codec.mqtt.MqttMessageType.*; import static io.netty.handler.codec.mqtt.MqttMessageType.*;
@ -75,6 +78,8 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
private final RelationService relationService; private final RelationService relationService;
private final QuotaService quotaService; private final QuotaService quotaService;
private final SslHandler sslHandler; private final SslHandler sslHandler;
private final ConcurrentMap<String, Integer> mqttQoSMap;
private volatile boolean connected; private volatile boolean connected;
private volatile InetSocketAddress address; private volatile InetSocketAddress address;
private volatile GatewaySessionCtx gatewaySessionCtx; private volatile GatewaySessionCtx gatewaySessionCtx;
@ -86,7 +91,8 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
this.relationService = relationService; this.relationService = relationService;
this.authService = authService; this.authService = authService;
this.adaptor = adaptor; 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.sessionId = deviceSessionCtx.getSessionId().toUidStr();
this.sslHandler = sslHandler; this.sslHandler = sslHandler;
this.quotaService = quotaService; this.quotaService = quotaService;
@ -166,18 +172,25 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
private void handleMqttPublishMsg(String topicName, int msgId, MqttPublishMessage mqttMsg) { private void handleMqttPublishMsg(String topicName, int msgId, MqttPublishMessage mqttMsg) {
try { try {
if (topicName.equals(GATEWAY_TELEMETRY_TOPIC)) { switch (topicName) {
case GATEWAY_TELEMETRY_TOPIC:
gatewaySessionCtx.onDeviceTelemetry(mqttMsg); gatewaySessionCtx.onDeviceTelemetry(mqttMsg);
} else if (topicName.equals(GATEWAY_ATTRIBUTES_TOPIC)) { break;
case GATEWAY_ATTRIBUTES_TOPIC:
gatewaySessionCtx.onDeviceAttributes(mqttMsg); gatewaySessionCtx.onDeviceAttributes(mqttMsg);
} else if (topicName.equals(GATEWAY_ATTRIBUTES_REQUEST_TOPIC)) { break;
case GATEWAY_ATTRIBUTES_REQUEST_TOPIC:
gatewaySessionCtx.onDeviceAttributesRequest(mqttMsg); gatewaySessionCtx.onDeviceAttributesRequest(mqttMsg);
} else if (topicName.equals(GATEWAY_RPC_TOPIC)) { break;
case GATEWAY_RPC_TOPIC:
gatewaySessionCtx.onDeviceRpcResponse(mqttMsg); gatewaySessionCtx.onDeviceRpcResponse(mqttMsg);
} else if (topicName.equals(GATEWAY_CONNECT_TOPIC)) { break;
case GATEWAY_CONNECT_TOPIC:
gatewaySessionCtx.onDeviceConnect(mqttMsg); gatewaySessionCtx.onDeviceConnect(mqttMsg);
} else if (topicName.equals(GATEWAY_DISCONNECT_TOPIC)) { break;
case GATEWAY_DISCONNECT_TOPIC:
gatewaySessionCtx.onDeviceDisconnect(mqttMsg); gatewaySessionCtx.onDeviceDisconnect(mqttMsg);
break;
} }
} catch (RuntimeException | AdaptorException e) { } catch (RuntimeException | AdaptorException e) {
log.warn("[{}] Failed to process publish msg [{}][{}]", sessionId, topicName, msgId, 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()); log.trace("[{}] Processing subscription [{}]!", sessionId, mqttMsg.variableHeader().messageId());
List<Integer> grantedQoSList = new ArrayList<>(); List<Integer> grantedQoSList = new ArrayList<>();
for (MqttTopicSubscription subscription : mqttMsg.payload().topicSubscriptions()) { for (MqttTopicSubscription subscription : mqttMsg.payload().topicSubscriptions()) {
String topicName = subscription.topicName(); String topic = subscription.topicName();
//TODO: handle this qos level.
MqttQoS reqQoS = subscription.qualityOfService(); MqttQoS reqQoS = subscription.qualityOfService();
try { try {
if (topicName.equals(DEVICE_ATTRIBUTES_TOPIC)) { switch (topic) {
case DEVICE_ATTRIBUTES_TOPIC: {
AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, SUBSCRIBE_ATTRIBUTES_REQUEST, mqttMsg); AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, SUBSCRIBE_ATTRIBUTES_REQUEST, mqttMsg);
processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg)); processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg));
grantedQoSList.add(getMinSupportedQos(reqQoS)); registerSubQoS(topic, grantedQoSList, reqQoS);
} else if (topicName.equals(DEVICE_RPC_REQUESTS_SUB_TOPIC)) { break;
}
case DEVICE_RPC_REQUESTS_SUB_TOPIC: {
AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, SUBSCRIBE_RPC_COMMANDS_REQUEST, mqttMsg); AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, SUBSCRIBE_RPC_COMMANDS_REQUEST, mqttMsg);
processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg)); processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg));
grantedQoSList.add(getMinSupportedQos(reqQoS)); registerSubQoS(topic, grantedQoSList, reqQoS);
} else if (topicName.equals(DEVICE_RPC_RESPONSE_SUB_TOPIC)) { break;
grantedQoSList.add(getMinSupportedQos(reqQoS)); }
} else if (topicName.equals(DEVICE_ATTRIBUTES_RESPONSES_TOPIC)) { 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(); deviceSessionCtx.setAllowAttributeResponses();
grantedQoSList.add(getMinSupportedQos(reqQoS)); registerSubQoS(topic, grantedQoSList, reqQoS);
} else if (topicName.equals(GATEWAY_ATTRIBUTES_TOPIC)) { break;
grantedQoSList.add(getMinSupportedQos(reqQoS)); default:
} else { log.warn("[{}] Failed to subscribe to [{}][{}]", sessionId, topic, reqQoS);
log.warn("[{}] Failed to subscribe to [{}][{}]", sessionId, topicName, reqQoS);
grantedQoSList.add(FAILURE.value()); grantedQoSList.add(FAILURE.value());
break;
} }
} catch (AdaptorException e) { } 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()); grantedQoSList.add(FAILURE.value());
} }
} }
ctx.writeAndFlush(createSubAckMessage(mqttMsg.variableHeader().messageId(), grantedQoSList)); 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) { private void processUnsubscribe(ChannelHandlerContext ctx, MqttUnsubscribeMessage mqttMsg) {
if (!checkConnected(ctx)) { if (!checkConnected(ctx)) {
return; return;
} }
log.trace("[{}] Processing subscription [{}]!", sessionId, mqttMsg.variableHeader().messageId()); log.trace("[{}] Processing subscription [{}]!", sessionId, mqttMsg.variableHeader().messageId());
for (String topicName : mqttMsg.payload().topics()) { for (String topicName : mqttMsg.payload().topics()) {
mqttQoSMap.remove(topicName);
try { try {
if (topicName.equals(DEVICE_ATTRIBUTES_TOPIC)) { switch (topicName) {
case DEVICE_ATTRIBUTES_TOPIC: {
AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, UNSUBSCRIBE_ATTRIBUTES_REQUEST, mqttMsg); AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, UNSUBSCRIBE_ATTRIBUTES_REQUEST, mqttMsg);
processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg)); processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg));
} else if (topicName.equals(DEVICE_RPC_REQUESTS_SUB_TOPIC)) { break;
}
case DEVICE_RPC_REQUESTS_SUB_TOPIC: {
AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, UNSUBSCRIBE_RPC_COMMANDS_REQUEST, mqttMsg); AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, UNSUBSCRIBE_RPC_COMMANDS_REQUEST, mqttMsg);
processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg)); processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg));
} else if (topicName.equals(DEVICE_ATTRIBUTES_RESPONSES_TOPIC)) { break;
}
case DEVICE_ATTRIBUTES_RESPONSES_TOPIC:
deviceSessionCtx.setDisallowAttributeResponses(); deviceSessionCtx.setDisallowAttributeResponses();
break;
} }
} catch (AdaptorException e) { } catch (AdaptorException e) {
log.warn("[{}] Failed to process unsubscription [{}] to [{}]", sessionId, mqttMsg.variableHeader().messageId(), topicName); log.warn("[{}] Failed to process unsubscription [{}] to [{}]", sessionId, mqttMsg.variableHeader().messageId(), topicName);

View File

@ -170,7 +170,7 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor {
private MqttPublishMessage createMqttPublishMsg(DeviceSessionCtx ctx, String topic, JsonElement json) { private MqttPublishMessage createMqttPublishMsg(DeviceSessionCtx ctx, String topic, JsonElement json) {
MqttFixedHeader mqttFixedHeader = 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()); MqttPublishVariableHeader header = new MqttPublishVariableHeader(topic, ctx.nextMsgId());
ByteBuf payload = ALLOCATOR.buffer(); ByteBuf payload = ALLOCATOR.buffer();
payload.writeBytes(GSON.toJson(json).getBytes(UTF8)); payload.writeBytes(GSON.toJson(json).getBytes(UTF8));

View File

@ -30,13 +30,15 @@ import org.thingsboard.server.common.transport.auth.DeviceAuthService;
import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext; import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext;
import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor; import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor;
import java.util.Map;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
/** /**
* @author Andrew Shvayka * @author Andrew Shvayka
*/ */
@Slf4j @Slf4j
public class DeviceSessionCtx extends DeviceAwareSessionContext { public class DeviceSessionCtx extends MqttDeviceAwareSessionContext {
private final MqttTransportAdaptor adaptor; private final MqttTransportAdaptor adaptor;
private final MqttSessionId sessionId; private final MqttSessionId sessionId;
@ -44,8 +46,8 @@ public class DeviceSessionCtx extends DeviceAwareSessionContext {
private volatile boolean allowAttributeResponses; private volatile boolean allowAttributeResponses;
private AtomicInteger msgIdSeq = new AtomicInteger(0); private AtomicInteger msgIdSeq = new AtomicInteger(0);
public DeviceSessionCtx(SessionMsgProcessor processor, DeviceAuthService authService, MqttTransportAdaptor adaptor) { public DeviceSessionCtx(SessionMsgProcessor processor, DeviceAuthService authService, MqttTransportAdaptor adaptor, ConcurrentMap<String, Integer> mqttQoSMap) {
super(processor, authService); super(processor, authService, mqttQoSMap);
this.adaptor = adaptor; this.adaptor = adaptor;
this.sessionId = new MqttSessionId(); this.sessionId = new MqttSessionId();
} }

View File

@ -38,13 +38,15 @@ import org.thingsboard.server.transport.mqtt.MqttTransportHandler;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
/** /**
* Created by ashvayka on 19.01.17. * 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 Gson GSON = new Gson();
private static final Charset UTF8 = Charset.forName("UTF-8"); private static final Charset UTF8 = Charset.forName("UTF-8");
@ -56,8 +58,8 @@ public class GatewayDeviceSessionCtx extends DeviceAwareSessionContext {
private volatile boolean closed; private volatile boolean closed;
private AtomicInteger msgIdSeq = new AtomicInteger(0); private AtomicInteger msgIdSeq = new AtomicInteger(0);
public GatewayDeviceSessionCtx(GatewaySessionCtx parent, Device device) { public GatewayDeviceSessionCtx(GatewaySessionCtx parent, Device device, ConcurrentMap<String, Integer> mqttQoSMap) {
super(parent.getProcessor(), parent.getAuthService(), device); super(parent.getProcessor(), parent.getAuthService(), device, mqttQoSMap);
this.parent = parent; this.parent = parent;
this.sessionId = new MqttSessionId(); this.sessionId = new MqttSessionId();
} }
@ -195,7 +197,7 @@ public class GatewayDeviceSessionCtx extends DeviceAwareSessionContext {
private MqttPublishMessage createMqttPublishMsg(String topic, JsonElement json) { private MqttPublishMessage createMqttPublishMsg(String topic, JsonElement json) {
MqttFixedHeader mqttFixedHeader = 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()); MqttPublishVariableHeader header = new MqttPublishVariableHeader(topic, msgIdSeq.incrementAndGet());
ByteBuf payload = ALLOCATOR.buffer(); ByteBuf payload = ALLOCATOR.buffer();
payload.writeBytes(GSON.toJson(json).getBytes(UTF8)); payload.writeBytes(GSON.toJson(json).getBytes(UTF8));

View File

@ -43,6 +43,7 @@ import org.thingsboard.server.transport.mqtt.MqttTransportHandler;
import org.thingsboard.server.transport.mqtt.adaptors.JsonMqttAdaptor; import org.thingsboard.server.transport.mqtt.adaptors.JsonMqttAdaptor;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.thingsboard.server.transport.mqtt.adaptors.JsonMqttAdaptor.validateJsonPayload; import static org.thingsboard.server.transport.mqtt.adaptors.JsonMqttAdaptor.validateJsonPayload;
@ -63,6 +64,7 @@ public class GatewaySessionCtx {
private final DeviceAuthService authService; private final DeviceAuthService authService;
private final RelationService relationService; private final RelationService relationService;
private final Map<String, GatewayDeviceSessionCtx> devices; private final Map<String, GatewayDeviceSessionCtx> devices;
private final ConcurrentMap<String, Integer> mqttQoSMap;
private ChannelHandlerContext channel; private ChannelHandlerContext channel;
public GatewaySessionCtx(SessionMsgProcessor processor, DeviceService deviceService, DeviceAuthService authService, RelationService relationService, DeviceSessionCtx gatewaySessionCtx) { public GatewaySessionCtx(SessionMsgProcessor processor, DeviceService deviceService, DeviceAuthService authService, RelationService relationService, DeviceSessionCtx gatewaySessionCtx) {
@ -73,6 +75,7 @@ public class GatewaySessionCtx {
this.gateway = gatewaySessionCtx.getDevice(); this.gateway = gatewaySessionCtx.getDevice();
this.gatewaySessionId = gatewaySessionCtx.getSessionId(); this.gatewaySessionId = gatewaySessionCtx.getSessionId();
this.devices = new HashMap<>(); this.devices = new HashMap<>();
this.mqttQoSMap = gatewaySessionCtx.getMqttQoSMap();
} }
public void onDeviceConnect(MqttPublishMessage msg) throws AdaptorException { public void onDeviceConnect(MqttPublishMessage msg) throws AdaptorException {
@ -96,7 +99,7 @@ public class GatewaySessionCtx {
relationService.saveRelationAsync(new EntityRelation(gateway.getId(), device.getId(), "Created")); relationService.saveRelationAsync(new EntityRelation(gateway.getId(), device.getId(), "Created"));
processor.onDeviceAdded(device); processor.onDeviceAdded(device);
} }
GatewayDeviceSessionCtx ctx = new GatewayDeviceSessionCtx(this, device); GatewayDeviceSessionCtx ctx = new GatewayDeviceSessionCtx(this, device, mqttQoSMap);
devices.put(deviceName, ctx); devices.put(deviceName, ctx);
log.debug("[{}] Added device [{}] to the gateway session", gatewaySessionId, deviceName); log.debug("[{}] Added device [{}] to the gateway session", gatewaySessionId, deviceName);
processor.process(new BasicTransportToDeviceSessionActorMsg(device, new BasicAdaptorToSessionActorMsg(ctx, new AttributesSubscribeMsg()))); processor.process(new BasicTransportToDeviceSessionActorMsg(device, new BasicAdaptorToSessionActorMsg(ctx, new AttributesSubscribeMsg())));

View File

@ -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;
}
}
}

View File

@ -251,7 +251,7 @@
"fill", "fill",
"stroke" "stroke"
], ],
"property-no-vendor-prefix": null, "property-no-vendor-prefix": true,
"rule-empty-line-before": ["always", { "rule-empty-line-before": ["always", {
"except": ["first-nested"], "except": ["first-nested"],
"ignore": ["after-comment"] "ignore": ["after-comment"]
@ -272,7 +272,7 @@
"selector-max-type": 5, "selector-max-type": 5,
"selector-max-universal": 1, "selector-max-universal": 1,
"selector-no-qualifying-type": null, "selector-no-qualifying-type": null,
"selector-no-vendor-prefix": null, "selector-no-vendor-prefix": true,
"selector-type-no-unknown": [true, { "selector-type-no-unknown": [true, {
"ignoreTypes": [ "ignoreTypes": [
"/^md-/", "/^md-/",
@ -287,6 +287,6 @@
"value-list-comma-newline-after": "always-multi-line", "value-list-comma-newline-after": "always-multi-line",
"value-list-comma-newline-before": "never-multi-line", "value-list-comma-newline-before": "never-multi-line",
"value-list-comma-space-after": "always-single-line", "value-list-comma-space-after": "always-single-line",
"value-no-vendor-prefix": null "value-no-vendor-prefix": true
} }
} }

View File

@ -119,7 +119,7 @@
"ng-annotate-loader": "^0.1.1", "ng-annotate-loader": "^0.1.1",
"ngtemplate-loader": "^1.3.1", "ngtemplate-loader": "^1.3.1",
"node-sass": "^4.5.3", "node-sass": "^4.5.3",
"postcss-loader": "^0.13.0", "postcss-loader": "^3.0.0",
"raw-loader": "^0.5.1", "raw-loader": "^0.5.1",
"react-hot-loader": "^3.0.0-beta.6", "react-hot-loader": "^3.0.0-beta.6",
"sass-loader": "^4.0.2", "sass-loader": "^4.0.2",
@ -145,5 +145,19 @@
"node_modules", "node_modules",
"target" "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
}
} }
} }

View File

@ -22,7 +22,7 @@ div.tb-widget {
overflow: hidden; overflow: hidden;
outline: none; outline: none;
@include transition(all .2s ease-in-out); transition: all .2s ease-in-out;
.tb-widget-title { .tb-widget-title {
max-height: 60px; max-height: 60px;
@ -99,7 +99,7 @@ md-content.tb-dashboard-content {
outline: none; outline: none;
.gridster-item { .gridster-item {
@include transition(none); transition: none;
} }
} }

View File

@ -20,7 +20,7 @@
} }
.tb-card-item { .tb-card-item {
@include transition(all .2s ease-in-out); transition: all .2s ease-in-out;
md-card-content { md-card-content {
max-height: 53px; max-height: 53px;
@ -46,7 +46,7 @@
.tb-current-item { .tb-current-item {
opacity: .5; opacity: .5;
@include transform(scale(1.05)); transform: scale(1.05);
} }
#tb-vertical-container { #tb-vertical-container {

View File

@ -16,7 +16,7 @@
@import "~compass-sass-mixins/lib/compass"; @import "~compass-sass-mixins/lib/compass";
.md-button-toggle .md-toggle-icon.tb-toggled { .md-button-toggle .md-toggle-icon.tb-toggled {
@include transform(rotateZ(180deg)); transform: rotateZ(180deg);
} }
.tb-menu-toggle-list.ng-hide { .tb-menu-toggle-list.ng-hide {
@ -28,7 +28,7 @@
z-index: 1; z-index: 1;
overflow: hidden; 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;
} }

View File

@ -28,7 +28,7 @@ $input-label-float-scale: .75 !default;
line-height: 12px; line-height: 12px;
color: rgb(244, 67, 54); 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 { .tb-container {
@ -77,14 +77,13 @@ label.tb-label {
bottom: 100%; bottom: 100%;
left: 0; left: 0;
color: rgba(0, 0, 0, .54); color: rgba(0, 0, 0, .54);
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; transform-origin: left top;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
@include transform(translate3d(0, $input-label-float-offset, 0) scale($input-label-float-scale));
@include transition(transform $swift-ease-out-timing-function $swift-ease-out-duration,
width $swift-ease-out-timing-function $swift-ease-out-duration);
&.tb-focused { &.tb-focused {
color: rgb(96, 125, 139); color: rgb(96, 125, 139);
} }

View File

@ -53,7 +53,7 @@
margin: auto 0 auto auto; margin: auto 0 auto auto;
background-size: 100% auto; background-size: 100% auto;
@include transition(transform .3s, ease-in-out); transition: transform .3s, ease-in-out;
} }
.tb-side-menu .md-button { .tb-side-menu .md-button {

View File

@ -244,13 +244,18 @@ function ManageWidgetActionsController($rootScope, $scope, $document, $mdDialog,
vm.widgetActions[actionSourceId] = targetActions; vm.widgetActions[actionSourceId] = targetActions;
} }
if (prevActionId) { if (prevActionId) {
var index = getActionIndex(prevActionId, vm.allActions); const indexInTarget = getActionIndex(prevActionId, targetActions);
if (index > -1) { const indexInAllActions = getActionIndex(prevActionId, vm.allActions);
vm.allActions[index] = action; 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 (indexInAllActions > -1) {
if (index > -1) { vm.allActions[indexInAllActions] = action;
targetActions[index] = widgetAction;
} }
} else { } else {
vm.allActions.push(action); vm.allActions.push(action);

View File

@ -16,12 +16,12 @@
.tb-dashboard-assigned-customers { .tb-dashboard-assigned-customers {
display: block; display: block;
display: -webkit-box; display: -webkit-box; /* stylelint-disable-line value-no-vendor-prefix */
height: 34px; height: 34px;
margin-bottom: 4px; margin-bottom: 4px;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
-webkit-line-clamp: 2; -webkit-line-clamp: 2;
-webkit-box-orient: vertical; -webkit-box-orient: vertical; /* stylelint-disable-line property-no-vendor-prefix */
} }

View File

@ -31,7 +31,7 @@ tb-dashboard-toolbar {
&.md-fab { &.md-fab {
opacity: 1; 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 { .md-fab-toolbar-background {
background-color: $primary-default !important; background-color: $primary-default !important;
@ -50,7 +50,7 @@ tb-dashboard-toolbar {
line-height: 36px; line-height: 36px;
opacity: .5; 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 { md-icon {
position: absolute; position: absolute;

View File

@ -75,13 +75,13 @@ section.tb-dashboard-toolbar {
&.tb-dashboard-toolbar-opened { &.tb-dashboard-toolbar-opened {
right: 0; 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 { &.tb-dashboard-toolbar-closed {
right: 18px; 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; 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 { &.tb-dashboard-toolbar-closed {
margin-top: 0; 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 { .tb-dashboard-layouts {
@ -133,7 +133,7 @@ section.tb-powered-by-footer {
position: absolute; position: absolute;
right: 25px; right: 25px;
bottom: 5px; bottom: 5px;
z-index: 3; z-index: 30;
pointer-events: none; pointer-events: none;
span { span {

View File

@ -146,7 +146,7 @@
ng-style="{minWidth: vm.rightLayoutWidth(), ng-style="{minWidth: vm.rightLayoutWidth(),
maxWidth: vm.rightLayoutWidth(), maxWidth: vm.rightLayoutWidth(),
height: vm.rightLayoutHeight(), height: vm.rightLayoutHeight(),
zIndex: 12}" zIndex: 25}"
md-component-id="right-dashboard-layout" md-component-id="right-dashboard-layout"
aria-label="Right dashboard layout" aria-label="Right dashboard layout"
md-is-open="vm.rightLayoutOpened" md-is-open="vm.rightLayoutOpened"

View File

@ -42,7 +42,7 @@
border: none; border: none;
opacity: .75; opacity: .75;
@include transition(opacity .35s); transition: opacity .35s;
} }
a:hover, a:hover,

View File

@ -84,9 +84,6 @@
.tb-panel-title { .tb-panel-title {
min-width: 150px; min-width: 150px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none; user-select: none;
} }
@ -163,10 +160,6 @@
.fc-canvas { .fc-canvas {
min-width: 100%; min-width: 100%;
min-height: 100%; min-height: 100%;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none; user-select: none;
outline: none; outline: none;
-webkit-touch-callout: none; -webkit-touch-callout: none;
@ -441,13 +434,7 @@
} }
.fc-noselect { .fc-noselect {
-webkit-touch-callout: none; /* iOS Safari */ user-select: none;
-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 */
} }
.fc-edge-label { .fc-edge-label {
@ -495,7 +482,6 @@
font-weight: 600; font-weight: 600;
text-align: center; text-align: center;
white-space: nowrap; white-space: nowrap;
-webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
span { span {

View File

@ -29,7 +29,7 @@ md-dialog.tb-node-script-test-dialog {
} }
.tb-split { .tb-split {
@include box-sizing(border-box); box-sizing: border-box;
overflow-x: hidden; overflow-x: hidden;
overflow-y: auto; overflow-y: auto;
} }

View File

@ -371,7 +371,9 @@ function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $mdP
content = strContent; content = strContent;
} }
} else { } 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; return content;
} else { } 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*/) { function defaultStyle(/*key, value*/) {
return {}; return {};
} }
@ -542,7 +536,9 @@ function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $mdP
vm.contentsInfo[dataKey.label] = { vm.contentsInfo[dataKey.label] = {
useCellContentFunction: useCellContentFunction, useCellContentFunction: useCellContentFunction,
cellContentFunction: cellContentFunction cellContentFunction: cellContentFunction,
units: dataKey.units,
decimals: dataKey.decimals
}; };
var columnWidth = angular.isDefined(keySettings.columnWidth) ? keySettings.columnWidth : '0px'; var columnWidth = angular.isDefined(keySettings.columnWidth) ? keySettings.columnWidth : '0px';

View File

@ -333,6 +333,7 @@ export default class TbFlot {
lineWidth: 0, lineWidth: 0,
fill: 0.9 fill: 0.9
} }
ctx.defaultBarWidth = settings.defaultBarWidth || 600;
} }
if (this.chartType === 'state') { if (this.chartType === 'state') {
@ -476,8 +477,12 @@ export default class TbFlot {
this.options.yaxes = angular.copy(this.yaxes); this.options.yaxes = angular.copy(this.yaxes);
if (this.chartType === 'line' || this.chartType === 'bar' || this.chartType === 'state') { if (this.chartType === 'line' || this.chartType === 'bar' || this.chartType === 'state') {
if (this.chartType === 'bar') { if (this.chartType === 'bar') {
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.series.bars.barWidth = this.subscription.timeWindow.interval * 0.6;
} }
}
this.options.xaxis.min = this.subscription.timeWindow.minTime; this.options.xaxis.min = this.subscription.timeWindow.minTime;
this.options.xaxis.max = this.subscription.timeWindow.maxTime; this.options.xaxis.max = this.subscription.timeWindow.maxTime;
} }
@ -594,8 +599,12 @@ export default class TbFlot {
this.options.xaxis.min = this.subscription.timeWindow.minTime; this.options.xaxis.min = this.subscription.timeWindow.minTime;
this.options.xaxis.max = this.subscription.timeWindow.maxTime; this.options.xaxis.max = this.subscription.timeWindow.maxTime;
if (this.chartType === 'bar') { if (this.chartType === 'bar') {
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.series.bars.barWidth = this.subscription.timeWindow.interval * 0.6;
} }
}
if (axisVisibilityChanged) { if (axisVisibilityChanged) {
this.redrawPlot(); this.redrawPlot();
@ -603,8 +612,12 @@ export default class TbFlot {
this.ctx.plot.getOptions().xaxes[0].min = this.subscription.timeWindow.minTime; this.ctx.plot.getOptions().xaxes[0].min = this.subscription.timeWindow.minTime;
this.ctx.plot.getOptions().xaxes[0].max = this.subscription.timeWindow.maxTime; this.ctx.plot.getOptions().xaxes[0].max = this.subscription.timeWindow.maxTime;
if (this.chartType === 'bar') { if (this.chartType === 'bar') {
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.ctx.plot.getOptions().series.bars.barWidth = this.subscription.timeWindow.interval * 0.6;
} }
}
this.updateData(); this.updateData();
} }
} else if (this.chartType === 'pie') { } else if (this.chartType === 'pie') {
@ -810,53 +823,69 @@ export default class TbFlot {
} }
} }
static get settingsSchema() { static settingsSchema(chartType) {
return {
var schema = {
"schema": { "schema": {
"type": "object", "type": "object",
"title": "Settings", "title": "Settings",
"properties": { "properties": {
"stack": { }
}
};
var properties = schema["schema"]["properties"];
properties["stack"] = {
"title": "Stacking", "title": "Stacking",
"type": "boolean", "type": "boolean",
"default": false "default": false
}, };
"smoothLines": { if (chartType === 'graph') {
properties["smoothLines"] = {
"title": "Display smooth (curved) lines", "title": "Display smooth (curved) lines",
"type": "boolean", "type": "boolean",
"default": false "default": false
}, };
"shadowSize": { }
if (chartType === 'bar') {
properties["defaultBarWidth"] = {
"title": "Default bar width for non-aggregated data (milliseconds)",
"type": "number",
"default": 600
};
}
properties["shadowSize"] = {
"title": "Shadow size", "title": "Shadow size",
"type": "number", "type": "number",
"default": 4 "default": 4
}, };
"fontColor": { properties["fontColor"] = {
"title": "Font color", "title": "Font color",
"type": "string", "type": "string",
"default": "#545454" "default": "#545454"
}, };
"fontSize": { properties["fontSize"] = {
"title": "Font size", "title": "Font size",
"type": "number", "type": "number",
"default": 10 "default": 10
}, };
"tooltipIndividual": { properties["tooltipIndividual"] = {
"title": "Hover individual points", "title": "Hover individual points",
"type": "boolean", "type": "boolean",
"default": false "default": false
}, };
"tooltipCumulative": { properties["tooltipCumulative"] = {
"title": "Show cumulative values in stacking mode", "title": "Show cumulative values in stacking mode",
"type": "boolean", "type": "boolean",
"default": false "default": false
}, };
"tooltipValueFormatter": { properties["tooltipValueFormatter"] = {
"title": "Tooltip value format function, f(value)", "title": "Tooltip value format function, f(value)",
"type": "string", "type": "string",
"default": "" "default": ""
}, };
"grid": {
properties["grid"] = {
"title": "Grid settings", "title": "Grid settings",
"type": "object", "type": "object",
"properties": { "properties": {
@ -891,8 +920,9 @@ export default class TbFlot {
"default": true "default": true
} }
} }
}, };
"xaxis": {
properties["xaxis"] = {
"title": "X axis settings", "title": "X axis settings",
"type": "object", "type": "object",
"properties": { "properties": {
@ -917,8 +947,9 @@ export default class TbFlot {
"default": null "default": null
} }
} }
}, };
"yaxis": {
properties["yaxis"] = {
"title": "Y axis settings", "title": "Y axis settings",
"type": "object", "type": "object",
"properties": { "properties": {
@ -968,26 +999,29 @@ export default class TbFlot {
"default": null "default": null
} }
} }
};
schema["schema"]["required"] = [];
schema["form"] = ["stack"];
if (chartType === 'graph') {
schema["form"].push("smoothLines");
} }
}, if (chartType === 'bar') {
"required": [] schema["form"].push("defaultBarWidth");
}, }
"form": [ schema["form"].push("shadowSize");
"stack", schema["form"].push({
"smoothLines",
"shadowSize",
{
"key": "fontColor", "key": "fontColor",
"type": "color" "type": "color"
}, });
"fontSize", schema["form"].push("fontSize");
"tooltipIndividual", schema["form"].push("tooltipIndividual");
"tooltipCumulative", schema["form"].push("tooltipCumulative");
{ schema["form"].push({
"key": "tooltipValueFormatter", "key": "tooltipValueFormatter",
"type": "javascript" "type": "javascript"
}, });
{ schema["form"].push({
"key": "grid", "key": "grid",
"items": [ "items": [
{ {
@ -1006,8 +1040,8 @@ export default class TbFlot {
"grid.verticalLines", "grid.verticalLines",
"grid.horizontalLines" "grid.horizontalLines"
] ]
}, });
{ schema["form"].push({
"key": "xaxis", "key": "xaxis",
"items": [ "items": [
"xaxis.showLabels", "xaxis.showLabels",
@ -1018,8 +1052,8 @@ export default class TbFlot {
"type": "color" "type": "color"
} }
] ]
}, });
{ schema["form"].push({
"key": "yaxis", "key": "yaxis",
"items": [ "items": [
"yaxis.min", "yaxis.min",
@ -1038,10 +1072,8 @@ export default class TbFlot {
"type": "javascript" "type": "javascript"
} }
] ]
} });
return schema;
]
}
} }
static get pieDatakeySettingsSchema() { static get pieDatakeySettingsSchema() {

View File

@ -37,8 +37,6 @@ $background-color: #e6e7e8 !default;
position: relative; position: relative;
&[draggable] { &[draggable] {
-moz-user-select: none;
-webkit-user-select: none;
user-select: none; user-select: none;
} }

View File

@ -60,19 +60,11 @@ $background-color: #e6e7e8 !default;
.led { .led {
position: relative; position: relative;
cursor: pointer; 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)); background-image: radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, .25));
border-radius: 50%; border-radius: 50%;
transition: background-color .5s, box-shadow .5s; transition: background-color .5s, box-shadow .5s;
&.disabled { &.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)); background-image: radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, .5), rgba(0, 0, 0, .1));
} }
} }

View File

@ -59,6 +59,8 @@ $background-color: #e6e7e8 !default;
.switch { .switch {
position: relative; position: relative;
box-sizing: border-box;
width: 260px; width: 260px;
min-width: 260px; min-width: 260px;
height: 260px; height: 260px;
@ -69,21 +71,14 @@ $background-color: #e6e7e8 !default;
color: #424242; color: #424242;
cursor: pointer; cursor: pointer;
-pie-background: -pie-linear-gradient(270deg, #bbb, #ddd);
background: #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); background: linear-gradient(180deg, #bbb, #ddd);
border-radius: 130px; border-radius: 130px;
@include box-sizing(border-box); box-shadow:
0 0 0 8px rgba(0, 0, 0, .1),
@include box-shadow( 0 0 3px 1px rgba(0, 0, 0, .1),
0 0 0 8px rgba(0,0,0,.1) inset 0 8px 3px -8px rgba(255, 255, 255, .4);
,0 0 3px 1px rgba(0,0,0,.1)
,inset 0 8px 3px -8px rgba(255,255,255,.4));
input { input {
display: none; display: none;
@ -95,7 +90,7 @@ $background-color: #e6e7e8 !default;
width: 100%; width: 100%;
text-align: center; text-align: center;
@include text-shadow(1px 1px 4px #4a4a4a); text-shadow: 1px 1px 4px #4a4a4a;
} }
.on { .on {
@ -103,15 +98,15 @@ $background-color: #e6e7e8 !default;
font-family: sans-serif; font-family: sans-serif;
color: #444; color: #444;
@include transition(all .1s); transition: all .1s;
} }
.off { .off {
bottom: 5px; bottom: 5px;
@include transition(all .1s); transition: all .1s;
@include transform(scaleY(.85)); transform: scaleY(.85);
} }
.but { .but {
@ -125,90 +120,82 @@ $background-color: #e6e7e8 !default;
border-bottom-width: 0; border-bottom-width: 0;
border-radius: 400px 400px 400px 400px / 400px 400px 300px 300px; border-radius: 400px 400px 400px 400px / 400px 400px 300px 300px;
@include box-shadow(inset 8px 6px 5px -7px #a2a2a2, box-shadow:
inset 8px 6px 5px -7px #a2a2a2,
inset -8px 6px 5px -7px #a2a2a2, inset -8px 6px 5px -7px #a2a2a2,
inset 0 -3px 2px -2px rgba(200, 200, 200, .5), inset 0 -3px 2px -2px rgba(200, 200, 200, .5),
0 3px 3px -2px #fff, 0 3px 3px -2px #fff,
inset 0 -230px 60px -200px rgba(255, 255, 255, .2), inset 0 -230px 60px -200px rgba(255, 255, 255, .2),
inset 0 220px 40px -200px rgba(0, 0, 0, .3)); inset 0 220px 40px -200px rgba(0, 0, 0, .3);
@include transition(all .2s); transition: all .2s;
} }
.back { .back {
box-sizing: border-box;
width: 210px; width: 210px;
height: 210px; height: 210px;
padding: 4px 4px; padding: 4px 4px;
cursor: pointer; cursor: pointer;
background-color: #888787; 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%); 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; border-radius: 105px;
@include box-shadow(30px 30px 30px -20px rgba(58, 58, 58, .3), box-shadow:
30px 30px 30px -20px rgba(58, 58, 58, .3),
-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), 0 30px 30px 0 rgba(16, 16, 16, .3),
inset 0 -1px 0 0 #484848); inset 0 -1px 0 0 #484848;
@include box-sizing(border-box); transition: all .2s;
@include transition(all .2s);
} }
input:checked + .back .on, input:checked + .back .on,
input:checked + .back .off{ input:checked + .back .off{
@include text-shadow(1px 1px 4px #4a4a4a); text-shadow: 1px 1px 4px #4a4a4a;
} }
input:checked + .back .on{ input:checked + .back .on{
top: 10px; top: 10px;
color: #4c4c4c; color: #4c4c4c;
@include transform(scaleY(.85)); transform: scaleY(.85);
} }
input:checked + .back .off{ input:checked + .back .off{
bottom: 5px; bottom: 5px;
color: #444; color: #444;
@include transform(scaleY(1)); transform: scaleY(1);
} }
input:checked + .back .but{ input:checked + .back .but{
margin-top: 20px; margin-top: 20px;
background: #dcdcdc; 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); 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; border-radius: 400px 400px 400px 400px / 300px 300px 400px 400px;
@include box-shadow(inset 8px -4px 5px -7px #a9a9a9, box-shadow:
inset 8px -4px 5px -7px #a9a9a9,
inset -8px -4px 5px -7px #808080, inset -8px -4px 5px -7px #808080,
0 -3px 8px -4px rgba(50, 50, 50, .4), 0 -3px 8px -4px rgba(50, 50, 50, .4),
inset 0 3px 4px -2px #9c9c9c, inset 0 3px 4px -2px #9c9c9c,
inset 0 280px 40px -200px rgba(0, 0, 0, .2), inset 0 280px 40px -200px rgba(0, 0, 0, .2),
inset 0 -200px 40px -200px rgba(180, 180, 180, .2)); inset 0 -200px 40px -200px rgba(180, 180, 180, .2);
} }
input:checked + .back{ input:checked + .back{
padding: 2px 4px; 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%); 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), box-shadow:
30px 30px 30px -20px rgba(49, 49, 49, .1),
-30px 30px 30px -20px rgba(111, 111, 111, .1), -30px 30px 30px -20px rgba(111, 111, 111, .1),
0 30px 30px 0 rgba(0, 0, 0, .2), 0 30px 30px 0 rgba(0, 0, 0, .2),
inset 0 1px 2px 0 rgba(167, 167, 167, .6)); inset 0 1px 2px 0 rgba(167, 167, 167, .6);
} }
} }
} }

View File

@ -217,7 +217,9 @@ function TimeseriesTableWidgetController($element, $scope, $filter, $timeout) {
content = strContent; content = strContent;
} }
} else { } 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; return content;
} }

View File

@ -19,7 +19,7 @@ $edit-toolbar-height: 40px !default;
.tb-editor { .tb-editor {
.tb-split { .tb-split {
@include box-sizing(border-box); box-sizing: border-box;
overflow-x: hidden; overflow-x: hidden;
overflow-y: auto; overflow-y: auto;
} }

View File

@ -15,34 +15,34 @@
*/ */
@import "~compass-sass-mixins/lib/animate"; @import "~compass-sass-mixins/lib/animate";
@include keyframes(tbMoveFromTopFade) { @keyframes tbMoveFromTopFade {
from { from {
opacity: 0; opacity: 0;
@include transform(translate(0, -100%)); transform: translate(0, -100%);
} }
} }
@include keyframes(tbMoveToTopFade) { @keyframes tbMoveToTopFade {
to { to {
opacity: 0; opacity: 0;
@include transform(translate(0, -100%)); transform: translate(0, -100%);
} }
} }
@include keyframes(tbMoveFromBottomFade) { @keyframes tbMoveFromBottomFade {
from { from {
opacity: 0; opacity: 0;
@include transform(translate(0, 100%)); transform: translate(0, 100%);
} }
} }
@include keyframes(tbMoveToBottomFade) { @keyframes tbMoveToBottomFade {
to { to {
opacity: 0; opacity: 0;
@include transform(translate(0, 150%)); transform: translate(0, 150%);
} }
} }

View File

@ -42,7 +42,7 @@ textarea {
word-wrap: normal; word-wrap: normal;
white-space: nowrap; white-space: nowrap;
direction: ltr; direction: ltr;
-webkit-font-feature-settings: "liga"; -webkit-font-feature-settings: "liga"; /* stylelint-disable-line property-no-vendor-prefix */
} }
a { a {
@ -51,7 +51,7 @@ a {
text-decoration: none; text-decoration: none;
border-bottom: 1px solid rgba(64, 84, 178, .25); border-bottom: 1px solid rgba(64, 84, 178, .25);
@include transition(border-bottom .35s); transition: border-bottom .35s;
} }
a:hover, a:hover,
@ -258,13 +258,7 @@ label {
} }
.tb-noselect { .tb-noselect {
-webkit-touch-callout: none; /* iOS Safari */ user-select: none;
-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 */
} }
.tb-readonly-label { .tb-readonly-label {
@ -556,7 +550,7 @@ $previewSize: 100px !default;
} }
.tb-error-message.ng-animate { .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, .tb-error-message.ng-enter-prepare,
@ -652,13 +646,13 @@ section.tb-top-header-buttons {
} }
.tb-header-buttons .tb-btn-header { .tb-header-buttons .tb-btn-header {
@include animation(tbMoveFromTopFade .3s ease both);
position: relative !important; position: relative !important;
display: inline-block !important; display: inline-block !important;
animation: tbMoveFromTopFade .3s ease both;
} }
.tb-header-buttons .tb-btn-header.ng-hide { .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; position: fixed;
right: 20px; right: 20px;
bottom: 20px; bottom: 20px;
z-index: 13; z-index: 30;
pointer-events: none; pointer-events: none;
} }
.tb-footer-buttons .tb-btn-footer { .tb-footer-buttons .tb-btn-footer {
@include animation(tbMoveFromBottomFade .3s ease both);
position: relative !important; position: relative !important;
display: inline-block !important; display: inline-block !important;
animation: tbMoveFromBottomFade .3s ease both;
} }
.tb-footer-buttons .tb-btn-footer.ng-hide { .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 { ._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);
} }
/*********************** /***********************

View File

@ -15,6 +15,7 @@
*/ */
@import "~compass-sass-mixins/lib/compass"; @import "~compass-sass-mixins/lib/compass";
/* stylelint-disable selector-no-vendor-prefix */
@mixin input-placeholder { @mixin input-placeholder {
// replaces compass/css/user-interface/input-placeholder() // replaces compass/css/user-interface/input-placeholder()
@ -36,6 +37,7 @@
@content; @content;
} }
} }
/* stylelint-enable selector-no-vendor-prefix */
@mixin line-clamp($numLines: 1, $lineHeight: 1.412) { @mixin line-clamp($numLines: 1, $lineHeight: 1.412) {
position: relative; position: relative;