Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Vladyslav_Prykhodko 2019-12-23 18:22:30 +02:00
commit caa7b8f420
33 changed files with 451 additions and 65 deletions

View File

@ -50,7 +50,7 @@
"controllerScript": "self.onInit = function() {\n var scope = self.ctx.$scope;\n scope.ctx = self.ctx;\n}\n\nself.onResize = function() {\n if (self.ctx.resize) {\n self.ctx.resize();\n }\n}\n\nself.onDestroy = function() {\n}\n",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"minValue\": {\n \"title\": \"Minimum value\",\n \"type\": \"number\",\n \"default\": 0\n },\n \"maxValue\": {\n \"title\": \"Maximum value\",\n \"type\": \"number\",\n \"default\": 100\n },\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"number\",\n \"default\": 50\n },\n \"title\": {\n \"title\": \"Knob title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"getValueMethod\": {\n \"title\": \"Get value method\",\n \"type\": \"string\",\n \"default\": \"getValue\"\n },\n \"setValueMethod\": {\n \"title\": \"Set value method\",\n \"type\": \"string\",\n \"default\": \"setValue\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout\",\n \"type\": \"number\",\n \"default\": 500\n }\n },\n \"required\": [\"minValue\", \"maxValue\", \"getValueMethod\", \"setValueMethod\", \"requestTimeout\"]\n },\n \"form\": [\n \"minValue\",\n \"maxValue\",\n \"initialValue\",\n \"title\",\n \"getValueMethod\",\n \"setValueMethod\",\n \"requestTimeout\"\n ]\n}",
"dataKeySettingsSchema": "{}\n",
"defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"maxValue\":100,\"initialValue\":50,\"minValue\":0,\"title\":\"Knob control\",\"getValueMethod\":\"getValue\",\"setValueMethod\":\"setValue\"},\"title\":\"Knob Control\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}"
"defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"maxValue\":100,\"initialValue\":50,\"minValue\":0,\"title\":\"Knob control\",\"getValueMethod\":\"getValue\",\"setValueMethod\":\"setValue\"},\"title\":\"Knob Control\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}"
}
},
{
@ -66,7 +66,7 @@
"controllerScript": "self.onInit = function() {\n var scope = self.ctx.$scope;\n scope.ctx = self.ctx;\n}\n\nself.onResize = function() {\n if (self.ctx.resize) {\n self.ctx.resize();\n }\n}\n\nself.onDestroy = function() {\n}\n",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"title\": {\n \"title\": \"Switch title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"showOnOffLabels\": {\n \"title\": \"Show on/off labels\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"retrieveValueMethod\": {\n \"title\": \"Retrieve on/off value using method\",\n \"type\": \"string\",\n \"default\": \"rpc\"\n },\n \"valueKey\": {\n \"title\": \"Attribute/Timeseries value key (only when subscribe for attribute/timeseries method)\",\n \"type\": \"string\",\n \"default\": \"value\"\n },\n \"getValueMethod\": {\n \"title\": \"RPC get value method\",\n \"type\": \"string\",\n \"default\": \"getValue\"\n },\n \"setValueMethod\": {\n \"title\": \"RPC set value method\",\n \"type\": \"string\",\n \"default\": \"setValue\"\n },\n \"parseValueFunction\": {\n \"title\": \"Parse value function, f(data), returns boolean\",\n \"type\": \"string\",\n \"default\": \"return data ? true : false;\"\n },\n \"convertValueFunction\": {\n \"title\": \"Convert value function, f(value), returns payload used by RPC set value method\",\n \"type\": \"string\",\n \"default\": \"return value;\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout\",\n \"type\": \"number\",\n \"default\": 500\n }\n },\n \"required\": [\"requestTimeout\"]\n },\n \"form\": [\n \"initialValue\",\n \"title\",\n \"showOnOffLabels\",\n {\n \"key\": \"retrieveValueMethod\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"none\",\n \"label\": \"Don't retrieve\"\n },\n {\n \"value\": \"rpc\",\n \"label\": \"Call RPC get value method\"\n },\n {\n \"value\": \"attribute\",\n \"label\": \"Subscribe for attribute\"\n },\n {\n \"value\": \"timeseries\",\n \"label\": \"Subscribe for timeseries\"\n }\n ]\n },\n \"valueKey\",\n \"getValueMethod\",\n \"setValueMethod\",\n {\n \"key\": \"parseValueFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"convertValueFunction\",\n \"type\": \"javascript\"\n },\n \"requestTimeout\"\n ]\n}",
"dataKeySettingsSchema": "{}\n",
"defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"initialValue\":false,\"getValueMethod\":\"getValue\",\"setValueMethod\":\"setValue\",\"showOnOffLabels\":true,\"title\":\"Switch control\"},\"title\":\"Switch Control\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}"
"defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"initialValue\":false,\"getValueMethod\":\"getValue\",\"setValueMethod\":\"setValue\",\"showOnOffLabels\":true,\"title\":\"Switch control\"},\"title\":\"Switch Control\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}"
}
},
{
@ -82,7 +82,7 @@
"controllerScript": "self.onInit = function() {\n var scope = self.ctx.$scope;\n scope.ctx = self.ctx;\n}\n\nself.onResize = function() {\n if (self.ctx.resize) {\n self.ctx.resize();\n }\n}\n\nself.onDestroy = function() {\n}\n",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"title\": {\n \"title\": \"Switch title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"retrieveValueMethod\": {\n \"title\": \"Retrieve on/off value using method\",\n \"type\": \"string\",\n \"default\": \"rpc\"\n },\n \"valueKey\": {\n \"title\": \"Attribute/Timeseries value key (only when subscribe for attribute/timeseries method)\",\n \"type\": \"string\",\n \"default\": \"value\"\n },\n \"getValueMethod\": {\n \"title\": \"RPC get value method\",\n \"type\": \"string\",\n \"default\": \"getValue\"\n },\n \"setValueMethod\": {\n \"title\": \"RPC set value method\",\n \"type\": \"string\",\n \"default\": \"setValue\"\n },\n \"parseValueFunction\": {\n \"title\": \"Parse value function, f(data), returns boolean\",\n \"type\": \"string\",\n \"default\": \"return data ? true : false;\"\n },\n \"convertValueFunction\": {\n \"title\": \"Convert value function, f(value), returns payload used by RPC set value method\",\n \"type\": \"string\",\n \"default\": \"return value;\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout\",\n \"type\": \"number\",\n \"default\": 500\n }\n },\n \"required\": [\"requestTimeout\"]\n },\n \"form\": [\n \"initialValue\",\n \"title\",\n {\n \"key\": \"retrieveValueMethod\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"none\",\n \"label\": \"Don't retrieve\"\n },\n {\n \"value\": \"rpc\",\n \"label\": \"Call RPC get value method\"\n },\n {\n \"value\": \"attribute\",\n \"label\": \"Subscribe for attribute\"\n },\n {\n \"value\": \"timeseries\",\n \"label\": \"Subscribe for timeseries\"\n }\n ]\n },\n \"valueKey\",\n \"getValueMethod\",\n \"setValueMethod\",\n {\n \"key\": \"parseValueFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"convertValueFunction\",\n \"type\": \"javascript\"\n },\n \"requestTimeout\"\n ]\n}",
"dataKeySettingsSchema": "{}\n",
"defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"initialValue\":false,\"getValueMethod\":\"getValue\",\"setValueMethod\":\"setValue\",\"title\":\"Round switch\",\"retrieveValueMethod\":\"rpc\",\"valueKey\":\"value\",\"parseValueFunction\":\"return data ? true : false;\",\"convertValueFunction\":\"return value;\"},\"title\":\"Round switch\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}"
"defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"initialValue\":false,\"getValueMethod\":\"getValue\",\"setValueMethod\":\"setValue\",\"title\":\"Round switch\",\"retrieveValueMethod\":\"rpc\",\"valueKey\":\"value\",\"parseValueFunction\":\"return data ? true : false;\",\"convertValueFunction\":\"return value;\"},\"title\":\"Round switch\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}"
}
},
{
@ -98,7 +98,7 @@
"controllerScript": "self.onInit = function() {\n var scope = self.ctx.$scope;\n scope.ctx = self.ctx;\n}\n\nself.onResize = function() {\n if (self.ctx.resize) {\n self.ctx.resize();\n }\n}\n\nself.onDestroy = function() {\n}\n",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"title\": {\n \"title\": \"LED title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"ledColor\": {\n \"title\": \"LED Color\",\n \"type\": \"string\",\n \"default\": \"green\"\n },\n \"performCheckStatus\": {\n \"title\": \"Perform RPC device status check\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"checkStatusMethod\": {\n \"title\": \"RPC check device status method\",\n \"type\": \"string\",\n \"default\": \"checkStatus\"\n },\n \"retrieveValueMethod\": {\n \"title\": \"Retrieve led status value using method\",\n \"type\": \"string\",\n \"default\": \"attribute\"\n },\n \"valueAttribute\": {\n \"title\": \"Device attribute/timeseries containing led status value\",\n \"type\": \"string\",\n \"default\": \"value\"\n },\n \"parseValueFunction\": {\n \"title\": \"Parse led status value function, f(data), returns boolean\",\n \"type\": \"string\",\n \"default\": \"return data ? true : false;\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout (ms)\",\n \"type\": \"number\",\n \"default\": 500\n }\n },\n \"required\": [\"valueAttribute\", \"requestTimeout\"]\n },\n \"form\": [\n \"initialValue\",\n \"title\",\n {\n \"key\": \"ledColor\",\n \"type\": \"color\"\n },\n \"performCheckStatus\",\n \"checkStatusMethod\",\n {\n \"key\": \"retrieveValueMethod\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"attribute\",\n \"label\": \"Subscribe for attribute\"\n },\n {\n \"value\": \"timeseries\",\n \"label\": \"Subscribe for timeseries\"\n }\n ]\n },\n \"valueAttribute\",\n {\n \"key\": \"parseValueFunction\",\n \"type\": \"javascript\"\n },\n \"requestTimeout\"\n ]\n}",
"dataKeySettingsSchema": "{}\n",
"defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"initialValue\":true,\"title\":\"Led indicator\",\"ledColor\":\"#4caf50\",\"valueAttribute\":\"value\",\"retrieveValueMethod\":\"attribute\",\"parseValueFunction\":\"return data ? true : false;\",\"performCheckStatus\":true,\"checkStatusMethod\":\"checkStatus\"},\"title\":\"Led indicator\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}"
"defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"initialValue\":true,\"title\":\"Led indicator\",\"ledColor\":\"#4caf50\",\"valueAttribute\":\"value\",\"retrieveValueMethod\":\"attribute\",\"parseValueFunction\":\"return data ? true : false;\",\"performCheckStatus\":true,\"checkStatusMethod\":\"checkStatus\"},\"title\":\"Led indicator\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}"
}
},
{

View File

@ -123,10 +123,10 @@ public class ThingsboardInstallService {
log.info("Upgrading ThingsBoard from version 2.4.1 to 2.4.2 ...");
databaseUpgradeService.upgradeDatabase("2.4.1");
case "2.4.2.1":
log.info("Upgrading ThingsBoard from version 2.4.2.1 to 2.4.3 ...");
case "2.4.2":
log.info("Upgrading ThingsBoard from version 2.4.2 to 2.4.3 ...");
databaseUpgradeService.upgradeDatabase("2.4.2.1");
databaseUpgradeService.upgradeDatabase("2.4.2");
log.info("Updating system data...");

View File

@ -278,7 +278,7 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService {
} catch (InvalidQueryException e) {}
log.info("Schema updated.");
break;
case "2.4.2.1":
case "2.4.2":
log.info("Updating schema ...");
String updateAlarmTableStmt = "alter table alarm add propagate_relation_types text";
try {

View File

@ -196,7 +196,7 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService {
log.info("Schema updated.");
}
break;
case "2.4.2.1":
case "2.4.2":
try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
log.info("Updating schema ...");
try {

View File

@ -71,10 +71,7 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import static org.thingsboard.server.common.data.DataConstants.ACTIVITY_EVENT;
import static org.thingsboard.server.common.data.DataConstants.CONNECT_EVENT;
import static org.thingsboard.server.common.data.DataConstants.DISCONNECT_EVENT;
import static org.thingsboard.server.common.data.DataConstants.INACTIVITY_EVENT;
import static org.thingsboard.server.common.data.DataConstants.*;
/**
* Created by ashvayka on 01.05.18.
@ -344,6 +341,7 @@ public class DefaultDeviceStateService implements DeviceStateService {
if (stateData != null) {
DeviceState state = stateData.getState();
stateData.getState().setLastActivityTime(lastReportedActivity);
stateData.getMetaData().putValue("scope", SERVER_SCOPE);
pushRuleEngineMessage(stateData, ACTIVITY_EVENT);
save(deviceId, LAST_ACTIVITY_TIME, lastReportedActivity);
deviceLastSavedActivity.put(deviceId, lastReportedActivity);

View File

@ -0,0 +1,68 @@
#!/bin/bash
#
# Copyright © 2016-2019 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.
#
for i in "$@"
do
case $i in
--fromVersion=*)
FROM_VERSION="${i#*=}"
shift
;;
*)
# unknown option
;;
esac
done
if [[ -z "${FROM_VERSION// }" ]]; then
echo "--fromVersion parameter is invalid or unspecified!"
echo "Usage: upgrade_dev_db.sh --fromVersion={VERSION}"
exit 1
else
fromVersion="${FROM_VERSION// }"
fi
BASE=${project.basedir}/target
CONF_FOLDER=${BASE}/conf
jarfile="${BASE}/thingsboard-${project.version}-boot.jar"
installDir=${BASE}/data
loadDemo=true
export JAVA_OPTS="$JAVA_OPTS -Dplatform=@pkg.platform@"
export LOADER_PATH=${BASE}/conf,${BASE}/extensions
export SQL_DATA_FOLDER=${SQL_DATA_FOLDER:-/tmp}
run_user="$USER"
sudo -u "$run_user" -s /bin/sh -c "java -cp ${jarfile} $JAVA_OPTS -Dloader.main=org.thingsboard.server.ThingsboardInstallApplication \
-Dinstall.data_dir=${installDir} \
-Dinstall.load_demo=${loadDemo} \
-Dspring.jpa.hibernate.ddl-auto=none \
-Dinstall.upgrade=true \
-Dinstall.upgrade.from_version=${fromVersion} \
-Dlogging.config=logback.xml \
org.springframework.boot.loader.PropertiesLauncher"
if [ $? -ne 0 ]; then
echo "ThingsBoard DB installation failed!"
else
echo "ThingsBoard DB installed successfully!"
fi
exit $?

View File

@ -71,6 +71,7 @@ public class TbCopyAttributesToEntityViewNode implements TbNode {
public void onMsg(TbContext ctx, TbMsg msg) {
if (DataConstants.ATTRIBUTES_UPDATED.equals(msg.getType()) ||
DataConstants.ATTRIBUTES_DELETED.equals(msg.getType()) ||
DataConstants.ACTIVITY_EVENT.equals(msg.getType()) ||
SessionMsgType.POST_ATTRIBUTES_REQUEST.name().equals(msg.getType())) {
if (!msg.getMetaData().getData().isEmpty()) {
long now = System.currentTimeMillis();
@ -87,7 +88,8 @@ public class TbCopyAttributesToEntityViewNode implements TbNode {
long endTime = entityView.getEndTimeMs();
if ((endTime != 0 && endTime > now && startTime < now) || (endTime == 0 && startTime < now)) {
if (DataConstants.ATTRIBUTES_UPDATED.equals(msg.getType()) ||
SessionMsgType.POST_ATTRIBUTES_REQUEST.name().equals(msg.getType())) {
DataConstants.ACTIVITY_EVENT.equals(msg.getType()) ||
SessionMsgType.POST_ATTRIBUTES_REQUEST.name().equals(msg.getType()) ) {
Set<AttributeKvEntry> attributes = JsonConverter.convertToAttributes(new JsonParser().parse(msg.getData()));
List<AttributeKvEntry> filteredAttributes =
attributes.stream().filter(attr -> attributeContainsInEntityView(scope, attr.getKey(), entityView)).collect(Collectors.toList());

View File

@ -47,6 +47,7 @@ function AlarmService($http, $q, $interval, $filter, $timeout, utils, types) {
saveAlarm: saveAlarm,
ackAlarm: ackAlarm,
clearAlarm: clearAlarm,
deleteAlarm: deleteAlarm,
getAlarms: getAlarms,
getHighestAlarmSeverity: getHighestAlarmSeverity,
pollAlarms: pollAlarms,
@ -132,6 +133,21 @@ function AlarmService($http, $q, $interval, $filter, $timeout, utils, types) {
return deferred.promise;
}
function deleteAlarm(alarmId, ignoreErrors, config) {
var deferred = $q.defer();
var url = '/api/alarm/' + alarmId;
if (!config) {
config = {};
}
config = Object.assign(config, { ignoreErrors: ignoreErrors });
$http.delete(url, config).then(function success(response) {
deferred.resolve(response.data);
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function getAlarms(entityType, entityId, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator, ascOrder, config) {
var deferred = $q.defer();
var url = '/api/alarm/' + entityType + '/' + entityId + '?limit=' + pageLink.limit;

View File

@ -848,7 +848,50 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
return deferred.promise;
}
function getEntityFieldKeys (entityType, searchText) {
let entityFieldKeys = [];
let query = searchText.toLowerCase();
switch(entityType) {
case types.entityType.user:
entityFieldKeys.push(types.entityField.name.keyName);
entityFieldKeys.push(types.entityField.email.keyName);
entityFieldKeys.push(types.entityField.firstName.keyName);
entityFieldKeys.push(types.entityField.lastName.keyName);
break;
case types.entityType.tenant:
case types.entityType.customer:
entityFieldKeys.push(types.entityField.title.keyName);
entityFieldKeys.push(types.entityField.email.keyName);
entityFieldKeys.push(types.entityField.country.keyName);
entityFieldKeys.push(types.entityField.state.keyName);
entityFieldKeys.push(types.entityField.city.keyName);
entityFieldKeys.push(types.entityField.address.keyName);
entityFieldKeys.push(types.entityField.address2.keyName);
entityFieldKeys.push(types.entityField.zip.keyName);
entityFieldKeys.push(types.entityField.phone.keyName);
break;
case types.entityType.entityView:
entityFieldKeys.push(types.entityField.name.keyName);
entityFieldKeys.push(types.entityField.type.keyName);
break;
case types.entityType.device:
case types.entityType.asset:
entityFieldKeys.push(types.entityField.name.keyName);
entityFieldKeys.push(types.entityField.type.keyName);
entityFieldKeys.push(types.entityField.label.keyName);
break;
case types.entityType.dashboard:
entityFieldKeys.push(types.entityField.title.keyName);
break;
}
return query ? entityFieldKeys.filter((entityField) => entityField.toLowerCase().indexOf(query) === 0) : entityFieldKeys;
}
function getEntityKeys(entityType, entityId, query, type, config) {
if (type === types.dataKeyType.entityField) {
return $q.when(getEntityFieldKeys(entityType, query));
}
var deferred = $q.defer();
var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/keys/';
if (type === types.dataKeyType.timeseries) {

View File

@ -332,7 +332,8 @@ export default class Subscription {
for (var a = 0; a < datasource.dataKeys.length; a++) {
var dataKey = datasource.dataKeys[a];
dataKey.hidden = false;
dataKey.hidden = dataKey.settings.hideDataByDefault ? true : false;
dataKey.inLegend = dataKey.settings.removeFromLegend ? false : true;
dataKey.pattern = angular.copy(dataKey.label);
if (this.comparisonEnabled && dataKey.settings.comparisonSettings && dataKey.settings.comparisonSettings.showValuesForComparison) {
@ -349,6 +350,11 @@ export default class Subscription {
dataKey: dataKey,
data: []
};
if (dataKey.type === this.ctx.types.dataKeyType.entityField) {
if(datasource.entity && datasource.entity[this.ctx.types.entityField[dataKey.name].value]){
datasourceData.data.push([Date.now(), datasource.entity[this.ctx.types.entityField[dataKey.name].value]]);
}
}
this.data.push(datasourceData);
this.hiddenData.push({data: []});
if (this.displayLegend) {
@ -877,8 +883,14 @@ export default class Subscription {
};
}
var entityFieldKey = false;
for (var a = 0; a < datasource.dataKeys.length; a++) {
this.data[index + a].data = [];
if (datasource.dataKeys[a].type !== this.ctx.types.dataKeyType.entityField) {
this.data[index + a].data = [];
} else {
entityFieldKey = true;
}
}
index += datasource.dataKeys.length;
@ -890,7 +902,7 @@ export default class Subscription {
}
var forceUpdate = false;
if (datasource.unresolvedStateEntity ||
if (datasource.unresolvedStateEntity || entityFieldKey ||
!datasource.dataKeys.length ||
(datasource.type === this.ctx.types.datasourceType.entity && !datasource.entityId)
) {

View File

@ -281,6 +281,9 @@ function TimeService($translate, $http, $q, types) {
var historyTimewindow = {
hideInterval: timewindow.hideInterval || false,
hideAggregation: timewindow.hideAggregation || false,
hideAggInterval: timewindow.hideAggInterval || false,
history: {
fixedTimewindow: {
startTimeMs: startTimeMs,

View File

@ -78,7 +78,8 @@ export default function AppConfig($provide,
$mdIconProvider.iconSet('mdi', mdiIconSet);
ngMdIconServiceProvider
.addShape('alpha-a-circle-outline', '<path d="M11,7H13A2,2 0 0,1 15,9V17H13V13H11V17H9V9A2,2 0 0,1 11,7M11,9V11H13V9H11M12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2Z" />');
.addShape('alpha-a-circle-outline', '<path d="M11,7H13A2,2 0 0,1 15,9V17H13V13H11V17H9V9A2,2 0 0,1 11,7M11,9V11H13V9H11M12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2Z" />')
.addShape('alpha-e-circle-outline', '<path d="M9,7H15V9H11V11H15V13H11V15H15V17H9V7M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z" />');
configureTheme();
@ -170,4 +171,4 @@ export default function AppConfig($provide,
return aliases;
}
}
}

View File

@ -322,7 +322,8 @@ export default angular.module('thingsboard.types', [])
timeseries: "timeseries",
attribute: "attribute",
function: "function",
alarm: "alarm"
alarm: "alarm",
entityField: "entityField"
},
contentType: {
"JSON": {
@ -467,6 +468,84 @@ export default angular.module('thingsboard.types', [])
list: 'entity.type-current-customer'
}
},
entityField: {
createdTime: {
keyName: 'createdTime',
name: 'entity-field.created-time',
value: 'createdTime',
time: true
},
name: {
keyName: 'name',
name: 'entity-field.name',
value: 'name'
},
type: {
keyName: 'type',
name: 'entity-field.type',
value: 'type'
},
firstName: {
keyName: 'firstName',
name: 'entity-field.first-name',
value: 'firstName'
},
lastName: {
keyName: 'lastName',
name: 'entity-field.last-name',
value: 'lastName'
},
email: {
keyName: 'email',
name: 'entity-field.email',
value: 'email'
},
title: {
keyName: 'title',
name: 'entity-field.title',
value: 'title'
},
country: {
keyName: 'country',
name: 'entity-field.country',
value: 'country'
},
state: {
keyName: 'state',
name: 'entity-field.state',
value: 'state'
},
city: {
keyName: 'city',
name: 'entity-field.city',
value: 'city'
},
address: {
keyName: 'address',
name: 'entity-field.address',
value: 'address'
},
address2: {
keyName: 'address2',
name: 'entity-field.address2',
value: 'address2'
},
zip: {
keyName: 'zip',
name: 'entity-field.zip',
value: 'zip'
},
phone: {
keyName: 'phone',
name: 'entity-field.phone',
value: 'phone'
},
label: {
keyName: 'label',
name: 'entity-field.label',
value: 'label'
}
},
entitySearchDirection: {
from: "FROM",
to: "TO"

View File

@ -16,9 +16,7 @@
-->
<md-content class="md-padding" layout="column">
<md-autocomplete ng-if="model.type === types.dataKeyType.timeseries ||
model.type === types.dataKeyType.attribute ||
model.type === types.dataKeyType.alarm"
<md-autocomplete ng-if="model.type !== types.dataKeyType.function"
style="padding-bottom: 8px;"
ng-required="true"
md-no-cache="true"
@ -87,4 +85,4 @@
prevOrigValue - {{ 'datakey.prev-orig-value-description' | translate }}
</label>
</section>
</md-content>
</md-content>

View File

@ -124,7 +124,7 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc
var alarmDataKeys = [];
for (var d in ngModelCtrl.$viewValue.dataKeys) {
var dataKey = ngModelCtrl.$viewValue.dataKeys[d];
if ((dataKey.type === types.dataKeyType.timeseries) || (dataKey.type === types.dataKeyType.attribute)) {
if ((dataKey.type === types.dataKeyType.timeseries) || (dataKey.type === types.dataKeyType.attribute) || (dataKey.type === types.dataKeyType.entityField)) {
dataKeys.push(dataKey);
} else if (dataKey.type === types.dataKeyType.alarm) {
alarmDataKeys.push(dataKey);
@ -219,7 +219,7 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc
w.triggerHandler('resize');
}
}).then(function (newDataKey) {
if ((newDataKey.type === types.dataKeyType.timeseries) || (newDataKey.type === types.dataKeyType.attribute)) {
if ((newDataKey.type === types.dataKeyType.timeseries) || (newDataKey.type === types.dataKeyType.attribute) || (newDataKey.type === types.dataKeyType.entityField)) {
let index = scope.dataKeys.indexOf(dataKey);
scope.dataKeys[index] = newDataKey;
} else if (newDataKey.type === types.dataKeyType.alarm) {
@ -246,10 +246,16 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc
items.push({ name: dataKeys[i], type: types.dataKeyType.timeseries });
}
if (scope.widgetType == types.widgetType.latest.value) {
scope.fetchEntityKeys({entityAliasId: scope.entityAlias.id, query: searchText, type: types.dataKeyType.attribute})
.then(function (dataKeys) {
var keysType = [types.dataKeyType.attribute, types.dataKeyType.entityField];
var promises = [];
keysType.forEach((type) => {
promises.push(scope.fetchEntityKeys({entityAliasId: scope.entityAlias.id, query: searchText, type: type}));
});
$q.all(promises).then(function (dataKeys) {
for (var i = 0; i < dataKeys.length; i++) {
items.push({ name: dataKeys[i], type: types.dataKeyType.attribute });
for (var j = 0; j < dataKeys[i].length; j++) {
items.push({name: dataKeys[i][j], type: keysType[i]});
}
}
deferred.resolve(items);
}, function (e) {

View File

@ -43,6 +43,10 @@
<md-tooltip>{{'datakey.attributes' | translate }}</md-tooltip>
<ng-md-icon size="16" icon="alpha-a-circle-outline"></ng-md-icon>
</span>
<span ng-show="item.type==types.dataKeyType.entityField">
<md-tooltip>{{'datakey.entityField' | translate }}</md-tooltip>
<ng-md-icon size="16" icon="alpha-e-circle-outline"></ng-md-icon>
</span>
<span ng-show="item.type==types.dataKeyType.timeseries">
<md-tooltip>{{'datakey.timeseries' | translate }}</md-tooltip>
<ng-md-icon size="16" icon="timeline"></ng-md-icon>
@ -60,6 +64,10 @@
<md-tooltip>{{'datakey.attributes' | translate }}</md-tooltip>
<ng-md-icon size="16" icon="alpha-a-circle-outline" ng-click="createKey($event, types.dataKeyType.attribute, '#datakey_chips')"></ng-md-icon>
</span>
<span ng-show="widgetType == types.widgetType.latest.value">
<md-tooltip>{{'datakey.entityField' | translate }}</md-tooltip>
<ng-md-icon size="16" icon="alpha-e-circle-outline" ng-click="createKey($event, types.dataKeyType.entityField, '#datakey_chips')"></ng-md-icon>
</span>
<span>
<md-tooltip>{{'datakey.timeseries' | translate }}</md-tooltip>
<ng-md-icon size="16" icon="timeline" ng-click="createKey($event, types.dataKeyType.timeseries, '#datakey_chips')"></ng-md-icon>
@ -81,6 +89,10 @@
<span ng-show="$chip.type==types.dataKeyType.attribute">
<md-tooltip>{{'datakey.attributes' | translate }}</md-tooltip>
<ng-md-icon size="20" icon="alpha-a-circle-outline"></ng-md-icon>
</span>
<span ng-show="$chip.type==types.dataKeyType.entityField">
<md-tooltip>{{'datakey.entityField' | translate }}</md-tooltip>
<ng-md-icon size="20" icon="alpha-e-circle-outline"></ng-md-icon>
</span>
<span ng-show="$chip.type==types.dataKeyType.timeseries">
<md-tooltip>{{'datakey.timeseries' | translate }}</md-tooltip>

View File

@ -46,7 +46,9 @@ function Legend($compile, $templateCache, types) {
scope.isRowDirection = scope.legendConfig.direction === types.direction.row.value;
scope.toggleHideData = function(index) {
scope.legendData.keys[index].dataKey.hidden = !scope.legendData.keys[index].dataKey.hidden;
if (!scope.legendData.keys[index].dataKey.settings.disableDataHiding) {
scope.legendData.keys[index].dataKey.hidden = !scope.legendData.keys[index].dataKey.hidden;
}
}
$compile(element.contents())(scope);

View File

@ -27,7 +27,8 @@
</tr>
</thead>
<tbody>
<tr class="tb-legend-keys" ng-repeat="legendKey in legendData.keys" ng-if="!isRowDirection">
<tr class="tb-legend-keys" ng-repeat="legendKey in legendData.keys"
ng-if="!isRowDirection && legendData.keys[legendKey.dataIndex].dataKey.inLegend">
<td><span class="tb-legend-line" ng-style="{backgroundColor: legendKey.dataKey.color}"></span></td>
<td class="tb-legend-label"
ng-click="toggleHideData(legendKey.dataIndex)"
@ -41,7 +42,7 @@
</tr>
<tr class="tb-legend-keys" ng-class="{ 'tb-row-direction': !displayHeader() }" ng-if="isRowDirection">
<td></td>
<td ng-repeat="legendKey in legendData.keys">
<td ng-repeat="legendKey in legendData.keys" ng-if="legendData.keys[legendKey.dataIndex].dataKey.inLegend">
<span class="tb-legend-line" ng-style="{backgroundColor: legendKey.dataKey.color}"></span>
<span class="tb-legend-label"
ng-click="toggleHideData(legendKey.dataIndex)"
@ -52,19 +53,31 @@
</tr>
<tr class="tb-legend-keys" ng-if="isRowDirection && legendConfig.showMin === true">
<td class="tb-legend-type">{{ 'legend.min' | translate }}</td>
<td class="tb-legend-value" ng-repeat="legendKey in legendData.keys">{{ legendData.data[legendKey.dataIndex].min }}</td>
<td class="tb-legend-value" ng-repeat="legendKey in legendData.keys"
ng-if="legendData.keys[legendKey.dataIndex].dataKey.inLegend">
{{ legendData.data[legendKey.dataIndex].min }}
</td>
</tr>
<tr class="tb-legend-keys" ng-if="isRowDirection && legendConfig.showMax === true">
<td class="tb-legend-type">{{ 'legend.max' | translate }}</td>
<td class="tb-legend-value" ng-repeat="legendKey in legendData.keys">{{ legendData.data[legendKey.dataIndex].max }}</td>
<td class="tb-legend-value" ng-repeat="legendKey in legendData.keys"
ng-if="legendData.keys[legendKey.dataIndex].dataKey.inLegend">
{{ legendData.data[legendKey.dataIndex].max }}
</td>
</tr>
<tr class="tb-legend-keys" ng-if="isRowDirection && legendConfig.showAvg === true">
<td class="tb-legend-type">{{ 'legend.avg' | translate }}</td>
<td class="tb-legend-value" ng-repeat="legendKey in legendData.keys">{{ legendData.data[legendKey.dataIndex].avg }}</td>
<td class="tb-legend-value" ng-repeat="legendKey in legendData.keys"
ng-if="legendData.keys[legendKey.dataIndex].dataKey.inLegend">
{{ legendData.data[legendKey.dataIndex].avg }}
</td>
</tr>
<tr class="tb-legend-keys" ng-if="isRowDirection && legendConfig.showTotal === true">
<td class="tb-legend-type">{{ 'legend.total' | translate }}</td>
<td class="tb-legend-value" ng-repeat="legendKey in legendData.keys">{{ legendData.data[legendKey.dataIndex].total }}</td>
<td class="tb-legend-value" ng-repeat="legendKey in legendData.keys"
ng-if="legendData.keys[legendKey.dataIndex].dataKey.inLegend">
{{ legendData.data[legendKey.dataIndex].total }}
</td>
</tr>
</tbody>
</table>

View File

@ -15,7 +15,7 @@
limitations under the License.
-->
<md-button ng-disabled="disabled" class="md-raised md-primary" ng-click="openEditMode($event)">
<md-button ng-disabled="timewindowDisabled" class="md-raised md-primary" ng-click="openEditMode($event)">
<ng-md-icon icon="query_builder"></ng-md-icon>
<span>{{model.displayValue}}</span>
</md-button>

View File

@ -97,7 +97,7 @@ function Timewindow($compile, $templateCache, $filter, $mdPanel, $document, $mdM
element.html(template);
scope.openEditMode = function (event) {
if (scope.disabled) {
if (scope.timewindowDisabled) {
return;
}
var position;
@ -212,6 +212,10 @@ function Timewindow($compile, $templateCache, $filter, $mdPanel, $document, $mdM
}
}
scope.isTimewindowDisabled = function () {
return scope.disabled || (!scope.isEdit && scope.model.hideInterval && scope.model.hideAggregation && scope.model.hideAggInterval);
}
ngModelCtrl.$render = function () {
scope.model = timeService.defaultTimewindow();
if (ngModelCtrl.$viewValue) {
@ -243,6 +247,7 @@ function Timewindow($compile, $templateCache, $filter, $mdPanel, $document, $mdM
model.hideAggregation = value.hideAggregation;
model.hideAggInterval = value.hideAggInterval;
}
scope.timewindowDisabled = scope.isTimewindowDisabled();
scope.updateDisplayValue();
};

View File

@ -16,7 +16,7 @@
-->
<section class="tb-timewindow" layout='row' layout-align="start center">
<md-button ng-if="direction === 'left'" ng-disabled="disabled" class="md-icon-button tb-md-32" aria-label="{{ 'timewindow.edit' | translate }}" ng-click="openEditMode($event)">
<md-button ng-if="direction === 'left'" ng-disabled="timewindowDisabled" class="md-icon-button tb-md-32" aria-label="{{ 'timewindow.edit' | translate }}" ng-click="openEditMode($event)">
<md-tooltip md-direction="{{tooltipDirection}}">
{{ 'timewindow.edit' | translate }}
</md-tooltip>
@ -28,7 +28,7 @@
</md-tooltip>
{{model.displayValue}}
</span>
<md-button ng-if="direction === 'right'" ng-disabled="disabled" class="md-icon-button tb-md-32" aria-label="{{ 'timewindow.edit' | translate }}" ng-click="openEditMode($event)">
<md-button ng-if="direction === 'right'" ng-disabled="timewindowDisabled" class="md-icon-button tb-md-32" aria-label="{{ 'timewindow.edit' | translate }}" ng-click="openEditMode($event)">
<md-tooltip md-direction="{{tooltipDirection}}">
{{ 'timewindow.edit' | translate }}
</md-tooltip>

View File

@ -423,10 +423,10 @@ function WidgetConfig($compile, $templateCache, $rootScope, $translate, $timeout
}
var label = chip;
if (type === types.dataKeyType.alarm) {
var alarmField = types.alarmFields[chip];
if (alarmField) {
label = $translate.instant(alarmField.name)+'';
if (type === types.dataKeyType.alarm || type === types.dataKeyType.entityField) {
var keyField = type === types.dataKeyType.alarm ? types.alarmFields[chip] : types.entityField[chip];
if (keyField) {
label = $translate.instant(keyField.name)+'';
}
}
label = scope.genNextLabel(label);

View File

@ -1102,6 +1102,22 @@
"copyId": "Αντιγραφή ID ομάδας οντοτήτων",
"idCopiedMessage": "Το ID της ομάδας οντοτήτων έχει αντιγραφεί στο πρόχειρο"
},
"entity-field": {
"created-time": "Δημιουργήθηκε",
"name": "Όνομα",
"type": "Τύπος",
"first-name": "Όνομα",
"last-name": "Επίθετο",
"email": "Email",
"title": "Τίτλος",
"country": "Χώρα",
"state": "Νομός",
"city": "Πόλη",
"address": "Διεύθυνση",
"address2": "Διεύθυνση 2",
"zip": "Τ.Κ.",
"phone": "Τηλέφωνο"
},
"entity-view": {
"entity-view": "Όψη Οντότητας",
"entity-view-required": "Απαιτείται προβολή οντότητας.",
@ -2603,4 +2619,4 @@
"el_GR": "Ελληνικά"
}
}
}
}

View File

@ -815,6 +815,23 @@
"no-data": "No data to display",
"columns-to-display": "Columns to Display"
},
"entity-field": {
"created-time": "Created time",
"name": "Name",
"type": "Type",
"first-name": "First name",
"last-name": "Last name",
"email": "Email",
"title": "Title",
"country": "Country",
"state": "State",
"city": "City",
"address": "Address",
"address2": "Address 2",
"zip": "Zip",
"phone": "Phone",
"label": "Label"
},
"entity-view": {
"entity-view": "Entity View",
"entity-view-required": "Entity view is required.",

View File

@ -808,6 +808,22 @@
"no-data": "No hay datos para mostrar",
"columns-to-display": "Columnas a mostrar"
},
"entity-field": {
"created-time": "Tiempo de creación",
"name": "Nombre",
"type": "Tipo",
"first-name": "Nombre",
"last-name": "Apellido",
"email": "Correo electrónico",
"title": "Título",
"country": "País",
"state": "Estado",
"city": "Ciudad",
"address": "Dirección",
"address2": "Dirección 2",
"zip": "Código postal",
"phone": "Teléfono"
},
"entity-view": {
"entity-view": "Vista de entidad",
"entity-view-required": "Vista de entidad es requerido.",

View File

@ -809,6 +809,22 @@
"use-entity-name-filter": "Utiliser un filtre",
"user-name-starts-with": "Utilisateurs dont les noms commencent par '{{prefix}}'"
},
"entity-field": {
"address": "Adresse",
"address2": "Adresse 2",
"city": "Ville",
"country": "Pays",
"created-time": "Heure de création",
"email": "Email",
"first-name": "Prénom",
"last-name": "Nom de famille",
"name": "Nom",
"phone": "Téléphone",
"state": "Prov",
"title": "Titre",
"type": "Type",
"zip": "Code postal"
},
"entity-view": {
"add": "Ajouter une vue d'entité",
"add-alias": "Ajouter un alias de vue d'entité",

View File

@ -661,7 +661,7 @@
"delete-device-title": "Вы точно хотите удалить устройство '{{deviceName}}'?",
"delete-device-text": "Внимание, после подтверждения устройство и все связанные с ним данные будут безвозвратно утеряны.",
"delete-devices-title": "Вы точно хотите удалить { count, plural, one {1 устройство} few {# устройства} other {# устройств} }?",
"delete-devices-action-title": "Удалить { count, plural, one {1 устройство} few {# устройства} other {# устройств} } }",
"delete-devices-action-title": "Удалить { count, plural, one {1 устройство} few {# устройства} other {# устройств} }",
"delete-devices-text": "Внимание, после подтверждения выбранные устройства и все связанные с ними данные будут безвозвратно утеряны..",
"unassign-device-title": "Вы точно хотите отозвать устройство '{{deviceName}}'?",
"unassign-device-text": "После подтверждения устройство будет недоступно клиенту.",
@ -814,6 +814,23 @@
"no-data": "Нет данных для отображения",
"columns-to-display": "Отобразить следующие колонки"
},
"entity-field": {
"created-time": "Время создания",
"name": "Название",
"type": "Тип",
"first-name": "Имя",
"last-name": "Фамилия",
"email": "Электронная почта",
"title": "Название",
"country": "Страна",
"state": "Штат/Область",
"city": "Город",
"address": "Адрес",
"address2": "Адрес 2",
"zip": "Индекс",
"phone": "Телефон",
"label": "Метка"
},
"entity-view": {
"entity-view": "Представление Объекта",
"entity-view-required": "Представление объекта обязательно.",

View File

@ -956,6 +956,23 @@
"list-of-integrations": "{ count, plural, 1 {Одна інтеграція} other {Список # інтеграцій} }",
"integration-name-starts-with": "Інтеграції, імена яких починаються з '{{prefix}}'"
},
"entity-field": {
"created-time": "Час створення",
"name": "Ім'я",
"type": "Тип",
"first-name": "Ім'я",
"last-name": "Прізвище",
"email": "Електронна пошта",
"title": "Назва",
"country": "Країна",
"state": "Штат",
"city": "Місто",
"address": "Адреса",
"address2": "Адреса 2",
"zip": "Zip",
"phone": "Телефон",
"label": "Мітка"
},
"entity-group": {
"entity-group": "Група сутності",
"details": "Деталі",

View File

@ -139,12 +139,7 @@ export default class TbFlot {
return seriesHover.index === seriesIndex;
});
if (found && found.length) {
let timestamp;
if (!angular.isNumber(hoverInfo[0].time) || (found[0].time < hoverInfo[0].time)) {
timestamp = parseInt(hoverInfo[1].time);
} else {
timestamp = parseInt(hoverInfo[0].time);
}
let timestamp = parseInt(found[0].time);
let date = moment(timestamp).format('YYYY-MM-DD HH:mm:ss');
let dateDiv = $('<div>' + date + '</div>');
dateDiv.css({
@ -1213,7 +1208,35 @@ export default class TbFlot {
}
static get pieDatakeySettingsSchema() {
return {}
return {
"schema": {
"type": "object",
"title": "DataKeySettings",
"properties": {
"hideDataByDefault": {
"title": "Data is hidden by default",
"type": "boolean",
"default": false
},
"disableDataHiding": {
"title": "Disable data hiding",
"type": "boolean",
"default": false
},
"removeFromLegend": {
"title": "Remove datakey from legend",
"type": "boolean",
"default": false
}
},
"required": []
},
"form": [
"hideDataByDefault",
"disableDataHiding",
"removeFromLegend"
]
};
}
static datakeySettingsSchema(defaultShowLines, chartType) {
@ -1228,6 +1251,21 @@ export default class TbFlot {
"type": "boolean",
"default": false
},
"hideDataByDefault": {
"title": "Data is hidden by default",
"type": "boolean",
"default": false
},
"disableDataHiding": {
"title": "Disable data hiding",
"type": "boolean",
"default": false
},
"removeFromLegend": {
"title": "Remove datakey from legend",
"type": "boolean",
"default": false
},
"showLines": {
"title": "Show lines",
"type": "boolean",
@ -1316,6 +1354,9 @@ export default class TbFlot {
"required": ["showLines", "fillLines", "showPoints"]
},
"form": [
"hideDataByDefault",
"disableDataHiding",
"removeFromLegend",
"excludeFromStacking",
"showLines",
"fillLines",

View File

@ -26,12 +26,9 @@ $minmax-height: percentage(.04) !default;
$minmax-container-margin-pct: percentage(.18) !default;
$minmax-container-margin-bottom-pct: percentage(.12) !default;
$background-color: #e6e7e8 !default;
.tb-knob {
width: 100%;
height: 100%;
background: $background-color;
.knob {
position: relative;

View File

@ -17,12 +17,9 @@
$error-height: 14px !default;
$background-color: #e6e7e8 !default;
.tb-led-indicator {
width: 100%;
height: 100%;
background: $background-color;
.title-container {
.led-title {

View File

@ -17,12 +17,9 @@
$error-height: 14px !default;
$background-color: #e6e7e8 !default;
.tb-round-switch {
width: 100%;
height: 100%;
background: $background-color;
.title-container {
.switch-title {

View File

@ -18,14 +18,11 @@ $thumb-checked-img: url("./svg/thumb-checked.svg") !default;
$thumb-bar-img: url("./svg/thumb-bar.svg") !default;
$thumb-bar-checked-img: url("./svg/thumb-bar-checked.svg") !default;
$background-color: #e6e7e8 !default;
$error-height: 14px !default;
.tb-switch {
width: 100%;
height: 100%;
background: $background-color;
.error-container {
position: absolute;