thingsboard-memoizeit
Changes
application/pom.xml 2(+1 -1)
application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java 2(+1 -1)
application/src/main/java/org/thingsboard/server/actors/shared/rulechain/TenantRuleChainManager.java 4(+1 -3)
application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java 7(+7 -0)
application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java 8(+7 -1)
application/src/main/java/org/thingsboard/server/service/install/DefaultDataUpdateService.java 25(+11 -14)
application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java 15(+9 -6)
application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java 171(+139 -32)
application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java 2(+1 -1)
application/src/main/proto/cluster.proto 12(+12 -0)
common/data/pom.xml 2(+1 -1)
common/message/pom.xml 2(+1 -1)
common/pom.xml 2(+1 -1)
common/transport/pom.xml 2(+1 -1)
common/transport/src/main/java/org/thingsboard/server/common/transport/SessionMsgProcessor.java 4(+3 -1)
dao/pom.xml 2(+1 -1)
docker/cassandra/Makefile 2(+1 -1)
docker/cassandra-setup/Makefile 2(+1 -1)
docker/docker-compose.yml 2(+1 -1)
docker/k8s/cassandra.yaml 2(+1 -1)
docker/k8s/cassandra-setup.yaml 2(+1 -1)
docker/k8s/tb.yaml 2(+1 -1)
docker/k8s/zookeeper.yaml 2(+1 -1)
docker/tb/Makefile 2(+1 -1)
docker/zookeeper/Makefile 2(+1 -1)
netty-mqtt/pom.xml 4(+2 -2)
pom.xml 2(+1 -1)
rule-engine/pom.xml 2(+1 -1)
rule-engine/rule-engine-api/pom.xml 2(+1 -1)
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java 3(+2 -1)
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNodeConfiguration.java 2(+2 -0)
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNode.java 14(+13 -1)
rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js 6(+3 -3)
tools/pom.xml 2(+1 -1)
transport/coap/pom.xml 2(+1 -1)
transport/http/pom.xml 2(+1 -1)
transport/mqtt/pom.xml 2(+1 -1)
transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java 2(+2 -0)
transport/pom.xml 2(+1 -1)
ui/package.json 2(+1 -1)
ui/pom.xml 2(+1 -1)
ui/src/app/locale/locale.constant.js 38(+19 -19)
ui/src/app/locale/locale.constant-es.js 1957(+1237 -720)
ui/src/app/locale/locale.constant-ko.js 773(+672 -101)
ui/src/app/locale/locale.constant-ru.js 760(+662 -98)
ui/src/app/locale/locale.constant-zh.js 370(+269 -101)
Details
application/pom.xml 2(+1 -1)
diff --git a/application/pom.xml b/application/pom.xml
index 3f2ffa3..45b35cf 100644
--- a/application/pom.xml
+++ b/application/pom.xml
@@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
- <version>2.0.1</version>
+ <version>2.0.2</version>
<artifactId>thingsboard</artifactId>
</parent>
<artifactId>application</artifactId>
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
index 85b3285..9527171 100644
--- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
@@ -294,7 +294,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
private void handleGetAttributesRequest(DeviceToDeviceActorMsg src) {
GetAttributesRequest request = (GetAttributesRequest) src.getPayload();
ListenableFuture<List<AttributeKvEntry>> clientAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.CLIENT_SCOPE, request.getClientAttributeNames());
- ListenableFuture<List<AttributeKvEntry>> sharedAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.SHARED_SCOPE, request.getClientAttributeNames());
+ ListenableFuture<List<AttributeKvEntry>> sharedAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.SHARED_SCOPE, request.getSharedAttributeNames());
Futures.addCallback(Futures.allAsList(Arrays.asList(clientAttributesFuture, sharedAttributesFuture)), new FutureCallback<List<List<AttributeKvEntry>>>() {
@Override
diff --git a/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java b/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java
index 3507f24..fac5d97 100644
--- a/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java
+++ b/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java
@@ -32,6 +32,7 @@ import org.thingsboard.server.actors.rpc.RpcManagerActor;
import org.thingsboard.server.actors.rpc.RpcSessionCreateRequestMsg;
import org.thingsboard.server.actors.session.SessionManagerActor;
import org.thingsboard.server.actors.stats.StatsActor;
+import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
@@ -48,6 +49,7 @@ import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
import org.thingsboard.server.service.cluster.discovery.DiscoveryService;
import org.thingsboard.server.service.cluster.discovery.ServerInstance;
import org.thingsboard.server.service.cluster.rpc.ClusterRpcService;
+import org.thingsboard.server.service.state.DeviceStateService;
import scala.concurrent.Await;
import scala.concurrent.Future;
import scala.concurrent.duration.Duration;
@@ -81,6 +83,9 @@ public class DefaultActorService implements ActorService {
@Autowired
private DiscoveryService discoveryService;
+ @Autowired
+ private DeviceStateService deviceStateService;
+
private ActorSystem system;
private ActorRef appActor;
@@ -199,7 +204,7 @@ public class DefaultActorService implements ActorService {
public void onReceivedMsg(ServerAddress source, ClusterAPIProtos.ClusterMessage msg) {
ServerAddress serverAddress = new ServerAddress(source.getHost(), source.getPort());
log.info("Received msg [{}] from [{}]", msg.getMessageType().name(), serverAddress);
- if(log.isDebugEnabled()){
+ if (log.isDebugEnabled()) {
log.info("MSG: ", msg);
}
switch (msg.getMessageType()) {
@@ -236,6 +241,9 @@ public class DefaultActorService implements ActorService {
case CLUSTER_RPC_FROM_DEVICE_RESPONSE_MESSAGE:
actorContext.getDeviceRpcService().processRemoteResponseFromDevice(serverAddress, msg.getPayload().toByteArray());
break;
+ case CLUSTER_DEVICE_STATE_SERVICE_MESSAGE:
+ actorContext.getDeviceStateService().onRemoteMsg(serverAddress, msg.getPayload().toByteArray());
+ break;
}
}
@@ -254,4 +262,8 @@ public class DefaultActorService implements ActorService {
rpcManagerActor.tell(msg, ActorRef.noSender());
}
+ @Override
+ public void onDeviceAdded(Device device) {
+ deviceStateService.onDeviceAdded(device);
+ }
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/TenantRuleChainManager.java b/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/TenantRuleChainManager.java
index 731d8d8..1e60751 100644
--- a/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/TenantRuleChainManager.java
+++ b/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/TenantRuleChainManager.java
@@ -33,9 +33,7 @@ public class TenantRuleChainManager extends RuleChainManager {
@Override
public void init(ActorContext context) {
- if (systemContext.isTenantComponentsInitEnabled()) {
- super.init(context);
- }
+ super.init(context);
}
@Override
diff --git a/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java b/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java
index 6002b0e..c3ffbab 100644
--- a/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java
+++ b/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java
@@ -36,6 +36,7 @@ import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
+import org.thingsboard.server.service.state.DeviceStateService;
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
import org.thingsboard.server.utils.MiscUtils;
@@ -74,6 +75,10 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi
@Lazy
private TelemetrySubscriptionService tsSubService;
+ @Autowired
+ @Lazy
+ private DeviceStateService deviceStateService;
+
private final List<DiscoveryServiceListener> listeners = new CopyOnWriteArrayList<>();
private CuratorFramework client;
@@ -203,6 +208,7 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi
switch (pathChildrenCacheEvent.getType()) {
case CHILD_ADDED:
tsSubService.onClusterUpdate();
+ deviceStateService.onClusterUpdate();
listeners.forEach(listener -> listener.onServerAdded(instance));
break;
case CHILD_UPDATED:
@@ -210,6 +216,7 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi
break;
case CHILD_REMOVED:
tsSubService.onClusterUpdate();
+ deviceStateService.onClusterUpdate();
listeners.forEach(listener -> listener.onServerRemoved(instance));
break;
default:
diff --git a/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java
index 4d2adea..f4ff92c 100644
--- a/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java
+++ b/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java
@@ -211,7 +211,13 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService {
private void loadCql(Path cql) throws Exception {
List<String> statements = new CQLStatementsParser(cql).getStatements();
- statements.forEach(statement -> installCluster.getSession().execute(statement));
+ statements.forEach(statement -> {
+ installCluster.getSession().execute(statement);
+ try {
+ Thread.sleep(2500);
+ } catch (InterruptedException e) {}
+ });
+ Thread.sleep(5000);
}
}
diff --git a/application/src/main/java/org/thingsboard/server/service/install/DefaultDataUpdateService.java b/application/src/main/java/org/thingsboard/server/service/install/DefaultDataUpdateService.java
index b372368..5daebcc 100644
--- a/application/src/main/java/org/thingsboard/server/service/install/DefaultDataUpdateService.java
+++ b/application/src/main/java/org/thingsboard/server/service/install/DefaultDataUpdateService.java
@@ -19,16 +19,15 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
+import org.thingsboard.server.common.data.SearchTextBased;
import org.thingsboard.server.common.data.Tenant;
-import org.thingsboard.server.common.data.id.IdBased;
+import org.thingsboard.server.common.data.id.UUIDBased;
+import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.tenant.TenantService;
-import java.util.List;
-import java.util.UUID;
-
@Service
@Profile("install")
@Slf4j
@@ -59,8 +58,8 @@ public class DefaultDataUpdateService implements DataUpdateService {
new PaginatedUpdater<String, Tenant>() {
@Override
- protected List<Tenant> findEntities(String region, TextPageLink pageLink) {
- return tenantService.findTenants(pageLink).getData();
+ protected TextPageData<Tenant> findEntities(String region, TextPageLink pageLink) {
+ return tenantService.findTenants(pageLink);
}
@Override
@@ -76,7 +75,7 @@ public class DefaultDataUpdateService implements DataUpdateService {
}
};
- public abstract class PaginatedUpdater<I, D extends IdBased<?>> {
+ public abstract class PaginatedUpdater<I, D extends SearchTextBased<? extends UUIDBased>> {
private static final int DEFAULT_LIMIT = 100;
@@ -84,20 +83,18 @@ public class DefaultDataUpdateService implements DataUpdateService {
TextPageLink pageLink = new TextPageLink(DEFAULT_LIMIT);
boolean hasNext = true;
while (hasNext) {
- List<D> entities = findEntities(id, pageLink);
- for (D entity : entities) {
+ TextPageData<D> entities = findEntities(id, pageLink);
+ for (D entity : entities.getData()) {
updateEntity(entity);
}
- hasNext = entities.size() == pageLink.getLimit();
+ hasNext = entities.hasNext();
if (hasNext) {
- int index = entities.size() - 1;
- UUID idOffset = entities.get(index).getUuidId();
- pageLink.setIdOffset(idOffset);
+ pageLink = entities.getNextPageLink();
}
}
}
- protected abstract List<D> findEntities(I id, TextPageLink pageLink);
+ protected abstract TextPageData<D> findEntities(I id, TextPageLink pageLink);
protected abstract void updateEntity(D entity);
diff --git a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java
index 29d5c65..3a4a837 100644
--- a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java
+++ b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java
@@ -70,8 +70,7 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService {
log.info("Updating schema ...");
Path schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "1.3.1", SCHEMA_UPDATE_SQL);
try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
- String sql = new String(Files.readAllBytes(schemaUpdateFile), Charset.forName("UTF-8"));
- conn.createStatement().execute(sql); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script
+ loadSql(schemaUpdateFile, conn);
}
log.info("Schema updated.");
break;
@@ -87,8 +86,7 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService {
log.info("Updating schema ...");
schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "1.4.0", SCHEMA_UPDATE_SQL);
- String sql = new String(Files.readAllBytes(schemaUpdateFile), Charset.forName("UTF-8"));
- conn.createStatement().execute(sql); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script
+ loadSql(schemaUpdateFile, conn);
log.info("Schema updated.");
log.info("Restoring dashboards ...");
@@ -105,8 +103,7 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService {
try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
log.info("Updating schema ...");
schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.0.0", SCHEMA_UPDATE_SQL);
- String sql = new String(Files.readAllBytes(schemaUpdateFile), Charset.forName("UTF-8"));
- conn.createStatement().execute(sql); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script
+ loadSql(schemaUpdateFile, conn);
log.info("Schema updated.");
}
break;
@@ -114,4 +111,10 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService {
throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion);
}
}
+
+ private void loadSql(Path sqlFile, Connection conn) throws Exception {
+ String sql = new String(Files.readAllBytes(sqlFile), Charset.forName("UTF-8"));
+ conn.createStatement().execute(sql); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script
+ Thread.sleep(5000);
+ }
}
diff --git a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java
index 45b9696..a41fdee 100644
--- a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java
+++ b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java
@@ -23,11 +23,13 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningScheduledExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
+import com.google.protobuf.InvalidProtocolBufferException;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
+import org.thingsboard.rule.engine.api.RpcError;
import org.thingsboard.server.actors.service.ActorService;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device;
@@ -36,13 +38,19 @@ import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.page.TextPageLink;
+import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.common.msg.TbMsgMetaData;
+import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.tenant.TenantService;
+import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
+import org.thingsboard.server.service.cluster.routing.ClusterRoutingService;
+import org.thingsboard.server.service.cluster.rpc.ClusterRpcService;
+import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
import javax.annotation.Nullable;
@@ -54,6 +62,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
+import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
@@ -98,6 +107,12 @@ public class DefaultDeviceStateService implements DeviceStateService {
@Autowired
private TelemetrySubscriptionService tsSubService;
+ @Autowired
+ private ClusterRoutingService routingService;
+
+ @Autowired
+ private ClusterRpcService clusterRpcService;
+
@Value("${state.defaultInactivityTimeoutInSec}")
@Getter
private long defaultInactivityTimeoutInSec;
@@ -172,12 +187,57 @@ public class DefaultDeviceStateService implements DeviceStateService {
}
@Override
- public Optional<DeviceState> getDeviceState(DeviceId deviceId) {
- DeviceStateData state = deviceStates.get(deviceId);
- if (state != null) {
- return Optional.of(state.getState());
+ public void onClusterUpdate() {
+ queueExecutor.submit(this::onClusterUpdateSync);
+ }
+
+ @Override
+ public void onRemoteMsg(ServerAddress serverAddress, byte[] data) {
+ ClusterAPIProtos.DeviceStateServiceMsgProto proto;
+ try {
+ proto = ClusterAPIProtos.DeviceStateServiceMsgProto.parseFrom(data);
+ } catch (InvalidProtocolBufferException e) {
+ throw new RuntimeException(e);
+ }
+ TenantId tenantId = new TenantId(new UUID(proto.getTenantIdMSB(), proto.getTenantIdLSB()));
+ DeviceId deviceId = new DeviceId(new UUID(proto.getDeviceIdMSB(), proto.getDeviceIdLSB()));
+ if (proto.getDeleted()) {
+ queueExecutor.submit(() -> onDeviceDeleted(tenantId, deviceId));
} else {
- return Optional.empty();
+ Device device = deviceService.findDeviceById(deviceId);
+ if (device != null) {
+ if (proto.getAdded()) {
+ onDeviceAdded(device);
+ } else if (proto.getUpdated()) {
+ onDeviceUpdated(device);
+ }
+ }
+ }
+ }
+
+ private void onClusterUpdateSync() {
+ List<Tenant> tenants = tenantService.findTenants(new TextPageLink(Integer.MAX_VALUE)).getData();
+ for (Tenant tenant : tenants) {
+ List<ListenableFuture<DeviceStateData>> fetchFutures = new ArrayList<>();
+ List<Device> devices = deviceService.findDevicesByTenantId(tenant.getId(), new TextPageLink(Integer.MAX_VALUE)).getData();
+ for (Device device : devices) {
+ if (!routingService.resolveById(device.getId()).isPresent()) {
+ if (!deviceStates.containsKey(device.getId())) {
+ fetchFutures.add(fetchDeviceState(device));
+ }
+ } else {
+ Set<DeviceId> tenantDeviceSet = tenantDevices.get(tenant.getId());
+ if (tenantDeviceSet != null) {
+ tenantDeviceSet.remove(device.getId());
+ }
+ deviceStates.remove(device.getId());
+ }
+ }
+ try {
+ Futures.successfulAsList(fetchFutures).get().forEach(this::addDeviceUsingState);
+ } catch (InterruptedException | ExecutionException e) {
+ log.warn("Failed to init device state service from DB", e);
+ }
}
}
@@ -187,7 +247,9 @@ public class DefaultDeviceStateService implements DeviceStateService {
List<ListenableFuture<DeviceStateData>> fetchFutures = new ArrayList<>();
List<Device> devices = deviceService.findDevicesByTenantId(tenant.getId(), new TextPageLink(Integer.MAX_VALUE)).getData();
for (Device device : devices) {
- fetchFutures.add(fetchDeviceState(device));
+ if (!routingService.resolveById(device.getId()).isPresent()) {
+ fetchFutures.add(fetchDeviceState(device));
+ }
}
try {
Futures.successfulAsList(fetchFutures).get().forEach(this::addDeviceUsingState);
@@ -209,7 +271,7 @@ public class DefaultDeviceStateService implements DeviceStateService {
DeviceStateData stateData = deviceStates.get(deviceId);
DeviceState state = stateData.getState();
state.setActive(ts < state.getLastActivityTime() + state.getInactivityTimeout());
- if (!state.isActive() && state.getLastInactivityAlarmTime() < state.getLastActivityTime()) {
+ if (!state.isActive() && (state.getLastInactivityAlarmTime() == 0L || state.getLastInactivityAlarmTime() < state.getLastActivityTime())) {
state.setLastInactivityAlarmTime(ts);
pushRuleEngineMessage(stateData, INACTIVITY_EVENT);
saveAttribute(deviceId, INACTIVITY_ALARM_TIME, ts);
@@ -219,7 +281,7 @@ public class DefaultDeviceStateService implements DeviceStateService {
}
private void onDeviceConnectSync(DeviceId deviceId) {
- DeviceStateData stateData = deviceStates.get(deviceId);
+ DeviceStateData stateData = getOrFetchDeviceStateData(deviceId);
if (stateData != null) {
long ts = System.currentTimeMillis();
stateData.getState().setLastConnectTime(ts);
@@ -229,7 +291,7 @@ public class DefaultDeviceStateService implements DeviceStateService {
}
private void onDeviceDisconnectSync(DeviceId deviceId) {
- DeviceStateData stateData = deviceStates.get(deviceId);
+ DeviceStateData stateData = getOrFetchDeviceStateData(deviceId);
if (stateData != null) {
long ts = System.currentTimeMillis();
stateData.getState().setLastDisconnectTime(ts);
@@ -239,7 +301,7 @@ public class DefaultDeviceStateService implements DeviceStateService {
}
private void onDeviceActivitySync(DeviceId deviceId) {
- DeviceStateData stateData = deviceStates.get(deviceId);
+ DeviceStateData stateData = getOrFetchDeviceStateData(deviceId);
if (stateData != null) {
DeviceState state = stateData.getState();
long ts = System.currentTimeMillis();
@@ -251,6 +313,23 @@ public class DefaultDeviceStateService implements DeviceStateService {
}
}
+ private DeviceStateData getOrFetchDeviceStateData(DeviceId deviceId) {
+ DeviceStateData deviceStateData = deviceStates.get(deviceId);
+ if (deviceStateData == null) {
+ if (!routingService.resolveById(deviceId).isPresent()) {
+ Device device = deviceService.findDeviceById(deviceId);
+ if (device != null) {
+ try {
+ deviceStateData = fetchDeviceState(device).get();
+ } catch (InterruptedException | ExecutionException e) {
+ log.debug("[{}] Failed to fetch device state!", deviceId, e);
+ }
+ }
+ }
+ }
+ return deviceStateData;
+ }
+
private void onInactivityTimeoutUpdate(DeviceId deviceId, long inactivityTimeout) {
if (inactivityTimeout == 0L) {
return;
@@ -269,37 +348,65 @@ public class DefaultDeviceStateService implements DeviceStateService {
}
private void onDeviceAddedSync(Device device) {
- Futures.addCallback(fetchDeviceState(device), new FutureCallback<DeviceStateData>() {
- @Override
- public void onSuccess(@Nullable DeviceStateData state) {
- addDeviceUsingState(state);
- }
+ Optional<ServerAddress> address = routingService.resolveById(device.getId());
+ if (!address.isPresent()) {
+ Futures.addCallback(fetchDeviceState(device), new FutureCallback<DeviceStateData>() {
+ @Override
+ public void onSuccess(@Nullable DeviceStateData state) {
+ addDeviceUsingState(state);
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ log.warn("Failed to register device to the state service", t);
+ }
+ });
+ } else {
+ sendDeviceEvent(device.getTenantId(), device.getId(), address.get(), true, false, false);
+ }
+ }
- @Override
- public void onFailure(Throwable t) {
- log.warn("Failed to register device to the state service", t);
- }
- });
+ private void sendDeviceEvent(TenantId tenantId, DeviceId deviceId, ServerAddress address, boolean added, boolean updated, boolean deleted) {
+ log.trace("[{}][{}] Device is monitored on other server: {}", tenantId, deviceId, address);
+ ClusterAPIProtos.DeviceStateServiceMsgProto.Builder builder = ClusterAPIProtos.DeviceStateServiceMsgProto.newBuilder();
+ builder.setTenantIdMSB(tenantId.getId().getMostSignificantBits());
+ builder.setTenantIdLSB(tenantId.getId().getLeastSignificantBits());
+ builder.setDeviceIdMSB(deviceId.getId().getMostSignificantBits());
+ builder.setDeviceIdLSB(deviceId.getId().getLeastSignificantBits());
+ builder.setAdded(added);
+ builder.setUpdated(updated);
+ builder.setDeleted(deleted);
+ clusterRpcService.tell(address, ClusterAPIProtos.MessageType.CLUSTER_DEVICE_STATE_SERVICE_MESSAGE, builder.build().toByteArray());
}
private void onDeviceUpdatedSync(Device device) {
- DeviceStateData stateData = deviceStates.get(device.getId());
- if (stateData != null) {
- TbMsgMetaData md = new TbMsgMetaData();
- md.putValue("deviceName", device.getName());
- md.putValue("deviceType", device.getType());
- stateData.setMetaData(md);
+ Optional<ServerAddress> address = routingService.resolveById(device.getId());
+ if (!address.isPresent()) {
+ DeviceStateData stateData = getOrFetchDeviceStateData(device.getId());
+ if (stateData != null) {
+ TbMsgMetaData md = new TbMsgMetaData();
+ md.putValue("deviceName", device.getName());
+ md.putValue("deviceType", device.getType());
+ stateData.setMetaData(md);
+ }
+ } else {
+ sendDeviceEvent(device.getTenantId(), device.getId(), address.get(), false, true, false);
}
}
private void onDeviceDeleted(TenantId tenantId, DeviceId deviceId) {
- deviceStates.remove(deviceId);
- Set<DeviceId> deviceIds = tenantDevices.get(tenantId);
- if (deviceIds != null) {
- deviceIds.remove(deviceId);
- if (deviceIds.isEmpty()) {
- tenantDevices.remove(tenantId);
+ Optional<ServerAddress> address = routingService.resolveById(deviceId);
+ if (!address.isPresent()) {
+ deviceStates.remove(deviceId);
+ Set<DeviceId> deviceIds = tenantDevices.get(tenantId);
+ if (deviceIds != null) {
+ deviceIds.remove(deviceId);
+ if (deviceIds.isEmpty()) {
+ tenantDevices.remove(tenantId);
+ }
}
+ } else {
+ sendDeviceEvent(tenantId, deviceId, address.get(), false, false, true);
}
}
diff --git a/application/src/main/java/org/thingsboard/server/service/state/DeviceStateService.java b/application/src/main/java/org/thingsboard/server/service/state/DeviceStateService.java
index 37f785c..dab9e69 100644
--- a/application/src/main/java/org/thingsboard/server/service/state/DeviceStateService.java
+++ b/application/src/main/java/org/thingsboard/server/service/state/DeviceStateService.java
@@ -17,6 +17,7 @@ package org.thingsboard.server.service.state;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.DeviceId;
+import org.thingsboard.server.common.msg.cluster.ServerAddress;
import java.util.Optional;
@@ -39,6 +40,7 @@ public interface DeviceStateService {
void onDeviceInactivityTimeoutUpdate(DeviceId deviceId, long inactivityTimeout);
- Optional<DeviceState> getDeviceState(DeviceId deviceId);
+ void onClusterUpdate();
+ void onRemoteMsg(ServerAddress serverAddress, byte[] bytes);
}
diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java
index 0942b40..dbd58d5 100644
--- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java
+++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java
@@ -362,7 +362,7 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio
DonAsynchron.withCallback(tsService.findAll(entityId, queries),
missedUpdates -> {
- if (!missedUpdates.isEmpty()) {
+ if (missedUpdates != null && !missedUpdates.isEmpty()) {
tellRemoteSubUpdate(address, sessionId, new SubscriptionUpdate(subscription.getSubscriptionId(), missedUpdates));
}
},
application/src/main/proto/cluster.proto 12(+12 -0)
diff --git a/application/src/main/proto/cluster.proto b/application/src/main/proto/cluster.proto
index ac96010..21c963b 100644
--- a/application/src/main/proto/cluster.proto
+++ b/application/src/main/proto/cluster.proto
@@ -57,6 +57,8 @@ enum MessageType {
CLUSTER_TELEMETRY_ATTR_UPDATE_MESSAGE = 10;
CLUSTER_TELEMETRY_TS_UPDATE_MESSAGE = 11;
CLUSTER_RPC_FROM_DEVICE_RESPONSE_MESSAGE = 12;
+
+ CLUSTER_DEVICE_STATE_SERVICE_MESSAGE = 13;
}
// Messages related to CLUSTER_TELEMETRY_MESSAGE
@@ -128,3 +130,13 @@ message FromDeviceRPCResponseProto {
string response = 3;
int32 error = 4;
}
+
+message DeviceStateServiceMsgProto {
+ int64 tenantIdMSB = 1;
+ int64 tenantIdLSB = 2;
+ int64 deviceIdMSB = 3;
+ int64 deviceIdLSB = 4;
+ bool added = 5;
+ bool updated = 6;
+ bool deleted = 7;
+}
\ No newline at end of file
diff --git a/application/src/test/java/org/thingsboard/server/system/BaseHttpDeviceApiTest.java b/application/src/test/java/org/thingsboard/server/system/BaseHttpDeviceApiTest.java
index 4fa6162..5e6896b 100644
--- a/application/src/test/java/org/thingsboard/server/system/BaseHttpDeviceApiTest.java
+++ b/application/src/test/java/org/thingsboard/server/system/BaseHttpDeviceApiTest.java
@@ -64,6 +64,7 @@ public abstract class BaseHttpDeviceApiTest extends AbstractControllerTest {
mockMvc.perform(
asyncDispatch(doPost("/api/v1/" + deviceCredentials.getCredentialsId() + "/attributes", attrMap, new String[]{}).andReturn()))
.andExpect(status().isOk());
+ Thread.sleep(2000);
doGetAsync("/api/v1/" + deviceCredentials.getCredentialsId() + "/attributes?clientKeys=keyA,keyB,keyC").andExpect(status().isOk());
}
common/data/pom.xml 2(+1 -1)
diff --git a/common/data/pom.xml b/common/data/pom.xml
index dac9ece..d2a44e2 100644
--- a/common/data/pom.xml
+++ b/common/data/pom.xml
@@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
- <version>2.0.1</version>
+ <version>2.0.2</version>
<artifactId>common</artifactId>
</parent>
<groupId>org.thingsboard.common</groupId>
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java
index ff908fc..7b74e62 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java
@@ -98,6 +98,7 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa
public boolean updateAssignedCustomer(Customer customer) {
ShortCustomerInfo customerInfo = customer.toShortCustomerInfo();
if (this.assignedCustomers != null && this.assignedCustomers.contains(customerInfo)) {
+ this.assignedCustomers.remove(customerInfo);
this.assignedCustomers.add(customerInfo);
return true;
} else {
common/message/pom.xml 2(+1 -1)
diff --git a/common/message/pom.xml b/common/message/pom.xml
index 9944fe0..3863443 100644
--- a/common/message/pom.xml
+++ b/common/message/pom.xml
@@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
- <version>2.0.1</version>
+ <version>2.0.2</version>
<artifactId>common</artifactId>
</parent>
<groupId>org.thingsboard.common</groupId>
common/pom.xml 2(+1 -1)
diff --git a/common/pom.xml b/common/pom.xml
index e1defb9..00711ed 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
- <version>2.0.1</version>
+ <version>2.0.2</version>
<artifactId>thingsboard</artifactId>
</parent>
<groupId>org.thingsboard</groupId>
common/transport/pom.xml 2(+1 -1)
diff --git a/common/transport/pom.xml b/common/transport/pom.xml
index e87bb60..4cc9e8d 100644
--- a/common/transport/pom.xml
+++ b/common/transport/pom.xml
@@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
- <version>2.0.1</version>
+ <version>2.0.2</version>
<artifactId>common</artifactId>
</parent>
<groupId>org.thingsboard.common</groupId>
diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/SessionMsgProcessor.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/SessionMsgProcessor.java
index ea77366..4cff643 100644
--- a/common/transport/src/main/java/org/thingsboard/server/common/transport/SessionMsgProcessor.java
+++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/SessionMsgProcessor.java
@@ -15,11 +15,13 @@
*/
package org.thingsboard.server.common.transport;
-import org.thingsboard.server.common.data.security.DeviceCredentialsFilter;
+import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.msg.aware.SessionAwareMsg;
public interface SessionMsgProcessor {
void process(SessionAwareMsg msg);
+ void onDeviceAdded(Device device);
+
}
dao/pom.xml 2(+1 -1)
diff --git a/dao/pom.xml b/dao/pom.xml
index da8acc9..0a062ac 100644
--- a/dao/pom.xml
+++ b/dao/pom.xml
@@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
- <version>2.0.1</version>
+ <version>2.0.2</version>
<artifactId>thingsboard</artifactId>
</parent>
<artifactId>dao</artifactId>
diff --git a/dao/src/main/java/org/thingsboard/server/dao/cassandra/AbstractCassandraCluster.java b/dao/src/main/java/org/thingsboard/server/dao/cassandra/AbstractCassandraCluster.java
index fd61976..f4626fb 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/cassandra/AbstractCassandraCluster.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/cassandra/AbstractCassandraCluster.java
@@ -67,9 +67,9 @@ public abstract class AbstractCassandraCluster {
private long initTimeout;
@Value("${cassandra.init_retry_interval_ms}")
private long initRetryInterval;
- @Value("${cassandra.max_requests_per_connection_local:128}")
+ @Value("${cassandra.max_requests_per_connection_local:32768}")
private int max_requests_local;
- @Value("${cassandra.max_requests_per_connection_remote:128}")
+ @Value("${cassandra.max_requests_per_connection_remote:32768}")
private int max_requests_remote;
@Autowired
docker/cassandra/Makefile 2(+1 -1)
diff --git a/docker/cassandra/Makefile b/docker/cassandra/Makefile
index 1ac51fc..0447716 100644
--- a/docker/cassandra/Makefile
+++ b/docker/cassandra/Makefile
@@ -1,4 +1,4 @@
-VERSION=2.0.1
+VERSION=2.0.2
PROJECT=thingsboard
APP=cassandra
docker/cassandra-setup/Makefile 2(+1 -1)
diff --git a/docker/cassandra-setup/Makefile b/docker/cassandra-setup/Makefile
index b90a1f1..9f0e211 100644
--- a/docker/cassandra-setup/Makefile
+++ b/docker/cassandra-setup/Makefile
@@ -1,4 +1,4 @@
-VERSION=2.0.1
+VERSION=2.0.2
PROJECT=thingsboard
APP=cassandra-setup
docker/docker-compose.yml 2(+1 -1)
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
index b7a715e..5219dcc 100644
--- a/docker/docker-compose.yml
+++ b/docker/docker-compose.yml
@@ -18,7 +18,7 @@ version: '2'
services:
tb:
- image: "thingsboard/application:2.0.1"
+ image: "thingsboard/application:2.0.2"
ports:
- "8080:8080"
- "1883:1883"
docker/k8s/cassandra.yaml 2(+1 -1)
diff --git a/docker/k8s/cassandra.yaml b/docker/k8s/cassandra.yaml
index 04cafcb..ca5dfc0 100644
--- a/docker/k8s/cassandra.yaml
+++ b/docker/k8s/cassandra.yaml
@@ -54,7 +54,7 @@ spec:
topologyKey: "kubernetes.io/hostname"
containers:
- name: cassandra
- image: thingsboard/cassandra:2.0.1
+ image: thingsboard/cassandra:2.0.2
imagePullPolicy: Always
ports:
- containerPort: 7000
docker/k8s/cassandra-setup.yaml 2(+1 -1)
diff --git a/docker/k8s/cassandra-setup.yaml b/docker/k8s/cassandra-setup.yaml
index eefc148..f0337f8 100644
--- a/docker/k8s/cassandra-setup.yaml
+++ b/docker/k8s/cassandra-setup.yaml
@@ -22,7 +22,7 @@ spec:
containers:
- name: cassandra-setup
imagePullPolicy: Always
- image: thingsboard/cassandra-setup:2.0.1
+ image: thingsboard/cassandra-setup:2.0.2
env:
- name: ADD_DEMO_DATA
value: "true"
docker/k8s/tb.yaml 2(+1 -1)
diff --git a/docker/k8s/tb.yaml b/docker/k8s/tb.yaml
index 947afb4..81b4ff1 100644
--- a/docker/k8s/tb.yaml
+++ b/docker/k8s/tb.yaml
@@ -84,7 +84,7 @@ spec:
containers:
- name: tb
imagePullPolicy: Always
- image: thingsboard/application:2.0.1
+ image: thingsboard/application:2.0.2
ports:
- containerPort: 8080
name: ui
docker/k8s/zookeeper.yaml 2(+1 -1)
diff --git a/docker/k8s/zookeeper.yaml b/docker/k8s/zookeeper.yaml
index 744c071..e4648dd 100644
--- a/docker/k8s/zookeeper.yaml
+++ b/docker/k8s/zookeeper.yaml
@@ -87,7 +87,7 @@ spec:
containers:
- name: zk
imagePullPolicy: Always
- image: thingsboard/zk:2.0.1
+ image: thingsboard/zk:2.0.2
ports:
- containerPort: 2181
name: client
docker/tb/Makefile 2(+1 -1)
diff --git a/docker/tb/Makefile b/docker/tb/Makefile
index 5e0fcf1..63d7401 100644
--- a/docker/tb/Makefile
+++ b/docker/tb/Makefile
@@ -1,4 +1,4 @@
-VERSION=2.0.1
+VERSION=2.0.2
PROJECT=thingsboard
APP=application
docker/zookeeper/Makefile 2(+1 -1)
diff --git a/docker/zookeeper/Makefile b/docker/zookeeper/Makefile
index 1d74b0d..6503c90 100644
--- a/docker/zookeeper/Makefile
+++ b/docker/zookeeper/Makefile
@@ -1,4 +1,4 @@
-VERSION=2.0.1
+VERSION=2.0.2
PROJECT=thingsboard
APP=zk
netty-mqtt/pom.xml 4(+2 -2)
diff --git a/netty-mqtt/pom.xml b/netty-mqtt/pom.xml
index 4fc6625..67603ff 100644
--- a/netty-mqtt/pom.xml
+++ b/netty-mqtt/pom.xml
@@ -19,12 +19,12 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
- <version>2.0.1</version>
+ <version>2.0.2</version>
<artifactId>thingsboard</artifactId>
</parent>
<groupId>org.thingsboard</groupId>
<artifactId>netty-mqtt</artifactId>
- <version>2.0.1</version>
+ <version>2.0.2</version>
<packaging>jar</packaging>
<name>Netty MQTT Client</name>
diff --git a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttChannelHandler.java b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttChannelHandler.java
index ef5e7a5..c927c42 100644
--- a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttChannelHandler.java
+++ b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttChannelHandler.java
@@ -98,33 +98,25 @@ final class MqttChannelHandler extends SimpleChannelInboundHandler<MqttMessage>
}
private void invokeHandlersForIncomingPublish(MqttPublishMessage message) {
- for (MqttSubscribtion subscribtion : ImmutableSet.copyOf(this.client.getSubscriptions().values())) {
- if (subscribtion.matches(message.variableHeader().topicName())) {
- if (subscribtion.isOnce() && subscribtion.isCalled()) {
+ boolean handlerInvoked = false;
+ for (MqttSubscription subscription : ImmutableSet.copyOf(this.client.getSubscriptions().values())) {
+ if (subscription.matches(message.variableHeader().topicName())) {
+ if (subscription.isOnce() && subscription.isCalled()) {
continue;
}
message.payload().markReaderIndex();
- subscribtion.setCalled(true);
- subscribtion.getHandler().onMessage(message.variableHeader().topicName(), message.payload());
- if (subscribtion.isOnce()) {
- this.client.off(subscribtion.getTopic(), subscribtion.getHandler());
+ subscription.setCalled(true);
+ subscription.getHandler().onMessage(message.variableHeader().topicName(), message.payload());
+ if (subscription.isOnce()) {
+ this.client.off(subscription.getTopic(), subscription.getHandler());
}
message.payload().resetReaderIndex();
+ handlerInvoked = true;
}
}
- /*Set<MqttSubscribtion> subscribtions = ImmutableSet.copyOf(this.client.getSubscriptions().get(message.variableHeader().topicName()));
- for (MqttSubscribtion subscribtion : subscribtions) {
- if(subscribtion.isOnce() && subscribtion.isCalled()){
- continue;
- }
- message.payload().markReaderIndex();
- subscribtion.setCalled(true);
- subscribtion.getHandler().onMessage(message.variableHeader().topicName(), message.payload());
- if(subscribtion.isOnce()){
- this.client.off(subscribtion.getTopic(), subscribtion.getHandler());
- }
- message.payload().resetReaderIndex();
- }*/
+ if (!handlerInvoked && client.getDefaultHandler() != null) {
+ client.getDefaultHandler().onMessage(message.variableHeader().topicName(), message.payload());
+ }
message.payload().release();
}
@@ -133,7 +125,7 @@ final class MqttChannelHandler extends SimpleChannelInboundHandler<MqttMessage>
case CONNECTION_ACCEPTED:
this.connectFuture.setSuccess(new MqttConnectResult(true, MqttConnectReturnCode.CONNECTION_ACCEPTED, channel.closeFuture()));
- this.client.getPendingSubscribtions().entrySet().stream().filter((e) -> !e.getValue().isSent()).forEach((e) -> {
+ this.client.getPendingSubscriptions().entrySet().stream().filter((e) -> !e.getValue().isSent()).forEach((e) -> {
channel.write(e.getValue().getSubscribeMessage());
e.getValue().setSent(true);
});
@@ -148,6 +140,9 @@ final class MqttChannelHandler extends SimpleChannelInboundHandler<MqttMessage>
}
});
channel.flush();
+ if (this.client.isReconnect()) {
+ this.client.onSuccessfulReconnect();
+ }
break;
case CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD:
@@ -163,19 +158,19 @@ final class MqttChannelHandler extends SimpleChannelInboundHandler<MqttMessage>
}
private void handleSubAck(MqttSubAckMessage message) {
- MqttPendingSubscribtion pendingSubscription = this.client.getPendingSubscribtions().remove(message.variableHeader().messageId());
+ MqttPendingSubscription pendingSubscription = this.client.getPendingSubscriptions().remove(message.variableHeader().messageId());
if (pendingSubscription == null) {
return;
}
pendingSubscription.onSubackReceived();
- for (MqttPendingSubscribtion.MqttPendingHandler handler : pendingSubscription.getHandlers()) {
- MqttSubscribtion subscribtion = new MqttSubscribtion(pendingSubscription.getTopic(), handler.getHandler(), handler.isOnce());
- this.client.getSubscriptions().put(pendingSubscription.getTopic(), subscribtion);
- this.client.getHandlerToSubscribtion().put(handler.getHandler(), subscribtion);
+ for (MqttPendingSubscription.MqttPendingHandler handler : pendingSubscription.getHandlers()) {
+ MqttSubscription subscription = new MqttSubscription(pendingSubscription.getTopic(), handler.getHandler(), handler.isOnce());
+ this.client.getSubscriptions().put(pendingSubscription.getTopic(), subscription);
+ this.client.getHandlerToSubscribtion().put(handler.getHandler(), subscription);
}
this.client.getPendingSubscribeTopics().remove(pendingSubscription.getTopic());
- this.client.getServerSubscribtions().add(pendingSubscription.getTopic());
+ this.client.getServerSubscriptions().add(pendingSubscription.getTopic());
if (!pendingSubscription.getFuture().isDone()) {
pendingSubscription.getFuture().setSuccess(null);
@@ -215,13 +210,13 @@ final class MqttChannelHandler extends SimpleChannelInboundHandler<MqttMessage>
}
private void handleUnsuback(MqttUnsubAckMessage message) {
- MqttPendingUnsubscribtion unsubscribtion = this.client.getPendingServerUnsubscribes().get(message.variableHeader().messageId());
- if (unsubscribtion == null) {
+ MqttPendingUnsubscription unsubscription = this.client.getPendingServerUnsubscribes().get(message.variableHeader().messageId());
+ if (unsubscription == null) {
return;
}
- unsubscribtion.onUnsubackReceived();
- this.client.getServerSubscribtions().remove(unsubscribtion.getTopic());
- unsubscribtion.getFuture().setSuccess(null);
+ unsubscription.onUnsubackReceived();
+ this.client.getServerSubscriptions().remove(unsubscription.getTopic());
+ unsubscription.getFuture().setSuccess(null);
this.client.getPendingServerUnsubscribes().remove(message.variableHeader().messageId());
}
diff --git a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClient.java b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClient.java
index 6563525..37efca3 100644
--- a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClient.java
+++ b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClient.java
@@ -92,7 +92,7 @@ public interface MqttClient {
/**
* Subscribe on the given topic. When a message is received, MqttClient will invoke the {@link MqttHandler#onMessage(String, ByteBuf)} function of the given handler
- * This subscribtion is only once. If the MqttClient has received 1 message, the subscribtion will be removed
+ * This subscription is only once. If the MqttClient has received 1 message, the subscription will be removed
*
* @param topic The topic filter to subscribe to
* @param handler The handler to invoke when we receive a message
@@ -102,7 +102,7 @@ public interface MqttClient {
/**
* Subscribe on the given topic, with the given qos. When a message is received, MqttClient will invoke the {@link MqttHandler#onMessage(String, ByteBuf)} function of the given handler
- * This subscribtion is only once. If the MqttClient has received 1 message, the subscribtion will be removed
+ * This subscription is only once. If the MqttClient has received 1 message, the subscription will be removed
*
* @param topic The topic filter to subscribe to
* @param handler The handler to invoke when we receive a message
@@ -112,7 +112,7 @@ public interface MqttClient {
Future<Void> once(String topic, MqttHandler handler, MqttQoS qos);
/**
- * Remove the subscribtion for the given topic and handler
+ * Remove the subscription for the given topic and handler
* If you want to unsubscribe from all handlers known for this topic, use {@link #off(String)}
*
* @param topic The topic to unsubscribe for
@@ -122,7 +122,7 @@ public interface MqttClient {
Future<Void> off(String topic, MqttHandler handler);
/**
- * Remove all subscribtions for the given topic.
+ * Remove all subscriptions for the given topic.
* If you want to specify which handler to unsubscribe, use {@link #off(String, MqttHandler)}
*
* @param topic The topic to unsubscribe for
@@ -172,24 +172,18 @@ public interface MqttClient {
*/
MqttClientConfig getClientConfig();
- /**
- * Construct the MqttClientImpl with default config
- */
- static MqttClient create(){
- return new MqttClientImpl();
- }
/**
* Construct the MqttClientImpl with additional config.
* This config can also be changed using the {@link #getClientConfig()} function
*
* @param config The config object to use while looking for settings
+ * @param defaultHandler The handler for incoming messages that do not match any topic subscriptions
*/
- static MqttClient create(MqttClientConfig config){
- return new MqttClientImpl(config);
+ static MqttClient create(MqttClientConfig config, MqttHandler defaultHandler){
+ return new MqttClientImpl(config, defaultHandler);
}
-
/**
* Send disconnect and close channel
*
diff --git a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientCallback.java b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientCallback.java
index d7f0a08..9f86b8e 100644
--- a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientCallback.java
+++ b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientCallback.java
@@ -15,6 +15,8 @@
*/
package org.thingsboard.mqtt;
+import io.netty.channel.ChannelId;
+
/**
* Created by Valerii Sosliuk on 12/30/2017.
*/
@@ -25,5 +27,11 @@ public interface MqttClientCallback {
*
* @param cause the reason behind the loss of connection.
*/
- public void connectionLost(Throwable cause);
+ void connectionLost(Throwable cause);
+
+ /**
+ * This method is called when the connection to the server is recovered.
+ *
+ */
+ void onSuccessfulReconnect();
}
diff --git a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java
index 3914105..a5df846 100644
--- a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java
+++ b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java
@@ -40,23 +40,26 @@ import java.util.concurrent.atomic.AtomicInteger;
@SuppressWarnings({"WeakerAccess", "unused"})
final class MqttClientImpl implements MqttClient {
- private final Set<String> serverSubscribtions = new HashSet<>();
- private final IntObjectHashMap<MqttPendingUnsubscribtion> pendingServerUnsubscribes = new IntObjectHashMap<>();
+ private final Set<String> serverSubscriptions = new HashSet<>();
+ private final IntObjectHashMap<MqttPendingUnsubscription> pendingServerUnsubscribes = new IntObjectHashMap<>();
private final IntObjectHashMap<MqttIncomingQos2Publish> qos2PendingIncomingPublishes = new IntObjectHashMap<>();
private final IntObjectHashMap<MqttPendingPublish> pendingPublishes = new IntObjectHashMap<>();
- private final HashMultimap<String, MqttSubscribtion> subscriptions = HashMultimap.create();
- private final IntObjectHashMap<MqttPendingSubscribtion> pendingSubscribtions = new IntObjectHashMap<>();
+ private final HashMultimap<String, MqttSubscription> subscriptions = HashMultimap.create();
+ private final IntObjectHashMap<MqttPendingSubscription> pendingSubscriptions = new IntObjectHashMap<>();
private final Set<String> pendingSubscribeTopics = new HashSet<>();
- private final HashMultimap<MqttHandler, MqttSubscribtion> handlerToSubscribtion = HashMultimap.create();
+ private final HashMultimap<MqttHandler, MqttSubscription> handlerToSubscribtion = HashMultimap.create();
private final AtomicInteger nextMessageId = new AtomicInteger(1);
private final MqttClientConfig clientConfig;
+ private final MqttHandler defaultHandler;
+
private EventLoopGroup eventLoop;
- private Channel channel;
+ private volatile Channel channel;
- private boolean disconnected = false;
+ private volatile boolean disconnected = false;
+ private volatile boolean reconnect = false;
private String host;
private int port;
private MqttClientCallback callback;
@@ -65,8 +68,9 @@ final class MqttClientImpl implements MqttClient {
/**
* Construct the MqttClientImpl with default config
*/
- public MqttClientImpl() {
+ public MqttClientImpl(MqttHandler defaultHandler) {
this.clientConfig = new MqttClientConfig();
+ this.defaultHandler = defaultHandler;
}
/**
@@ -75,8 +79,9 @@ final class MqttClientImpl implements MqttClient {
*
* @param clientConfig The config object to use while looking for settings
*/
- public MqttClientImpl(MqttClientConfig clientConfig) {
+ public MqttClientImpl(MqttClientConfig clientConfig, MqttHandler defaultHandler) {
this.clientConfig = clientConfig;
+ this.defaultHandler = defaultHandler;
}
/**
@@ -100,12 +105,15 @@ final class MqttClientImpl implements MqttClient {
*/
@Override
public Future<MqttConnectResult> connect(String host, int port) {
+ return connect(host, port, false);
+ }
+
+ private Future<MqttConnectResult> connect(String host, int port, boolean reconnect) {
if (this.eventLoop == null) {
this.eventLoop = new NioEventLoopGroup();
}
this.host = host;
this.port = port;
-
Promise<MqttConnectResult> connectFuture = new DefaultPromise<>(this.eventLoop.next());
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(this.eventLoop);
@@ -113,22 +121,47 @@ final class MqttClientImpl implements MqttClient {
bootstrap.remoteAddress(host, port);
bootstrap.handler(new MqttChannelInitializer(connectFuture, host, port, clientConfig.getSslContext()));
ChannelFuture future = bootstrap.connect();
+
future.addListener((ChannelFutureListener) f -> {
if (f.isSuccess()) {
MqttClientImpl.this.channel = f.channel();
- } else if (clientConfig.isReconnect() && !disconnected) {
- eventLoop.schedule((Runnable) () -> connect(host, port), 1L, TimeUnit.SECONDS);
+ MqttClientImpl.this.channel.closeFuture().addListener((ChannelFutureListener) channelFuture -> {
+ if (isConnected()) {
+ return;
+ }
+ ChannelClosedException e = new ChannelClosedException("Channel is closed!");
+ if (callback != null) {
+ callback.connectionLost(e);
+ }
+ pendingSubscriptions.clear();
+ serverSubscriptions.clear();
+ subscriptions.clear();
+ pendingServerUnsubscribes.clear();
+ qos2PendingIncomingPublishes.clear();
+ pendingPublishes.clear();
+ pendingSubscribeTopics.clear();
+ handlerToSubscribtion.clear();
+ scheduleConnectIfRequired(host, port, true);
+ });
+ } else {
+ scheduleConnectIfRequired(host, port, reconnect);
}
});
return connectFuture;
}
+ private void scheduleConnectIfRequired(String host, int port, boolean reconnect) {
+ if (clientConfig.isReconnect() && !disconnected) {
+ if (reconnect) {
+ this.reconnect = true;
+ }
+ eventLoop.schedule((Runnable) () -> connect(host, port, reconnect), 1L, TimeUnit.SECONDS);
+ }
+ }
+
@Override
public boolean isConnected() {
- if (!disconnected) {
- return channel == null ? false : channel.isActive();
- };
- return false;
+ return !disconnected && channel != null && channel.isActive();
}
@Override
@@ -183,12 +216,12 @@ final class MqttClientImpl implements MqttClient {
*/
@Override
public Future<Void> on(String topic, MqttHandler handler, MqttQoS qos) {
- return createSubscribtion(topic, handler, false, qos);
+ return createSubscription(topic, handler, false, qos);
}
/**
* Subscribe on the given topic. When a message is received, MqttClient will invoke the {@link MqttHandler#onMessage(String, ByteBuf)} function of the given handler
- * This subscribtion is only once. If the MqttClient has received 1 message, the subscribtion will be removed
+ * This subscription is only once. If the MqttClient has received 1 message, the subscription will be removed
*
* @param topic The topic filter to subscribe to
* @param handler The handler to invoke when we receive a message
@@ -201,7 +234,7 @@ final class MqttClientImpl implements MqttClient {
/**
* Subscribe on the given topic, with the given qos. When a message is received, MqttClient will invoke the {@link MqttHandler#onMessage(String, ByteBuf)} function of the given handler
- * This subscribtion is only once. If the MqttClient has received 1 message, the subscribtion will be removed
+ * This subscription is only once. If the MqttClient has received 1 message, the subscription will be removed
*
* @param topic The topic filter to subscribe to
* @param handler The handler to invoke when we receive a message
@@ -210,11 +243,11 @@ final class MqttClientImpl implements MqttClient {
*/
@Override
public Future<Void> once(String topic, MqttHandler handler, MqttQoS qos) {
- return createSubscribtion(topic, handler, true, qos);
+ return createSubscription(topic, handler, true, qos);
}
/**
- * Remove the subscribtion for the given topic and handler
+ * Remove the subscription for the given topic and handler
* If you want to unsubscribe from all handlers known for this topic, use {@link #off(String)}
*
* @param topic The topic to unsubscribe for
@@ -224,8 +257,8 @@ final class MqttClientImpl implements MqttClient {
@Override
public Future<Void> off(String topic, MqttHandler handler) {
Promise<Void> future = new DefaultPromise<>(this.eventLoop.next());
- for (MqttSubscribtion subscribtion : this.handlerToSubscribtion.get(handler)) {
- this.subscriptions.remove(topic, subscribtion);
+ for (MqttSubscription subscription : this.handlerToSubscribtion.get(handler)) {
+ this.subscriptions.remove(topic, subscription);
}
this.handlerToSubscribtion.removeAll(handler);
this.checkSubscribtions(topic, future);
@@ -233,7 +266,7 @@ final class MqttClientImpl implements MqttClient {
}
/**
- * Remove all subscribtions for the given topic.
+ * Remove all subscriptions for the given topic.
* If you want to specify which handler to unsubscribe, use {@link #off(String, MqttHandler)}
*
* @param topic The topic to unsubscribe for
@@ -242,12 +275,12 @@ final class MqttClientImpl implements MqttClient {
@Override
public Future<Void> off(String topic) {
Promise<Void> future = new DefaultPromise<>(this.eventLoop.next());
- ImmutableSet<MqttSubscribtion> subscribtions = ImmutableSet.copyOf(this.subscriptions.get(topic));
- for (MqttSubscribtion subscribtion : subscribtions) {
- for (MqttSubscribtion handSub : this.handlerToSubscribtion.get(subscribtion.getHandler())) {
+ ImmutableSet<MqttSubscription> subscriptions = ImmutableSet.copyOf(this.subscriptions.get(topic));
+ for (MqttSubscription subscription : subscriptions) {
+ for (MqttSubscription handSub : this.handlerToSubscribtion.get(subscription.getHandler())) {
this.subscriptions.remove(topic, handSub);
}
- this.handlerToSubscribtion.remove(subscribtion.getHandler(), subscribtion);
+ this.handlerToSubscribtion.remove(subscription.getHandler(), subscription);
}
this.checkSubscribtions(topic, future);
return future;
@@ -310,7 +343,7 @@ final class MqttClientImpl implements MqttClient {
ChannelFuture channelFuture = this.sendAndFlushPacket(message);
if (channelFuture != null) {
- pendingPublish.setSent(channelFuture != null);
+ pendingPublish.setSent(true);
if (channelFuture.cause() != null) {
future.setFailure(channelFuture.cause());
return future;
@@ -352,6 +385,15 @@ final class MqttClientImpl implements MqttClient {
///////////////////////////////////////////// PRIVATE API /////////////////////////////////////////////
+ public boolean isReconnect() {
+ return reconnect;
+ }
+
+ public void onSuccessfulReconnect() {
+ callback.onSuccessfulReconnect();
+ }
+
+
ChannelFuture sendAndFlushPacket(Object message) {
if (this.channel == null) {
return null;
@@ -359,11 +401,7 @@ final class MqttClientImpl implements MqttClient {
if (this.channel.isActive()) {
return this.channel.writeAndFlush(message);
}
- ChannelClosedException e = new ChannelClosedException("Channel is closed");
- if (callback != null) {
- callback.connectionLost(e);
- }
- return this.channel.newFailedFuture(e);
+ return this.channel.newFailedFuture(new ChannelClosedException("Channel is closed!"));
}
private MqttMessageIdVariableHeader getNewMessageId() {
@@ -371,18 +409,18 @@ final class MqttClientImpl implements MqttClient {
return MqttMessageIdVariableHeader.from(this.nextMessageId.getAndIncrement());
}
- private Future<Void> createSubscribtion(String topic, MqttHandler handler, boolean once, MqttQoS qos) {
+ private Future<Void> createSubscription(String topic, MqttHandler handler, boolean once, MqttQoS qos) {
if (this.pendingSubscribeTopics.contains(topic)) {
- Optional<Map.Entry<Integer, MqttPendingSubscribtion>> subscribtionEntry = this.pendingSubscribtions.entrySet().stream().filter((e) -> e.getValue().getTopic().equals(topic)).findAny();
- if (subscribtionEntry.isPresent()) {
- subscribtionEntry.get().getValue().addHandler(handler, once);
- return subscribtionEntry.get().getValue().getFuture();
+ Optional<Map.Entry<Integer, MqttPendingSubscription>> subscriptionEntry = this.pendingSubscriptions.entrySet().stream().filter((e) -> e.getValue().getTopic().equals(topic)).findAny();
+ if (subscriptionEntry.isPresent()) {
+ subscriptionEntry.get().getValue().addHandler(handler, once);
+ return subscriptionEntry.get().getValue().getFuture();
}
}
- if (this.serverSubscribtions.contains(topic)) {
- MqttSubscribtion subscribtion = new MqttSubscribtion(topic, handler, once);
- this.subscriptions.put(topic, subscribtion);
- this.handlerToSubscribtion.put(handler, subscribtion);
+ if (this.serverSubscriptions.contains(topic)) {
+ MqttSubscription subscription = new MqttSubscription(topic, handler, once);
+ this.subscriptions.put(topic, subscription);
+ this.handlerToSubscribtion.put(handler, subscription);
return this.channel.newSucceededFuture();
}
@@ -393,27 +431,27 @@ final class MqttClientImpl implements MqttClient {
MqttSubscribePayload payload = new MqttSubscribePayload(Collections.singletonList(subscription));
MqttSubscribeMessage message = new MqttSubscribeMessage(fixedHeader, variableHeader, payload);
- final MqttPendingSubscribtion pendingSubscribtion = new MqttPendingSubscribtion(future, topic, message);
- pendingSubscribtion.addHandler(handler, once);
- this.pendingSubscribtions.put(variableHeader.messageId(), pendingSubscribtion);
+ final MqttPendingSubscription pendingSubscription = new MqttPendingSubscription(future, topic, message);
+ pendingSubscription.addHandler(handler, once);
+ this.pendingSubscriptions.put(variableHeader.messageId(), pendingSubscription);
this.pendingSubscribeTopics.add(topic);
- pendingSubscribtion.setSent(this.sendAndFlushPacket(message) != null); //If not sent, we will send it when the connection is opened
+ pendingSubscription.setSent(this.sendAndFlushPacket(message) != null); //If not sent, we will send it when the connection is opened
- pendingSubscribtion.startRetransmitTimer(this.eventLoop.next(), this::sendAndFlushPacket);
+ pendingSubscription.startRetransmitTimer(this.eventLoop.next(), this::sendAndFlushPacket);
return future;
}
private void checkSubscribtions(String topic, Promise<Void> promise) {
- if (!(this.subscriptions.containsKey(topic) && this.subscriptions.get(topic).size() != 0) && this.serverSubscribtions.contains(topic)) {
+ if (!(this.subscriptions.containsKey(topic) && this.subscriptions.get(topic).size() != 0) && this.serverSubscriptions.contains(topic)) {
MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.UNSUBSCRIBE, false, MqttQoS.AT_LEAST_ONCE, false, 0);
MqttMessageIdVariableHeader variableHeader = getNewMessageId();
MqttUnsubscribePayload payload = new MqttUnsubscribePayload(Collections.singletonList(topic));
MqttUnsubscribeMessage message = new MqttUnsubscribeMessage(fixedHeader, variableHeader, payload);
- MqttPendingUnsubscribtion pendingUnsubscribtion = new MqttPendingUnsubscribtion(promise, topic, message);
- this.pendingServerUnsubscribes.put(variableHeader.messageId(), pendingUnsubscribtion);
- pendingUnsubscribtion.startRetransmissionTimer(this.eventLoop.next(), this::sendAndFlushPacket);
+ MqttPendingUnsubscription pendingUnsubscription = new MqttPendingUnsubscription(promise, topic, message);
+ this.pendingServerUnsubscribes.put(variableHeader.messageId(), pendingUnsubscription);
+ pendingUnsubscription.startRetransmissionTimer(this.eventLoop.next(), this::sendAndFlushPacket);
this.sendAndFlushPacket(message);
} else {
@@ -421,11 +459,11 @@ final class MqttClientImpl implements MqttClient {
}
}
- IntObjectHashMap<MqttPendingSubscribtion> getPendingSubscribtions() {
- return pendingSubscribtions;
+ IntObjectHashMap<MqttPendingSubscription> getPendingSubscriptions() {
+ return pendingSubscriptions;
}
- HashMultimap<String, MqttSubscribtion> getSubscriptions() {
+ HashMultimap<String, MqttSubscription> getSubscriptions() {
return subscriptions;
}
@@ -433,15 +471,15 @@ final class MqttClientImpl implements MqttClient {
return pendingSubscribeTopics;
}
- HashMultimap<MqttHandler, MqttSubscribtion> getHandlerToSubscribtion() {
+ HashMultimap<MqttHandler, MqttSubscription> getHandlerToSubscribtion() {
return handlerToSubscribtion;
}
- Set<String> getServerSubscribtions() {
- return serverSubscribtions;
+ Set<String> getServerSubscriptions() {
+ return serverSubscriptions;
}
- IntObjectHashMap<MqttPendingUnsubscribtion> getPendingServerUnsubscribes() {
+ IntObjectHashMap<MqttPendingUnsubscription> getPendingServerUnsubscribes() {
return pendingServerUnsubscribes;
}
@@ -481,4 +519,9 @@ final class MqttClientImpl implements MqttClient {
ch.pipeline().addLast("mqttHandler", new MqttChannelHandler(MqttClientImpl.this, connectFuture));
}
}
+
+ MqttHandler getDefaultHandler() {
+ return defaultHandler;
+ }
+
}
pom.xml 2(+1 -1)
diff --git a/pom.xml b/pom.xml
index b46faf6..3252d89 100755
--- a/pom.xml
+++ b/pom.xml
@@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.thingsboard</groupId>
<artifactId>thingsboard</artifactId>
- <version>2.0.1</version>
+ <version>2.0.2</version>
<packaging>pom</packaging>
<name>Thingsboard</name>
rule-engine/pom.xml 2(+1 -1)
diff --git a/rule-engine/pom.xml b/rule-engine/pom.xml
index 53b2d1a..232517b 100644
--- a/rule-engine/pom.xml
+++ b/rule-engine/pom.xml
@@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
- <version>2.0.1</version>
+ <version>2.0.2</version>
<artifactId>thingsboard</artifactId>
</parent>
<artifactId>rule-engine</artifactId>
rule-engine/rule-engine-api/pom.xml 2(+1 -1)
diff --git a/rule-engine/rule-engine-api/pom.xml b/rule-engine/rule-engine-api/pom.xml
index a1c33fa..367a5e2 100644
--- a/rule-engine/rule-engine-api/pom.xml
+++ b/rule-engine/rule-engine-api/pom.xml
@@ -22,7 +22,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
- <version>2.0.1</version>
+ <version>2.0.2</version>
<artifactId>rule-engine</artifactId>
</parent>
<groupId>org.thingsboard.rule-engine</groupId>
diff --git a/rule-engine/rule-engine-components/pom.xml b/rule-engine/rule-engine-components/pom.xml
index 3983268..a561f0e 100644
--- a/rule-engine/rule-engine-components/pom.xml
+++ b/rule-engine/rule-engine-components/pom.xml
@@ -22,7 +22,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
- <version>2.0.1</version>
+ <version>2.0.2</version>
<artifactId>rule-engine</artifactId>
</parent>
<groupId>org.thingsboard.rule-engine</groupId>
diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java
index dc16a1e..6b05b7c 100644
--- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java
+++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java
@@ -111,8 +111,9 @@ public class TbMqttNode implements TbNode {
if (!StringUtils.isEmpty(this.config.getClientId())) {
config.setClientId(this.config.getClientId());
}
+ config.setCleanSession(this.config.isCleanSession());
this.config.getCredentials().configure(config);
- MqttClient client = MqttClient.create(config);
+ MqttClient client = MqttClient.create(config, null);
client.setEventLoop(this.eventLoopGroup);
Future<MqttConnectResult> connectFuture = client.connect(this.config.getHost(), this.config.getPort());
MqttConnectResult result;
diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNodeConfiguration.java
index f63e201..d1e000f 100644
--- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNodeConfiguration.java
+++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNodeConfiguration.java
@@ -30,6 +30,7 @@ public class TbMqttNodeConfiguration implements NodeConfiguration<TbMqttNodeConf
private int connectTimeoutSec;
private String clientId;
+ private boolean cleanSession;
private boolean ssl;
private MqttClientCredentials credentials;
@@ -40,6 +41,7 @@ public class TbMqttNodeConfiguration implements NodeConfiguration<TbMqttNodeConf
configuration.setHost("localhost");
configuration.setPort(1883);
configuration.setConnectTimeoutSec(10);
+ configuration.setCleanSession(true);
configuration.setSsl(false);
configuration.setCredentials(new AnonymousCredentials());
return configuration;
diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNode.java
index 7cc08bf..5d7e124 100644
--- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNode.java
+++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNode.java
@@ -16,7 +16,10 @@
package org.thingsboard.rule.engine.rpc;
import com.datastax.driver.core.utils.UUIDs;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.JsonNode;
import com.google.gson.Gson;
+import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import lombok.extern.slf4j.Slf4j;
@@ -35,6 +38,7 @@ import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.msg.TbMsg;
+import java.io.IOException;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@@ -86,10 +90,18 @@ public class TbSendRPCRequestNode implements TbNode {
tmp = msg.getMetaData().getValue("expirationTime");
long expirationTime = !StringUtils.isEmpty(tmp) ? Long.parseLong(tmp) : (System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(config.getTimeoutInSeconds()));
+ String params;
+ JsonElement paramsEl = json.get("params");
+ if (paramsEl.isJsonPrimitive()) {
+ params = paramsEl.getAsString();
+ } else {
+ params = gson.toJson(paramsEl);
+ }
+
RuleEngineDeviceRpcRequest request = RuleEngineDeviceRpcRequest.builder()
.oneway(oneway)
.method(json.get("method").getAsString())
- .body(gson.toJson(json.get("params")))
+ .body(params)
.deviceId(new DeviceId(msg.getOriginator().getId()))
.requestId(requestId)
.requestUUID(requestUUID)
diff --git a/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js b/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js
index e5056b3..c5e057e 100644
--- a/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js
+++ b/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js
@@ -1,4 +1,4 @@
-!function(e){function t(a){if(n[a])return n[a].exports;var r=n[a]={exports:{},id:a,loaded:!1};return e[a].call(r.exports,r,r.exports,t),r.loaded=!0,r.exports}var n={};return t.m=e,t.c=n,t.p="/static/",t(0)}(function(e){for(var t in e)if(Object.prototype.hasOwnProperty.call(e,t))switch(typeof e[t]){case"function":break;case"object":e[t]=function(t){var n=t.slice(1),a=e[t[0]];return function(e,t,r){a.apply(this,[e,t,r].concat(n))}}(e[t]);break;default:e[t]=e[e[t]]}return e}([function(e,t,n){e.exports=n(72)},function(e,t){},1,1,1,function(e,t){e.exports=' <section ng-form name=attributesConfigForm layout=column> <md-input-container class=md-block> <label translate>attribute.attributes-scope</label> <md-select ng-model=configuration.scope ng-disabled=$root.loading> <md-option ng-repeat="scope in types.attributesScope" ng-value=scope.value> {{scope.name | translate}} </md-option> </md-select> </md-input-container> </section> '},function(e,t){e.exports=" <section class=tb-alarm-config ng-form name=alarmConfigForm layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.alarm-details-builder</label> <tb-js-func ng-model=configuration.alarmDetailsBuildJs function-name=Details function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row style=padding-bottom:15px> <md-button ng-click=testDetailsBuildJs($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-details-function' | translate }} </md-button> </div> <md-input-container class=md-block> <label translate>tb.rulenode.alarm-type</label> <input ng-required=true name=alarmType ng-model=configuration.alarmType> <div ng-messages=alarmConfigForm.alarmType.$error> <div ng-message=required translate>tb.rulenode.alarm-type-required</div> </div> </md-input-container> </section> "},function(e,t){e.exports=" <section class=tb-alarm-config ng-form name=alarmConfigForm layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.alarm-details-builder</label> <tb-js-func ng-model=configuration.alarmDetailsBuildJs function-name=Details function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row style=padding-bottom:15px> <md-button ng-click=testDetailsBuildJs($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-details-function' | translate }} </md-button> </div> <section layout=column layout-gt-sm=row> <md-input-container flex class=md-block> <label translate>tb.rulenode.alarm-type</label> <input ng-required=true name=alarmType ng-model=configuration.alarmType> <div ng-messages=alarmConfigForm.alarmType.$error> <div ng-message=required translate>tb.rulenode.alarm-type-required</div> </div> </md-input-container> <md-input-container flex class=md-block> <label translate>tb.rulenode.alarm-severity</label> <md-select required name=severity ng-model=configuration.severity> <md-option ng-repeat=\"(severityKey, severity) in types.alarmSeverity\" ng-value=severityKey> {{ severity.name | translate}} </md-option> </md-select> <div ng-messages=alarmConfigForm.severity.$error> <div ng-message=required translate>tb.rulenode.alarm-severity-required</div> </div> </md-input-container> </section> <md-checkbox aria-label=\"{{ 'tb.rulenode.propagate' | translate }}\" ng-model=configuration.propagate>{{ 'tb.rulenode.propagate' | translate }} </md-checkbox> </section> "},function(e,t){e.exports=" <section class=tb-generator-config ng-form name=generatorConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.message-count</label> <input ng-required=true type=number step=1 name=messageCount ng-model=configuration.msgCount min=0> <div ng-messages=generatorConfigForm.messageCount.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.message-count-required</div> <div ng-message=min translate>tb.rulenode.min-message-count-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.period-seconds</label> <input ng-required=true type=number step=1 name=periodInSeconds ng-model=configuration.periodInSeconds min=1> <div ng-messages=generatorConfigForm.periodInSeconds.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.period-seconds-required</div> <div ng-message=min translate>tb.rulenode.min-period-seconds-message</div> </div> </md-input-container> <div layout=column> <label class=tb-small>{{ 'tb.rulenode.originator' | translate }}</label> <tb-entity-select the-form=generatorConfigForm tb-required=false ng-model=originator> </tb-entity-select> </div> <label translate class=\"tb-title no-padding\">tb.rulenode.generate</label> <tb-js-func ng-model=configuration.jsScript function-name=Generate function-args=\"{{ ['prevMsg', 'prevMetadata', 'prevMsgType'] }}\" no-validate=true> </tb-js-func> <div layout=row> <md-button ng-click=testScript($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-generator-function' | translate }} </md-button> </div> </section> "},function(e,t){e.exports=' <section ng-form name=kafkaConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.topic-pattern</label> <input ng-required=true name=topicPattern ng-model=configuration.topicPattern> <div ng-messages=kafkaConfigForm.topicPattern.$error> <div ng-message=required translate>tb.rulenode.topic-pattern-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.bootstrap-servers</label> <input ng-required=true name=bootstrapServers ng-model=configuration.bootstrapServers> <div ng-messages=kafkaConfigForm.bootstrapServers.$error> <div ng-message=required translate>tb.rulenode.bootstrap-servers-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.retries</label> <input type=number step=1 name=retries ng-model=configuration.retries min=0> <div ng-messages=kafkaConfigForm.retries.$error> <div ng-message=min translate>tb.rulenode.min-retries-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.batch-size-bytes</label> <input type=number step=1 name=batchSize ng-model=configuration.batchSize min=0> <div ng-messages=kafkaConfigForm.batchSize.$error> <div ng-message=min translate>tb.rulenode.min-batch-size-bytes-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.linger-ms</label> <input type=number step=1 name=linger ng-model=configuration.linger min=0> <div ng-messages=kafkaConfigForm.linger.$error> <div ng-message=min translate>tb.rulenode.min-linger-ms-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.buffer-memory-bytes</label> <input type=number step=1 name=bufferMemory ng-model=configuration.bufferMemory min=0> <div ng-messages=kafkaConfigForm.bufferMemory.$error> <div ng-message=min translate>tb.rulenode.min-buffer-memory-bytes-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.acks</label> <md-select ng-model=configuration.acks ng-disabled=$root.loading> <md-option ng-repeat="ackValue in ackValues" ng-value=ackValue> {{ ackValue }} </md-option> </md-select> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.key-serializer</label> <input ng-required=true name=keySerializer ng-model=configuration.keySerializer> <div ng-messages=kafkaConfigForm.keySerializer.$error> <div ng-message=required translate>tb.rulenode.key-serializer-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.value-serializer</label> <input ng-required=true name=valueSerializer ng-model=configuration.valueSerializer> <div ng-messages=kafkaConfigForm.valueSerializer.$error> <div ng-message=required translate>tb.rulenode.value-serializer-required</div> </div> </md-input-container> <label translate class=tb-title>tb.rulenode.other-properties</label> <tb-kv-map-config ng-model=configuration.otherProperties ng-required=false key-text="\'tb.rulenode.key\'" key-required-text="\'tb.rulenode.key-required\'" val-text="\'tb.rulenode.value\'" val-required-text="\'tb.rulenode.value-required\'"> </tb-kv-map-config> </section> '},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.to-string</label> <tb-js-func ng-model=configuration.jsScript function-name=ToString function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row> <md-button ng-click=testScript($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-to-string-function' | translate }} </md-button> </div> </section> "},function(e,t){e.exports=' <section class=tb-mqtt-config ng-form name=mqttConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.topic-pattern</label> <input ng-required=true name=topicPattern ng-model=configuration.topicPattern> <div ng-messages=mqttConfigForm.topicPattern.$error> <div translate ng-message=required>tb.rulenode.topic-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.mqtt-topic-pattern-hint</div> </md-input-container> <div flex layout=column layout-gt-sm=row> <md-input-container flex=60 class=md-block> <label translate>tb.rulenode.host</label> <input ng-required=true name=host ng-model=configuration.host> <div ng-messages=mqttConfigForm.host.$error> <div translate ng-message=required>tb.rulenode.host-required</div> </div> </md-input-container> <md-input-container flex=40 class=md-block> <label translate>tb.rulenode.port</label> <input type=number step=1 min=1 max=65535 ng-required=true name=port ng-model=configuration.port> <div ng-messages=mqttConfigForm.port.$error> <div translate ng-message=required>tb.rulenode.port-required</div> <div translate ng-message=min>tb.rulenode.port-range</div> <div translate ng-message=max>tb.rulenode.port-range</div> </div> </md-input-container> <md-input-container flex=40 class=md-block> <label translate>tb.rulenode.connect-timeout</label> <input type=number step=1 min=1 max=200 ng-required=true name=connectTimeoutSec ng-model=configuration.connectTimeoutSec> <div ng-messages=mqttConfigForm.connectTimeoutSec.$error> <div translate ng-message=required>tb.rulenode.connect-timeout-required</div> <div translate ng-message=min>tb.rulenode.connect-timeout-range</div> <div translate ng-message=max>tb.rulenode.connect-timeout-range</div> </div> </md-input-container> </div> <md-input-container class=md-block> <label translate>tb.rulenode.client-id</label> <input name=clientId ng-model=configuration.clientId> </md-input-container> <md-checkbox ng-disabled="$root.loading || readonly" aria-label="{{ \'tb.rulenode.enable-ssl\' | translate }}" ng-model=configuration.ssl> {{ \'tb.rulenode.enable-ssl\' | translate }} </md-checkbox> <md-expansion-panel-group class=tb-credentials-panel-group ng-class="{\'disabled\': $root.loading || readonly}" md-component-id=credentialsPanelGroup> <md-expansion-panel md-component-id=credentialsPanel> <md-expansion-panel-collapsed> <div class=tb-panel-title>{{ \'tb.rulenode.credentials\' | translate }}</div> <div class=tb-panel-prompt>{{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}</div> <span flex></span> <md-expansion-panel-icon></md-expansion-panel-icon> </md-expansion-panel-collapsed> <md-expansion-panel-expanded> <md-expansion-panel-header ng-click="$mdExpansionPanel(\'credentialsPanel\').collapse()"> <div class=tb-panel-title>{{ \'tb.rulenode.credentials\' | translate }}</div> <div class=tb-panel-prompt>{{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}</div> <span flex></span> <md-expansion-panel-icon></md-expansion-panel-icon> </md-expansion-panel-header> <md-expansion-panel-content> <div layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.credentials-type</label> <md-select ng-required=true name=credentialsType ng-model=configuration.credentials.type ng-disabled="$root.loading || readonly" ng-change=credentialsTypeChanged()> <md-option ng-repeat="(credentialsType, credentialsValue) in ruleNodeTypes.mqttCredentialTypes" ng-value=credentialsValue.value> {{credentialsValue.name | translate}} </md-option> </md-select> <div ng-messages=mqttConfigForm.credentialsType.$error> <div translate ng-message=required>tb.rulenode.credentials-type-required</div> </div> </md-input-container> <section flex layout=column ng-if="configuration.credentials.type == ruleNodeTypes.mqttCredentialTypes.basic.value"> <md-input-container class=md-block> <label translate>tb.rulenode.username</label> <input ng-required=true name=mqttUsername ng-model=configuration.credentials.username> <div ng-messages=mqttConfigForm.mqttUsername.$error> <div translate ng-message=required>tb.rulenode.username-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.password</label> <input type=password ng-required=true name=mqttPassword ng-model=configuration.credentials.password> <div ng-messages=mqttConfigForm.mqttPassword.$error> <div translate ng-message=required>tb.rulenode.password-required</div> </div> </md-input-container> </section> <section flex layout=column ng-if="configuration.credentials.type == ruleNodeTypes.mqttCredentialTypes[\'cert.PEM\'].value" class=dropdown-section> <div class=tb-container ng-class="configuration.credentials.caCertFileName ? \'ng-valid\' : \'ng-invalid\'"> <label class=tb-label translate>tb.rulenode.ca-cert</label> <div flow-init={singleFile:true} flow-file-added="certFileAdded($file, \'caCert\')" class=tb-file-select-container> <div class=tb-file-clear-container> <md-button ng-click="clearCertFile(\'caCert\')" class="tb-file-clear-btn md-icon-button md-primary" aria-label="{{ \'action.remove\' | translate }}"> <md-tooltip md-direction=top> {{ \'action.remove\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.remove\' | translate }}" class=material-icons>close</md-icon> </md-button> </div> <div class="alert tb-flow-drop" flow-drop> <label for=caCertSelect translate>tb.rulenode.drop-file</label> <input class=file-input flow-btn id=caCertSelect> </div> </div> </div> <div class=dropdown-messages> <div ng-if=!configuration.credentials.caCertFileName class=tb-error-message translate>tb.rulenode.no-file</div> <div ng-if=configuration.credentials.caCertFileName>{{configuration.credentials.caCertFileName}}</div> </div> <div class=tb-container ng-class="configuration.credentials.certFileName ? \'ng-valid\' : \'ng-invalid\'"> <label class=tb-label translate>tb.rulenode.cert</label> <div flow-init={singleFile:true} flow-file-added="certFileAdded($file, \'Cert\')" class=tb-file-select-container> <div class=tb-file-clear-container> <md-button ng-click="clearCertFile(\'Cert\')" class="tb-file-clear-btn md-icon-button md-primary" aria-label="{{ \'action.remove\' | translate }}"> <md-tooltip md-direction=top> {{ \'action.remove\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.remove\' | translate }}" class=material-icons>close</md-icon> </md-button> </div> <div class="alert tb-flow-drop" flow-drop> <label for=CertSelect translate>tb.rulenode.drop-file</label> <input class=file-input flow-btn id=CertSelect> </div> </div> </div> <div class=dropdown-messages> <div ng-if=!configuration.credentials.certFileName class=tb-error-message translate>tb.rulenode.no-file</div> <div ng-if=configuration.credentials.certFileName>{{configuration.credentials.certFileName}}</div> </div> <div class=tb-container ng-class="configuration.credentials.privateKeyFileName ? \'ng-valid\' : \'ng-invalid\'"> <label class=tb-label translate>tb.rulenode.private-key</label> <div flow-init={singleFile:true} flow-file-added="certFileAdded($file, \'privateKey\')" class=tb-file-select-container> <div class=tb-file-clear-container> <md-button ng-click="clearCertFile(\'privateKey\')" class="tb-file-clear-btn md-icon-button md-primary" aria-label="{{ \'action.remove\' | translate }}"> <md-tooltip md-direction=top> {{ \'action.remove\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.remove\' | translate }}" class=material-icons>close</md-icon> </md-button> </div> <div class="alert tb-flow-drop" flow-drop> <label for=privateKeySelect translate>tb.rulenode.drop-file</label> <input class=file-input flow-btn id=privateKeySelect> </div> </div> </div> <div class=dropdown-messages> <div ng-if=!configuration.credentials.privateKeyFileName class=tb-error-message translate>tb.rulenode.no-file</div> <div ng-if=configuration.credentials.privateKeyFileName>{{configuration.credentials.privateKeyFileName}}</div> </div> <md-input-container class=md-block> <label translate>tb.rulenode.private-key-password</label> <input type=password name=privateKeyPassword ng-model=configuration.credentials.password> </md-input-container> </section> </div> </md-expansion-panel-content> </md-expansion-panel-expanded> </md-expansion-panel> </md-expansion-panel-group> </section>'},function(e,t){e.exports=' <section ng-form name=rabbitMqConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.exchange-name-pattern</label> <input name=exchangeNamePattern ng-model=configuration.exchangeNamePattern> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.routing-key-pattern</label> <input name=routingKeyPattern ng-model=configuration.routingKeyPattern> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.message-properties</label> <md-select ng-model=configuration.messageProperties ng-disabled="$root.loading || readonly"> <md-option ng-repeat="property in messageProperties" ng-value=property> {{ property }} </md-option> </md-select> </md-input-container> <div layout-gt-sm=row> <md-input-container class=md-block flex=100 flex-gt-sm=60> <label translate>tb.rulenode.host</label> <input ng-required=true name=host ng-model=configuration.host> <div ng-messages=rabbitMqConfigForm.host.$error> <div ng-message=required translate>tb.rulenode.host-required</div> </div> </md-input-container> <md-input-container class=md-block flex=100 flex-gt-sm=40> <label translate>tb.rulenode.port</label> <input ng-required=true type=number step=1 name=port ng-model=configuration.port min=0 max=65535> <div ng-messages=rabbitMqConfigForm.port.$error> <div ng-message=required translate>tb.rulenode.port-required</div> <div ng-message=min translate>tb.rulenode.port-range</div> <div ng-message=max translate>tb.rulenode.port-range</div> </div> </md-input-container> </div> <md-input-container class=md-block> <label translate>tb.rulenode.virtual-host</label> <input name=virtualHost ng-model=configuration.virtualHost> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.username</label> <input name=virtualHost ng-model=configuration.username> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.password</label> <input name=virtualHost type=password ng-model=configuration.password> </md-input-container> <md-input-container class=md-block> <md-checkbox ng-disabled="$root.loading || readonly" aria-label="{{ \'tb.rulenode.automatic-recovery\' | translate }}" ng-model=ruleNode.automaticRecoveryEnabled>{{ \'tb.rulenode.automatic-recovery\' | translate }} </md-checkbox> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.connection-timeout-ms</label> <input type=number step=1 name=connectionTimeout ng-model=configuration.connectionTimeout min=0> <div ng-messages=rabbitMqConfigForm.connectionTimeout.$error> <div ng-message=min translate>tb.rulenode.min-connection-timeout-ms-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.handshake-timeout-ms</label> <input type=number step=1 name=handshakeTimeout ng-model=configuration.handshakeTimeout min=0> <div ng-messages=rabbitMqConfigForm.handshakeTimeout.$error> <div ng-message=min translate>tb.rulenode.min-handshake-timeout-ms-message</div> </div> </md-input-container> <label translate class=tb-title>tb.rulenode.client-properties</label> <tb-kv-map-config ng-model=configuration.clientProperties ng-required=false key-text="\'tb.rulenode.key\'" key-required-text="\'tb.rulenode.key-required\'" val-text="\'tb.rulenode.value\'" val-required-text="\'tb.rulenode.value-required\'"> </tb-kv-map-config> </section> '},function(e,t){e.exports=' <section ng-form name=restApiCallConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.endpoint-url-pattern</label> <input ng-required=true name=endpointUrlPattern ng-model=configuration.restEndpointUrlPattern> <div ng-messages=restApiCallConfigForm.endpointUrlPattern.$error> <div ng-message=required translate>tb.rulenode.endpoint-url-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.endpoint-url-pattern-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.request-method</label> <md-select ng-model=configuration.requestMethod ng-disabled=$root.loading> <md-option ng-repeat="type in ruleNodeTypes.httpRequestType" ng-value=type> {{ type }} </md-option> </md-select> </md-input-container> <label translate class=tb-title>tb.rulenode.headers</label> <div class=tb-hint translate>tb.rulenode.headers-hint</div> <tb-kv-map-config ng-model=configuration.headers ng-required=false key-text="\'tb.rulenode.header\'" key-required-text="\'tb.rulenode.header-required\'" val-text="\'tb.rulenode.value\'" val-required-text="\'tb.rulenode.value-required\'"> </tb-kv-map-config> </section> '},function(e,t){e.exports=" <section ng-form name=rpcReplyConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.request-id-metadata-attribute</label> <input name=requestIdMetaDataAttribute ng-model=configuration.requestIdMetaDataAttribute> </md-input-container> </section> "},function(e,t){e.exports=" <section ng-form name=rpcRequestConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.timeout-sec</label> <input ng-required=true type=number step=1 name=timeoutInSeconds ng-model=configuration.timeoutInSeconds min=0> <div ng-messages=rpcRequestConfigForm.timeoutInSeconds.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.timeout-required</div> <div ng-message=min translate>tb.rulenode.min-timeout-message</div> </div> </md-input-container> </section> "},function(e,t){e.exports=' <section ng-form name=sendEmailConfigForm layout=column> <md-checkbox ng-disabled="$root.loading || readonly" aria-label="{{ \'tb.rulenode.use-system-smtp-settings\' | translate }}" ng-model=configuration.useSystemSmtpSettings> {{ \'tb.rulenode.use-system-smtp-settings\' | translate }} </md-checkbox> <section layout=column ng-if=!configuration.useSystemSmtpSettings> <md-input-container class=md-block> <label translate>tb.rulenode.smtp-protocol</label> <md-select ng-disabled="$root.loading || readonly" ng-model=configuration.smtpProtocol> <md-option ng-repeat="smtpProtocol in smtpProtocols" value={{smtpProtocol}}> {{smtpProtocol.toUpperCase()}} </md-option> </md-select> </md-input-container> <div layout-gt-sm=row> <md-input-container class=md-block flex=100 flex-gt-sm=60> <label translate>tb.rulenode.smtp-host</label> <input ng-required=true name=smtpHost ng-model=configuration.smtpHost> <div ng-messages=sendEmailConfigForm.smtpHost.$error> <div translate ng-message=required>tb.rulenode.smtp-host-required</div> </div> </md-input-container> <md-input-container class=md-block flex=100 flex-gt-sm=40> <label translate>tb.rulenode.smtp-port</label> <input type=number step=1 min=1 max=65535 ng-required=true name=port ng-model=configuration.smtpPort> <div ng-messages=sendEmailConfigForm.port.$error> <div translate ng-message=required>tb.rulenode.smtp-port-required</div> <div translate ng-message=min>tb.rulenode.smtp-port-range</div> <div translate ng-message=max>tb.rulenode.smtp-port-range</div> </div> </md-input-container> </div> <md-input-container class=md-block> <label translate>tb.rulenode.timeout-msec</label> <input type=number step=1 min=0 ng-required=true name=timeout ng-model=configuration.timeout> <div ng-messages=sendEmailConfigForm.timeout.$error> <div translate ng-message=required>tb.rulenode.timeout-required</div> <div translate ng-message=min>tb.rulenode.min-timeout-msec-message</div> </div> </md-input-container> <md-checkbox ng-disabled="$root.loading || readonly" aria-label="{{ \'tb.rulenode.enable-tls\' | translate }}" ng-model=configuration.enableTls>{{ \'tb.rulenode.enable-tls\' | translate }}</md-checkbox> <md-input-container class=md-block> <label translate>tb.rulenode.username</label> <input name=username placeholder="{{ \'tb.rulenode.enter-username\' | translate }}" ng-model=configuration.username> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.password</label> <input name=password placeholder="{{ \'tb.rulenode.enter-password\' | translate }}" type=password ng-model=configuration.password> </md-input-container> </section> </section> '},function(e,t){e.exports=" <section ng-form name=snsConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.topic-arn-pattern</label> <input ng-required=true name=topicArnPattern ng-model=configuration.topicArnPattern> <div ng-messages=snsConfigForm.topicArnPattern.$error> <div ng-message=required translate>tb.rulenode.topic-arn-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.topic-arn-pattern-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.aws-access-key-id</label> <input ng-required=true name=accessKeyId ng-model=configuration.accessKeyId> <div ng-messages=snsConfigForm.accessKeyId.$error> <div ng-message=required translate>tb.rulenode.aws-access-key-id-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.aws-secret-access-key</label> <input ng-required=true name=secretAccessKey ng-model=configuration.secretAccessKey> <div ng-messages=snsConfigForm.secretAccessKey.$error> <div ng-message=required translate>tb.rulenode.aws-secret-access-key-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.aws-region</label> <input ng-required=true name=region ng-model=configuration.region> <div ng-messages=snsConfigForm.region.$error> <div ng-message=required translate>tb.rulenode.aws-region-required</div> </div> </md-input-container> </section> "},function(e,t){e.exports=' <section ng-form name=sqsConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.queue-type</label> <md-select ng-model=configuration.queueType ng-disabled="$root.loading || readonly"> <md-option ng-repeat="type in ruleNodeTypes.sqsQueueType" ng-value=type.value> {{ type.name | translate }} </md-option> </md-select> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.queue-url-pattern</label> <input ng-required=true name=queueUrlPattern ng-model=configuration.queueUrlPattern> <div ng-messages=sqsConfigForm.queueUrlPattern.$error> <div ng-message=required translate>tb.rulenode.queue-url-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.queue-url-pattern-hint</div> </md-input-container> <md-input-container class=md-block ng-if="configuration.queueType == ruleNodeTypes.sqsQueueType.STANDARD.value"> <label translate>tb.rulenode.delay-seconds</label> <input type=number step=1 name=delaySeconds ng-model=configuration.delaySeconds min=0 max=900> <div ng-messages=sqsConfigForm.delaySeconds.$error> <div ng-message=min translate>tb.rulenode.min-delay-seconds-message</div> <div ng-message=max translate>tb.rulenode.max-delay-seconds-message</div> </div> </md-input-container> <label translate class=tb-title>tb.rulenode.message-attributes</label> <div class=tb-hint translate>tb.rulenode.message-attributes-hint</div> <tb-kv-map-config ng-model=configuration.messageAttributes ng-required=false key-text="\'tb.rulenode.name\'" key-required-text="\'tb.rulenode.name-required\'" val-text="\'tb.rulenode.value\'" val-required-text="\'tb.rulenode.value-required\'"> </tb-kv-map-config> <md-input-container class=md-block> <label translate>tb.rulenode.aws-access-key-id</label> <input ng-required=true name=accessKeyId ng-model=configuration.accessKeyId> <div ng-messages=snsConfigForm.accessKeyId.$error> <div ng-message=required translate>tb.rulenode.aws-access-key-id-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.aws-secret-access-key</label> <input ng-required=true name=secretAccessKey ng-model=configuration.secretAccessKey> <div ng-messages=snsConfigForm.secretAccessKey.$error> <div ng-message=required translate>tb.rulenode.aws-secret-access-key-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.aws-region</label> <input ng-required=true name=region ng-model=configuration.region> <div ng-messages=snsConfigForm.region.$error> <div ng-message=required translate>tb.rulenode.aws-region-required</div> </div> </md-input-container> </section> '},function(e,t){e.exports=" <section ng-form name=timeseriesConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.default-ttl</label> <input ng-required=true type=number step=1 name=defaultTTL ng-model=configuration.defaultTTL min=0> <div ng-messages=timeseriesConfigForm.defaultTTL.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.default-ttl-required</div> <div ng-message=min translate>tb.rulenode.min-default-ttl-message</div> </div> </md-input-container> </section> "},function(e,t){e.exports=' <section layout=column> <div layout=row> <md-input-container class=md-block style=min-width:100px> <label translate>relation.direction</label> <md-select required ng-model=query.direction> <md-option ng-repeat="direction in types.entitySearchDirection" ng-value=direction> {{ (\'relation.search-direction.\' + direction) | translate}} </md-option> </md-select> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.max-relation-level</label> <input name=maxRelationLevel type=number min=1 step=1 placeholder="{{ \'tb.rulenode.unlimited-level\' | translate }}" ng-model=query.maxLevel aria-label="{{ \'tb.rulenode.max-relation-level\' | translate }}"> </md-input-container> </div> <div class=md-caption style=color:rgba(0,0,0,.57) translate>relation.relation-type</div> <tb-relation-type-autocomplete flex hide-label ng-model=query.relationType tb-required=false> </tb-relation-type-autocomplete> <div class="md-caption tb-required" style=color:rgba(0,0,0,.57) translate>device.device-types</div> <tb-entity-subtype-list tb-required=true entity-type=types.entityType.device ng-model=query.deviceTypes> </tb-entity-subtype-list> </section> '},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title tb-required\">tb.rulenode.attr-mapping</label> <md-checkbox aria-label=\"{{ 'tb.rulenode.latest-telemetry' | translate }}\" ng-model=configuration.telemetry>{{ 'tb.rulenode.latest-telemetry' | translate }} </md-checkbox> <tb-kv-map-config ng-model=configuration.attrMapping ng-required=true required-text=\"'tb.rulenode.attr-mapping-required'\" key-text=\"configuration.telemetry ? 'tb.rulenode.source-telemetry' : 'tb.rulenode.source-attribute'\" key-required-text=\"configuration.telemetry ? 'tb.rulenode.source-telemetry-required' : 'tb.rulenode.source-attribute-required'\" val-text=\"'tb.rulenode.target-attribute'\" val-required-text=\"'tb.rulenode.target-attribute-required'\"> </tb-kv-map-config> </section> ";
-},function(e,t){e.exports=' <section layout=column> <label translate class="tb-title tb-required">tb.rulenode.device-relations-query</label> <tb-device-relations-query-config style=padding-bottom:15px ng-model=configuration.deviceRelationsQuery> </tb-device-relations-query-config> <label translate class="tb-title no-padding">tb.rulenode.client-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.clientAttributeNames placeholder="{{\'tb.rulenode.client-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.shared-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.sharedAttributeNames placeholder="{{\'tb.rulenode.shared-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.server-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.serverAttributeNames placeholder="{{\'tb.rulenode.server-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.latest-timeseries</label> <md-chips ng-required=false readonly=readonly ng-model=configuration.latestTsKeyNames placeholder="{{\'tb.rulenode.latest-timeseries\' | translate}}" md-separator-keys=separatorKeys> </md-chips> </section> '},function(e,t){e.exports=' <section layout=column> <label translate class="tb-title no-padding">tb.rulenode.client-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.clientAttributeNames placeholder="{{\'tb.rulenode.client-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.shared-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.sharedAttributeNames placeholder="{{\'tb.rulenode.shared-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.server-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.serverAttributeNames placeholder="{{\'tb.rulenode.server-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.latest-timeseries</label> <md-chips ng-required=false readonly=readonly ng-model=configuration.latestTsKeyNames placeholder="{{\'tb.rulenode.latest-timeseries\' | translate}}" md-separator-keys=separatorKeys> </md-chips> </section> '},function(e,t){e.exports=' <section layout=column> <label translate class="tb-title tb-required">tb.rulenode.fields-mapping</label> <tb-kv-map-config ng-model=configuration.fieldsMapping ng-required=true required-text="\'tb.rulenode.fields-mapping-required\'" key-text="\'tb.rulenode.source-field\'" key-required-text="\'tb.rulenode.source-field-required\'" val-text="\'tb.rulenode.target-attribute\'" val-required-text="\'tb.rulenode.target-attribute-required\'"> </tb-kv-map-config> </section> '},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title tb-required\">tb.rulenode.relations-query</label> <tb-relations-query-config style=padding-bottom:15px ng-model=configuration.relationsQuery> </tb-relations-query-config> <label translate class=\"tb-title tb-required\">tb.rulenode.attr-mapping</label> <md-checkbox aria-label=\"{{ 'tb.rulenode.latest-telemetry' | translate }}\" ng-model=configuration.telemetry>{{ 'tb.rulenode.latest-telemetry' | translate }} </md-checkbox> <tb-kv-map-config ng-model=configuration.attrMapping ng-required=true required-text=\"'tb.rulenode.attr-mapping-required'\" key-text=\"configuration.telemetry ? 'tb.rulenode.source-telemetry' : 'tb.rulenode.source-attribute'\" key-required-text=\"configuration.telemetry ? 'tb.rulenode.source-telemetry-required' : 'tb.rulenode.source-attribute-required'\" val-text=\"'tb.rulenode.target-attribute'\" val-required-text=\"'tb.rulenode.target-attribute-required'\"> </tb-kv-map-config> </section> "},21,function(e,t){e.exports=" <section ng-form name=checkRelationConfigForm> <md-input-container class=md-block style=min-width:100px> <label translate>relation.direction</label> <md-select required ng-model=configuration.direction> <md-option ng-repeat=\"direction in types.entitySearchDirection\" ng-value=direction> {{ ('relation.search-direction.' + direction) | translate}} </md-option> </md-select> </md-input-container> <div layout=row class=tb-entity-select> <tb-entity-type-select style=min-width:100px the-form=checkRelationConfigForm tb-required=true ng-model=configuration.entityType> </tb-entity-type-select> <tb-entity-autocomplete flex ng-if=configuration.entityType the-form=checkRelationConfigForm tb-required=true entity-type=configuration.entityType ng-model=configuration.entityId> </tb-entity-autocomplete> </div> <tb-relation-type-autocomplete hide-label ng-model=configuration.relationType tb-required=true> </tb-relation-type-autocomplete> </section> "},function(e,t){e.exports=' <section layout=column> <label translate class="tb-title no-padding" ng-class="{\'tb-required\': required}">tb.rulenode.message-types-filter</label> <md-chips id=message_type_chips ng-required=required readonly=readonly ng-model=messageTypes md-autocomplete-snap md-transform-chip=transformMessageTypeChip($chip) md-require-match=false> <md-autocomplete id=message_type md-no-cache=true md-selected-item=selectedMessageType md-search-text=messageTypeSearchText md-items="item in messageTypesSearch(messageTypeSearchText)" md-item-text=item.name md-min-length=0 placeholder="{{\'tb.rulenode.message-type\' | translate }}" md-menu-class=tb-message-type-autocomplete> <span md-highlight-text=messageTypeSearchText md-highlight-flags=^i>{{item}}</span> <md-not-found> <div class=tb-not-found> <div class=tb-no-entries ng-if="!messageTypeSearchText || !messageTypeSearchText.length"> <span translate>tb.rulenode.no-message-types-found</span> </div> <div ng-if="messageTypeSearchText && messageTypeSearchText.length"> <span translate translate-values=\'{ messageType: "{{messageTypeSearchText | truncate:true:6:'...'}}" }\'>tb.rulenode.no-message-type-matching</span> <span> <a translate ng-click="createMessageType($event, \'#message_type_chips\')">tb.rulenode.create-new-message-type</a> </span> </div> </div> </md-not-found> </md-autocomplete> <md-chip-template> <span>{{$chip.name}}</span> </md-chip-template> </md-chips> <div class=tb-error-messages ng-messages=ngModelCtrl.$error role=alert> <div translate ng-message=messageTypes class=tb-error-message>tb.rulenode.message-types-required</div> </div> </section>'},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.filter</label> <tb-js-func ng-model=configuration.jsScript function-name=Filter function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row> <md-button ng-click=testScript($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-filter-function' | translate }} </md-button> </div> </section> "},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.switch</label> <tb-js-func ng-model=configuration.jsScript function-name=Switch function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row> <md-button ng-click=testScript($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-switch-function' | translate }} </md-button> </div> </section> "},function(e,t){e.exports=' <section class=tb-kv-map-config layout=column> <div class=header flex layout=row> <span class=cell flex translate>{{ keyText }}</span> <span class=cell flex translate>{{ valText }}</span> <span ng-show=!disabled style=width:52px> </span> </div> <div class=body> <div class=row ng-form name=kvForm flex layout=row layout-align="start center" ng-repeat="keyVal in kvList track by $index"> <md-input-container class="cell md-block" flex md-no-float> <input placeholder="{{ keyText | translate }}" ng-required=true name=key ng-model=keyVal.key> <div ng-messages=kvForm.key.$error> <div translate ng-message=required>{{keyRequiredText}}</div> </div> </md-input-container> <md-input-container class="cell md-block" flex md-no-float> <input placeholder="{{ valText | translate }}" ng-required=true name=value ng-model=keyVal.value> <div ng-messages=kvForm.value.$error> <div translate ng-message=required>{{valRequiredText}}</div> </div> </md-input-container> <md-button ng-show=!disabled ng-disabled=loading class="md-icon-button md-primary" ng-click=removeKeyVal($index) aria-label="{{ \'action.remove\' | translate }}"> <md-tooltip md-direction=top> {{ \'tb.key-val.remove-entry\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.delete\' | translate }}" class=material-icons> close </md-icon> </md-button> </div> </div> <div class=tb-error-messages ng-messages=ngModelCtrl.$error role=alert> <div translate ng-message=kvMap class=tb-error-message>{{requiredText}}</div> </div> <div> <md-button ng-show=!disabled ng-disabled=loading class="md-primary md-raised" ng-click=addKeyVal() aria-label="{{ \'action.add\' | translate }}"> <md-tooltip md-direction=top> {{ \'tb.key-val.add-entry\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.add\' | translate }}" class=material-icons> add </md-icon> {{ \'action.add\' | translate }} </md-button> </div> </section> '},function(e,t){e.exports=" <section layout=column> <div layout=row> <md-input-container class=md-block style=min-width:100px> <label translate>relation.direction</label> <md-select required ng-model=query.direction> <md-option ng-repeat=\"direction in types.entitySearchDirection\" ng-value=direction> {{ ('relation.search-direction.' + direction) | translate}} </md-option> </md-select> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.max-relation-level</label> <input name=maxRelationLevel type=number min=1 step=1 placeholder=\"{{ 'tb.rulenode.unlimited-level' | translate }}\" ng-model=query.maxLevel aria-label=\"{{ 'tb.rulenode.max-relation-level' | translate }}\"> </md-input-container> </div> <div class=md-caption style=padding-bottom:10px;color:rgba(0,0,0,.57) translate>relation.relation-filters</div> <tb-relation-filters ng-model=query.filters> </tb-relation-filters> </section> "},function(e,t){e.exports=' <section layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.originator-source</label> <md-select required ng-model=configuration.originatorSource> <md-option ng-repeat="source in ruleNodeTypes.originatorSource" ng-value=source.value> {{ source.name | translate}} </md-option> </md-select> </md-input-container> <section layout=column ng-if="configuration.originatorSource == ruleNodeTypes.originatorSource.RELATED.value"> <label translate class="tb-title tb-required">tb.rulenode.relations-query</label> <tb-relations-query-config style=padding-bottom:15px ng-model=configuration.relationsQuery> </tb-relations-query-config> </section> </section> '},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.transform</label> <tb-js-func ng-model=configuration.jsScript function-name=Transform function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row style=padding-bottom:15px> <md-button ng-click=testScript($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-transformer-function' | translate }} </md-button> </div> </section> "},function(e,t){e.exports=" <section ng-form name=toEmailConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.from-template</label> <textarea ng-required=true name=fromTemplate ng-model=configuration.fromTemplate rows=2></textarea> <div ng-messages=toEmailConfigForm.fromTemplate.$error> <div ng-message=required translate>tb.rulenode.from-template-required</div> </div> <div class=tb-hint translate>tb.rulenode.from-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.to-template</label> <textarea ng-required=true name=toTemplate ng-model=configuration.toTemplate rows=2></textarea> <div ng-messages=toEmailConfigForm.toTemplate.$error> <div ng-message=required translate>tb.rulenode.to-template-required</div> </div> <div class=tb-hint translate>tb.rulenode.mail-address-list-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.cc-template</label> <textarea name=ccTemplate ng-model=configuration.ccTemplate rows=2></textarea> <div class=tb-hint translate>tb.rulenode.mail-address-list-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.bcc-template</label> <textarea name=ccTemplate ng-model=configuration.bccTemplate rows=2></textarea> <div class=tb-hint translate>tb.rulenode.mail-address-list-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.subject-template</label> <textarea ng-required=true name=subjectTemplate ng-model=configuration.subjectTemplate rows=2></textarea> <div ng-messages=toEmailConfigForm.subjectTemplate.$error> <div ng-message=required translate>tb.rulenode.subject-template-required</div> </div> <div class=tb-hint translate>tb.rulenode.subject-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.body-template</label> <textarea ng-required=true name=bodyTemplate ng-model=configuration.bodyTemplate rows=6></textarea> <div ng-messages=toEmailConfigForm.bodyTemplate.$error> <div ng-message=required translate>tb.rulenode.body-template-required</div> </div> <div class=tb-hint translate>tb.rulenode.body-template-hint</div> </md-input-container> </section> "},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(5),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n,a){var r=function(r,i,l,s){var u=o.default;i.html(u),r.types=n,r.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(r.configuration)}),s.$render=function(){r.configuration=s.$viewValue},r.testDetailsBuildJs=function(e){var n=angular.copy(r.configuration.alarmDetailsBuildJs);a.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],r.ruleNodeId).then(function(e){r.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(i.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:r}}r.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(6),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n,a){var r=function(r,i,l,s){var u=o.default;i.html(u),r.types=n,r.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(r.configuration)}),s.$render=function(){r.configuration=s.$viewValue},r.testDetailsBuildJs=function(e){var n=angular.copy(r.configuration.alarmDetailsBuildJs);a.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],r.ruleNodeId).then(function(e){r.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(i.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:r}}r.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(7),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n,a){var r=function(r,i,l,s){var u=o.default;i.html(u),r.types=n,r.originator=null,r.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(r.configuration)}),s.$render=function(){r.configuration=s.$viewValue,r.configuration.originatorId&&r.configuration.originatorType?r.originator={id:r.configuration.originatorId,entityType:r.configuration.originatorType}:r.originator=null,r.$watch("originator",function(e,t){angular.equals(e,t)||(r.originator?(s.$viewValue.originatorId=r.originator.id,s.$viewValue.originatorType=r.originator.entityType):(s.$viewValue.originatorId=null,s.$viewValue.originatorType=null))},!0)},r.testScript=function(e){var n=angular.copy(r.configuration.jsScript);a.testNodeScript(e,n,"generate",t.instant("tb.rulenode.generator")+"","Generate",["prevMsg","prevMetadata","prevMsgType"],r.ruleNodeId).then(function(e){r.configuration.jsScript=e,s.$setDirty()})},e(i.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:r}}r.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r,n(1);var i=n(8),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(51),i=a(r),o=n(36),l=a(o),s=n(39),u=a(s),d=n(38),c=a(d),m=n(37),p=a(m),g=n(42),f=a(g),b=n(46),v=a(b),y=n(47),q=a(y),h=n(45),T=a(h),$=n(41),k=a($),w=n(49),C=a(w),_=n(50),x=a(_),E=n(44),M=a(E),S=n(43),N=a(S),V=n(48),P=a(V);t.default=angular.module("thingsboard.ruleChain.config.action",[]).directive("tbActionNodeTimeseriesConfig",i.default).directive("tbActionNodeAttributesConfig",l.default).directive("tbActionNodeGeneratorConfig",u.default).directive("tbActionNodeCreateAlarmConfig",c.default).directive("tbActionNodeClearAlarmConfig",p.default).directive("tbActionNodeLogConfig",f.default).directive("tbActionNodeRpcReplyConfig",v.default).directive("tbActionNodeRpcRequestConfig",q.default).directive("tbActionNodeRestApiCallConfig",T.default).directive("tbActionNodeKafkaConfig",k.default).directive("tbActionNodeSnsConfig",C.default).directive("tbActionNodeSqsConfig",x.default).directive("tbActionNodeRabbitMqConfig",M.default).directive("tbActionNodeMqttConfig",N.default).directive("tbActionNodeSendEmailConfig",P.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.ackValues=["all","-1","0","1"],t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(9),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s),a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},a.testScript=function(e){var r=angular.copy(a.configuration.jsScript);n.testNodeScript(e,r,"string",t.instant("tb.rulenode.to-string")+"","ToString",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,l.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}r.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(10),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s),a.$mdExpansionPanel=t,a.ruleNodeTypes=n,a.credentialsTypeChanged=function(){var e=a.configuration.credentials.type;a.configuration.credentials={},a.configuration.credentials.type=e,a.updateValidity()},a.certFileAdded=function(e,t){var n=new FileReader;n.onload=function(n){a.$apply(function(){if(n.target.result){l.$setDirty();var r=n.target.result;r&&r.length>0&&("caCert"==t&&(a.configuration.credentials.caCertFileName=e.name,a.configuration.credentials.caCert=r),"privateKey"==t&&(a.configuration.credentials.privateKeyFileName=e.name,a.configuration.credentials.privateKey=r),"Cert"==t&&(a.configuration.credentials.certFileName=e.name,a.configuration.credentials.cert=r)),a.updateValidity()}})},n.readAsText(e.file)},a.clearCertFile=function(e){l.$setDirty(),"caCert"==e&&(a.configuration.credentials.caCertFileName=null,a.configuration.credentials.caCert=null),"privateKey"==e&&(a.configuration.credentials.privateKeyFileName=null,a.configuration.credentials.privateKey=null),"Cert"==e&&(a.configuration.credentials.certFileName=null,a.configuration.credentials.cert=null),a.updateValidity()},a.updateValidity=function(){var e=!0,t=a.configuration.credentials;t.type==n.mqttCredentialTypes["cert.PEM"].value&&(t.caCert&&t.cert&&t.privateKey||(e=!1)),l.$setValidity("Certs",e)},a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:a}}r.$inject=["$compile","$mdExpansionPanel","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r,n(2);var i=n(11),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.messageProperties=[null,"BASIC","TEXT_PLAIN","MINIMAL_BASIC","MINIMAL_PERSISTENT_BASIC","PERSISTENT_BASIC","PERSISTENT_TEXT_PLAIN"],t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(12),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(13),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(14),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(15),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.smtpProtocols=["smtp","smtps"],t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(16),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(17),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}r.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(18),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(19),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||i.$setViewValue(n.query)}),i.$render=function(){n.query=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(20),o=a(i)},function(e,t){"use strict";function n(e){var t=function(t,n,a,r){n.html("<div></div>"),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}n.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(21),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(22),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(57),i=a(r),o=n(58),l=a(o),s=n(55),u=a(s),d=n(59),c=a(d),m=n(54),p=a(m),g=n(60),f=a(g);t.default=angular.module("thingsboard.ruleChain.config.enrichment",[]).directive("tbEnrichmentNodeOriginatorAttributesConfig",i.default).directive("tbEnrichmentNodeOriginatorFieldsConfig",l.default).directive("tbEnrichmentNodeDeviceAttributesConfig",u.default).directive("tbEnrichmentNodeRelatedAttributesConfig",c.default).directive("tbEnrichmentNodeCustomerAttributesConfig",p.default).directive("tbEnrichmentNodeTenantAttributesConfig",f.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(23),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(24),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(25),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(26),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(27),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(64),i=a(r),o=n(63),l=a(o),s=n(65),u=a(s),d=n(61),c=a(d);t.default=angular.module("thingsboard.ruleChain.config.filter",[]).directive("tbFilterNodeScriptConfig",i.default).directive("tbFilterNodeMessageTypeConfig",l.default).directive("tbFilterNodeSwitchConfig",u.default).directive("tbFilterNodeCheckRelationConfig",c.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){function s(){if(l.$viewValue){for(var e=[],t=0;t<a.messageTypes.length;t++)e.push(a.messageTypes[t].value);l.$viewValue.messageTypes=e,u()}}function u(){if(a.required){var e=!(!l.$viewValue.messageTypes||!l.$viewValue.messageTypes.length);l.$setValidity("messageTypes",e)}else l.$setValidity("messageTypes",!0)}var d=o.default;r.html(d),a.selectedMessageType=null,a.messageTypeSearchText=null,a.ngModelCtrl=l;var c=[];for(var m in n.messageType){var p={name:n.messageType[m].name,value:n.messageType[m].value};c.push(p)}a.transformMessageTypeChip=function(e){
-var n,a=t("filter")(c,{name:e},!0);return n=a&&a.length?angular.copy(a[0]):{name:e,value:e}},a.messageTypesSearch=function(e){var n=e?t("filter")(c,{name:e}):c;return n.map(function(e){return e.name})},a.createMessageType=function(e,t){var n=angular.element(t,r)[0].firstElementChild,a=angular.element(n),i=a.scope().$mdChipsCtrl.getChipBuffer();e.preventDefault(),e.stopPropagation(),a.scope().$mdChipsCtrl.appendChip(i.trim()),a.scope().$mdChipsCtrl.resetChipBuffer()},l.$render=function(){var e=l.$viewValue,t=[];if(e&&e.messageTypes)for(var r=0;r<e.messageTypes.length;r++){var i=e.messageTypes[r];n.messageType[i]?t.push(angular.copy(n.messageType[i])):t.push({name:i,value:i})}a.messageTypes=t,a.$watch("messageTypes",function(e,t){angular.equals(e,t)||s()},!0)},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",readonly:"=ngReadonly"},link:a}}r.$inject=["$compile","$filter","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r,n(3);var i=n(28),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s),a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},a.testScript=function(e){var r=angular.copy(a.configuration.jsScript);n.testNodeScript(e,r,"filter",t.instant("tb.rulenode.filter")+"","Filter",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,l.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}r.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(29),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s),a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},a.testScript=function(e){var r=angular.copy(a.configuration.jsScript);n.testNodeScript(e,r,"switch",t.instant("tb.rulenode.switch")+"","Switch",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,l.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}r.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(30),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){function i(e){e>-1&&t.kvList.splice(e,1)}function l(){t.kvList||(t.kvList=[]),t.kvList.push({key:"",value:""})}function s(){var e={};t.kvList.forEach(function(t){t.key&&(e[t.key]=t.value)}),r.$setViewValue(e),u()}function u(){var e=!0;t.required&&!t.kvList.length&&(e=!1),r.$setValidity("kvMap",e)}var d=o.default;n.html(d),t.ngModelCtrl=r,t.removeKeyVal=i,t.addKeyVal=l,t.kvList=[],t.$watch("query",function(e,n){angular.equals(e,n)||r.$setViewValue(t.query)}),r.$render=function(){if(r.$viewValue){var e=r.$viewValue;t.kvList.length=0;for(var n in e)t.kvList.push({key:n,value:e[n]})}t.$watch("kvList",function(e,t){angular.equals(e,t)||s()},!0),u()},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",disabled:"=ngDisabled",requiredText:"=",keyText:"=",keyRequiredText:"=",valText:"=",valRequiredText:"="},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(31),o=a(i);n(4)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||i.$setViewValue(n.query)}),i.$render=function(){n.query=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(32),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(33),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(68),i=a(r),o=n(70),l=a(o),s=n(71),u=a(s);t.default=angular.module("thingsboard.ruleChain.config.transform",[]).directive("tbTransformationNodeChangeOriginatorConfig",i.default).directive("tbTransformationNodeScriptConfig",l.default).directive("tbTransformationNodeToEmailConfig",u.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s),a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},a.testScript=function(e){var r=angular.copy(a.configuration.jsScript);n.testNodeScript(e,r,"update",t.instant("tb.rulenode.transformer")+"","Transform",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,l.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}r.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(34),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(35),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(75),i=a(r),o=n(62),l=a(o),s=n(56),u=a(s),d=n(69),c=a(d),m=n(40),p=a(m),g=n(53),f=a(g),b=n(67),v=a(b),y=n(52),q=a(y),h=n(66),T=a(h),$=n(74),k=a($);t.default=angular.module("thingsboard.ruleChain.config",[i.default,l.default,u.default,c.default,p.default]).directive("tbNodeEmptyConfig",f.default).directive("tbRelationsQueryConfig",v.default).directive("tbDeviceRelationsQueryConfig",q.default).directive("tbKvMapConfig",T.default).config(k.default).name},function(e,t){"use strict";function n(e){var t={tb:{rulenode:{filter:"Filter",switch:"Switch","message-type":"Message type","message-type-required":"Message type is required.","message-types-filter":"Message types filter","no-message-types-found":"No message types found","no-message-type-matching":"'{{messageType}}' not found.","create-new-message-type":"Create a new one!","message-types-required":"Message types are required.","client-attributes":"Client attributes","shared-attributes":"Shared attributes","server-attributes":"Server attributes","latest-timeseries":"Latest timeseries","relations-query":"Relations query","device-relations-query":"Device relations query","max-relation-level":"Max relation level","unlimited-level":"Unlimited level","latest-telemetry":"Latest telemetry","attr-mapping":"Attributes mapping","source-attribute":"Source attribute","source-attribute-required":"Source attribute is required.","source-telemetry":"Source telemetry","source-telemetry-required":"Source telemetry is required.","target-attribute":"Target attribute","target-attribute-required":"Target attribute is required.","attr-mapping-required":"At least one attribute mapping should be specified.","fields-mapping":"Fields mapping","fields-mapping-required":"At least one field mapping should be specified.","source-field":"Source field","source-field-required":"Source field is required.","originator-source":"Originator source","originator-customer":"Customer","originator-tenant":"Tenant","originator-related":"Related","clone-message":"Clone message",transform:"Transform","default-ttl":"Default TTL in seconds","default-ttl-required":"Default TTL is required.","min-default-ttl-message":"Only 0 minimum TTL is allowed.","message-count":"Message count (0 - unlimited)","message-count-required":"Message count is required.","min-message-count-message":"Only 0 minimum message count is allowed.","period-seconds":"Period in seconds","period-seconds-required":"Period is required.","min-period-seconds-message":"Only 1 second minimum period is allowed.",originator:"Originator","message-body":"Message body","message-metadata":"Message metadata",generate:"Generate","test-generator-function":"Test generator function",generator:"Generator","test-filter-function":"Test filter function","test-switch-function":"Test switch function","test-transformer-function":"Test transformer function",transformer:"Transformer","alarm-create-condition":"Alarm create condition","test-condition-function":"Test condition function","alarm-clear-condition":"Alarm clear condition","alarm-details-builder":"Alarm details builder","test-details-function":"Test details function","alarm-type":"Alarm type","alarm-type-required":"Alarm type is required.","alarm-severity":"Alarm severity","alarm-severity-required":"Alarm severity is required",propagate:"Propagate",condition:"Condition",details:"Details","to-string":"To string","test-to-string-function":"Test to string function","from-template":"From Template","from-template-required":"From Template is required","from-template-hint":"From address template, use <code>${metaKeyName}</code> to substitute variables from metadata","to-template":"To Template","to-template-required":"To Template is required","mail-address-list-template-hint":"Comma separated address list, use <code>${metaKeyName}</code> to substitute variables from metadata","cc-template":"Cc Template","bcc-template":"Bcc Template","subject-template":"Subject Template","subject-template-required":"Subject Template is required","subject-template-hint":"Mail subject template, use <code>${metaKeyName}</code> to substitute variables from metadata","body-template":"Body Template","body-template-required":"Body Template is required","body-template-hint":"Mail body template, use <code>${metaKeyName}</code> to substitute variables from metadata","request-id-metadata-attribute":"Request Id Metadata attribute name","timeout-sec":"Timeout in seconds","timeout-required":"Timeout is required","min-timeout-message":"Only 0 minimum timeout value is allowed.","endpoint-url-pattern":"Endpoint URL pattern","endpoint-url-pattern-required":"Endpoint URL pattern is required","endpoint-url-pattern-hint":"HTTP URL address pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","request-method":"Request method",headers:"Headers","headers-hint":"Use <code>${metaKeyName}</code> in header/value fields to substitute variables from metadata",header:"Header","header-required":"Header is required",value:"Value","value-required":"Value is required","topic-pattern":"Topic pattern","topic-pattern-required":"Topic pattern is required","mqtt-topic-pattern-hint":"MQTT topic pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","bootstrap-servers":"Bootstrap servers","bootstrap-servers-required":"Bootstrap servers value is required","other-properties":"Other properties",key:"Key","key-required":"Key is required",retries:"Automatically retry times if fails","min-retries-message":"Only 0 minimum retries is allowed.","batch-size-bytes":"Produces batch size in bytes","min-batch-size-bytes-message":"Only 0 minimum batch size is allowed.","linger-ms":"Time to buffer locally (ms)","min-linger-ms-message":"Only 0 ms minimum value is allowed.","buffer-memory-bytes":"Client buffer max size in bytes","min-buffer-memory-message":"Only 0 minimum buffer size is allowed.",acks:"Number of acknowledgments","key-serializer":"Key serializer","key-serializer-required":"Key serializer is required","value-serializer":"Value serializer","value-serializer-required":"Value serializer is required","topic-arn-pattern":"Topic ARN pattern","topic-arn-pattern-required":"Topic ARN pattern is required","topic-arn-pattern-hint":"Topic ARN pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","aws-access-key-id":"AWS Access Key ID","aws-access-key-id-required":"AWS Access Key ID is required","aws-secret-access-key":"AWS Secret Access Key","aws-secret-access-key-required":"AWS Secret Access Key is required","aws-region":"AWS Region","aws-region-required":"AWS Region is required","exchange-name-pattern":"Exchange name pattern","routing-key-pattern":"Routing key pattern","message-properties":"Message properties",host:"Host","host-required":"Host is required",port:"Port","port-required":"Port is required","port-range":"Port should be in a range from 1 to 65535.","virtual-host":"Virtual host",username:"Username",password:"Password","automatic-recovery":"Automatic recovery","connection-timeout-ms":"Connection timeout (ms)","min-connection-timeout-ms-message":"Only 0 ms minimum value is allowed.","handshake-timeout-ms":"Handshake timeout (ms)","min-handshake-timeout-ms-message":"Only 0 ms minimum value is allowed.","client-properties":"Client properties","queue-url-pattern":"Queue URL pattern","queue-url-pattern-required":"Queue URL pattern is required","queue-url-pattern-hint":"Queue URL pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","delay-seconds":"Delay (seconds)","min-delay-seconds-message":"Only 0 seconds minimum value is allowed.","max-delay-seconds-message":"Only 900 seconds maximum value is allowed.",name:"Name","name-required":"Name is required","queue-type":"Queue type","sqs-queue-standard":"Standard","sqs-queue-fifo":"FIFO","message-attributes":"Message attributes","message-attributes-hint":"Use <code>${metaKeyName}</code> in name/value fields to substitute variables from metadata","connect-timeout":"Connection timeout (sec)","connect-timeout-required":"Connection timeout is required.","connect-timeout-range":"Connection timeout should be in a range from 1 to 200.","client-id":"Client ID","enable-ssl":"Enable SSL",credentials:"Credentials","credentials-type":"Credentials type","credentials-type-required":"Credentials type is required.","credentials-anonymous":"Anonymous","credentials-basic":"Basic","credentials-pem":"PEM","username-required":"Username is required.","password-required":"Password is required.","ca-cert":"CA certificate file *","private-key":"Private key file *",cert:"Certificate file *","no-file":"No file selected.","drop-file":"Drop a file or click to select a file to upload.","private-key-password":"Private key password","use-system-smtp-settings":"Use system SMTP settings","smtp-protocol":"Protocol","smtp-host":"SMTP host","smtp-host-required":"SMTP host is required.","smtp-port":"SMTP port","smtp-port-required":"You must supply a smtp port.","smtp-port-range":"SMTP port should be in a range from 1 to 65535.","timeout-msec":"Timeout ms","min-timeout-msec-message":"Only 0 ms minimum value is allowed.","enter-username":"Enter username","enter-password":"Enter password","enable-tls":"Enable TLS"},"key-val":{key:"Key",value:"Value","remove-entry":"Remove entry","add-entry":"Add entry"}}};angular.merge(e.en_US,t)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){(0,o.default)(t);for(var n in t){var a=t[n];e.translations(n,a)}}r.$inject=["$translateProvider","locales"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(73),o=a(i)},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=angular.module("thingsboard.ruleChain.config.types",[]).constant("ruleNodeTypes",{messageType:{POST_ATTRIBUTES_REQUEST:{name:"Post attributes",value:"POST_ATTRIBUTES_REQUEST"},POST_TELEMETRY_REQUEST:{name:"Post telemetry",value:"POST_TELEMETRY_REQUEST"},TO_SERVER_RPC_REQUEST:{name:"RPC Request from Device",value:"TO_SERVER_RPC_REQUEST"},RPC_CALL_FROM_SERVER_TO_DEVICE:{name:"RPC Request to Device",value:"RPC_CALL_FROM_SERVER_TO_DEVICE"},ACTIVITY_EVENT:{name:"Activity Event",value:"ACTIVITY_EVENT"},INACTIVITY_EVENT:{name:"Inactivity Event",value:"INACTIVITY_EVENT"},CONNECT_EVENT:{name:"Connect Event",value:"CONNECT_EVENT"},DISCONNECT_EVENT:{name:"Disconnect Event",value:"DISCONNECT_EVENT"},ENTITY_CREATED:{name:"Entity Created",value:"ENTITY_CREATED"},ENTITY_UPDATED:{name:"Entity Updated",value:"ENTITY_UPDATED"},ENTITY_DELETED:{name:"Entity Deleted",value:"ENTITY_DELETED"},ENTITY_ASSIGNED:{name:"Entity Assigned",value:"ENTITY_ASSIGNED"},ENTITY_UNASSIGNED:{name:"Entity Unassigned",value:"ENTITY_UNASSIGNED"},ATTRIBUTES_UPDATED:{name:"Attributes Updated",value:"ATTRIBUTES_UPDATED"},ATTRIBUTES_DELETED:{name:"Attributes Deleted",value:"ATTRIBUTES_DELETED"}},originatorSource:{CUSTOMER:{name:"tb.rulenode.originator-customer",value:"CUSTOMER"},TENANT:{name:"tb.rulenode.originator-tenant",value:"TENANT"},RELATED:{name:"tb.rulenode.originator-related",value:"RELATED"}},httpRequestType:["GET","POST","PUT","DELETE"],sqsQueueType:{STANDARD:{name:"tb.rulenode.sqs-queue-standard",value:"STANDARD"},FIFO:{name:"tb.rulenode.sqs-queue-fifo",value:"FIFO"}},mqttCredentialTypes:{anonymous:{value:"anonymous",name:"tb.rulenode.credentials-anonymous"},basic:{value:"basic",name:"tb.rulenode.credentials-basic"},"cert.PEM":{value:"cert.PEM",name:"tb.rulenode.credentials-pem"}}}).name}]));
+!function(e){function t(a){if(n[a])return n[a].exports;var r=n[a]={exports:{},id:a,loaded:!1};return e[a].call(r.exports,r,r.exports,t),r.loaded=!0,r.exports}var n={};return t.m=e,t.c=n,t.p="/static/",t(0)}(function(e){for(var t in e)if(Object.prototype.hasOwnProperty.call(e,t))switch(typeof e[t]){case"function":break;case"object":e[t]=function(t){var n=t.slice(1),a=e[t[0]];return function(e,t,r){a.apply(this,[e,t,r].concat(n))}}(e[t]);break;default:e[t]=e[e[t]]}return e}([function(e,t,n){e.exports=n(72)},function(e,t){},1,1,1,function(e,t){e.exports=' <section ng-form name=attributesConfigForm layout=column> <md-input-container class=md-block> <label translate>attribute.attributes-scope</label> <md-select ng-model=configuration.scope ng-disabled=$root.loading> <md-option ng-repeat="scope in types.attributesScope" ng-value=scope.value> {{scope.name | translate}} </md-option> </md-select> </md-input-container> </section> '},function(e,t){e.exports=" <section class=tb-alarm-config ng-form name=alarmConfigForm layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.alarm-details-builder</label> <tb-js-func ng-model=configuration.alarmDetailsBuildJs function-name=Details function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row style=padding-bottom:15px> <md-button ng-click=testDetailsBuildJs($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-details-function' | translate }} </md-button> </div> <md-input-container class=md-block> <label translate>tb.rulenode.alarm-type</label> <input ng-required=true name=alarmType ng-model=configuration.alarmType> <div ng-messages=alarmConfigForm.alarmType.$error> <div ng-message=required translate>tb.rulenode.alarm-type-required</div> </div> </md-input-container> </section> "},function(e,t){e.exports=" <section class=tb-alarm-config ng-form name=alarmConfigForm layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.alarm-details-builder</label> <tb-js-func ng-model=configuration.alarmDetailsBuildJs function-name=Details function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row style=padding-bottom:15px> <md-button ng-click=testDetailsBuildJs($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-details-function' | translate }} </md-button> </div> <section layout=column layout-gt-sm=row> <md-input-container flex class=md-block> <label translate>tb.rulenode.alarm-type</label> <input ng-required=true name=alarmType ng-model=configuration.alarmType> <div ng-messages=alarmConfigForm.alarmType.$error> <div ng-message=required translate>tb.rulenode.alarm-type-required</div> </div> </md-input-container> <md-input-container flex class=md-block> <label translate>tb.rulenode.alarm-severity</label> <md-select required name=severity ng-model=configuration.severity> <md-option ng-repeat=\"(severityKey, severity) in types.alarmSeverity\" ng-value=severityKey> {{ severity.name | translate}} </md-option> </md-select> <div ng-messages=alarmConfigForm.severity.$error> <div ng-message=required translate>tb.rulenode.alarm-severity-required</div> </div> </md-input-container> </section> <md-checkbox aria-label=\"{{ 'tb.rulenode.propagate' | translate }}\" ng-model=configuration.propagate>{{ 'tb.rulenode.propagate' | translate }} </md-checkbox> </section> "},function(e,t){e.exports=" <section class=tb-generator-config ng-form name=generatorConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.message-count</label> <input ng-required=true type=number step=1 name=messageCount ng-model=configuration.msgCount min=0> <div ng-messages=generatorConfigForm.messageCount.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.message-count-required</div> <div ng-message=min translate>tb.rulenode.min-message-count-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.period-seconds</label> <input ng-required=true type=number step=1 name=periodInSeconds ng-model=configuration.periodInSeconds min=1> <div ng-messages=generatorConfigForm.periodInSeconds.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.period-seconds-required</div> <div ng-message=min translate>tb.rulenode.min-period-seconds-message</div> </div> </md-input-container> <div layout=column> <label class=tb-small>{{ 'tb.rulenode.originator' | translate }}</label> <tb-entity-select the-form=generatorConfigForm tb-required=false ng-model=originator> </tb-entity-select> </div> <label translate class=\"tb-title no-padding\">tb.rulenode.generate</label> <tb-js-func ng-model=configuration.jsScript function-name=Generate function-args=\"{{ ['prevMsg', 'prevMetadata', 'prevMsgType'] }}\" no-validate=true> </tb-js-func> <div layout=row> <md-button ng-click=testScript($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-generator-function' | translate }} </md-button> </div> </section> "},function(e,t){e.exports=' <section ng-form name=kafkaConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.topic-pattern</label> <input ng-required=true name=topicPattern ng-model=configuration.topicPattern> <div ng-messages=kafkaConfigForm.topicPattern.$error> <div ng-message=required translate>tb.rulenode.topic-pattern-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.bootstrap-servers</label> <input ng-required=true name=bootstrapServers ng-model=configuration.bootstrapServers> <div ng-messages=kafkaConfigForm.bootstrapServers.$error> <div ng-message=required translate>tb.rulenode.bootstrap-servers-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.retries</label> <input type=number step=1 name=retries ng-model=configuration.retries min=0> <div ng-messages=kafkaConfigForm.retries.$error> <div ng-message=min translate>tb.rulenode.min-retries-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.batch-size-bytes</label> <input type=number step=1 name=batchSize ng-model=configuration.batchSize min=0> <div ng-messages=kafkaConfigForm.batchSize.$error> <div ng-message=min translate>tb.rulenode.min-batch-size-bytes-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.linger-ms</label> <input type=number step=1 name=linger ng-model=configuration.linger min=0> <div ng-messages=kafkaConfigForm.linger.$error> <div ng-message=min translate>tb.rulenode.min-linger-ms-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.buffer-memory-bytes</label> <input type=number step=1 name=bufferMemory ng-model=configuration.bufferMemory min=0> <div ng-messages=kafkaConfigForm.bufferMemory.$error> <div ng-message=min translate>tb.rulenode.min-buffer-memory-bytes-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.acks</label> <md-select ng-model=configuration.acks ng-disabled=$root.loading> <md-option ng-repeat="ackValue in ackValues" ng-value=ackValue> {{ ackValue }} </md-option> </md-select> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.key-serializer</label> <input ng-required=true name=keySerializer ng-model=configuration.keySerializer> <div ng-messages=kafkaConfigForm.keySerializer.$error> <div ng-message=required translate>tb.rulenode.key-serializer-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.value-serializer</label> <input ng-required=true name=valueSerializer ng-model=configuration.valueSerializer> <div ng-messages=kafkaConfigForm.valueSerializer.$error> <div ng-message=required translate>tb.rulenode.value-serializer-required</div> </div> </md-input-container> <label translate class=tb-title>tb.rulenode.other-properties</label> <tb-kv-map-config ng-model=configuration.otherProperties ng-required=false key-text="\'tb.rulenode.key\'" key-required-text="\'tb.rulenode.key-required\'" val-text="\'tb.rulenode.value\'" val-required-text="\'tb.rulenode.value-required\'"> </tb-kv-map-config> </section> '},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.to-string</label> <tb-js-func ng-model=configuration.jsScript function-name=ToString function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row> <md-button ng-click=testScript($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-to-string-function' | translate }} </md-button> </div> </section> "},function(e,t){e.exports=' <section class=tb-mqtt-config ng-form name=mqttConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.topic-pattern</label> <input ng-required=true name=topicPattern ng-model=configuration.topicPattern> <div ng-messages=mqttConfigForm.topicPattern.$error> <div translate ng-message=required>tb.rulenode.topic-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.mqtt-topic-pattern-hint</div> </md-input-container> <div flex layout=column layout-gt-sm=row> <md-input-container flex=60 class=md-block> <label translate>tb.rulenode.host</label> <input ng-required=true name=host ng-model=configuration.host> <div ng-messages=mqttConfigForm.host.$error> <div translate ng-message=required>tb.rulenode.host-required</div> </div> </md-input-container> <md-input-container flex=40 class=md-block> <label translate>tb.rulenode.port</label> <input type=number step=1 min=1 max=65535 ng-required=true name=port ng-model=configuration.port> <div ng-messages=mqttConfigForm.port.$error> <div translate ng-message=required>tb.rulenode.port-required</div> <div translate ng-message=min>tb.rulenode.port-range</div> <div translate ng-message=max>tb.rulenode.port-range</div> </div> </md-input-container> <md-input-container flex=40 class=md-block> <label translate>tb.rulenode.connect-timeout</label> <input type=number step=1 min=1 max=200 ng-required=true name=connectTimeoutSec ng-model=configuration.connectTimeoutSec> <div ng-messages=mqttConfigForm.connectTimeoutSec.$error> <div translate ng-message=required>tb.rulenode.connect-timeout-required</div> <div translate ng-message=min>tb.rulenode.connect-timeout-range</div> <div translate ng-message=max>tb.rulenode.connect-timeout-range</div> </div> </md-input-container> </div> <md-input-container class=md-block> <label translate>tb.rulenode.client-id</label> <input name=clientId ng-model=configuration.clientId> </md-input-container> <md-checkbox ng-disabled="$root.loading || readonly" aria-label="{{ \'tb.rulenode.clean-session\' | translate }}" ng-model=configuration.cleanSession> {{ \'tb.rulenode.clean-session\' | translate }} </md-checkbox> <md-checkbox ng-disabled="$root.loading || readonly" aria-label="{{ \'tb.rulenode.enable-ssl\' | translate }}" ng-model=configuration.ssl> {{ \'tb.rulenode.enable-ssl\' | translate }} </md-checkbox> <md-expansion-panel-group class=tb-credentials-panel-group ng-class="{\'disabled\': $root.loading || readonly}" md-component-id=credentialsPanelGroup> <md-expansion-panel md-component-id=credentialsPanel> <md-expansion-panel-collapsed> <div class=tb-panel-title>{{ \'tb.rulenode.credentials\' | translate }}</div> <div class=tb-panel-prompt>{{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}</div> <span flex></span> <md-expansion-panel-icon></md-expansion-panel-icon> </md-expansion-panel-collapsed> <md-expansion-panel-expanded> <md-expansion-panel-header ng-click="$mdExpansionPanel(\'credentialsPanel\').collapse()"> <div class=tb-panel-title>{{ \'tb.rulenode.credentials\' | translate }}</div> <div class=tb-panel-prompt>{{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}</div> <span flex></span> <md-expansion-panel-icon></md-expansion-panel-icon> </md-expansion-panel-header> <md-expansion-panel-content> <div layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.credentials-type</label> <md-select ng-required=true name=credentialsType ng-model=configuration.credentials.type ng-disabled="$root.loading || readonly" ng-change=credentialsTypeChanged()> <md-option ng-repeat="(credentialsType, credentialsValue) in ruleNodeTypes.mqttCredentialTypes" ng-value=credentialsValue.value> {{credentialsValue.name | translate}} </md-option> </md-select> <div ng-messages=mqttConfigForm.credentialsType.$error> <div translate ng-message=required>tb.rulenode.credentials-type-required</div> </div> </md-input-container> <section flex layout=column ng-if="configuration.credentials.type == ruleNodeTypes.mqttCredentialTypes.basic.value"> <md-input-container class=md-block> <label translate>tb.rulenode.username</label> <input ng-required=true name=mqttUsername ng-model=configuration.credentials.username> <div ng-messages=mqttConfigForm.mqttUsername.$error> <div translate ng-message=required>tb.rulenode.username-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.password</label> <input type=password ng-required=true name=mqttPassword ng-model=configuration.credentials.password> <div ng-messages=mqttConfigForm.mqttPassword.$error> <div translate ng-message=required>tb.rulenode.password-required</div> </div> </md-input-container> </section> <section flex layout=column ng-if="configuration.credentials.type == ruleNodeTypes.mqttCredentialTypes[\'cert.PEM\'].value" class=dropdown-section> <div class=tb-container ng-class="configuration.credentials.caCertFileName ? \'ng-valid\' : \'ng-invalid\'"> <label class=tb-label translate>tb.rulenode.ca-cert</label> <div flow-init={singleFile:true} flow-file-added="certFileAdded($file, \'caCert\')" class=tb-file-select-container> <div class=tb-file-clear-container> <md-button ng-click="clearCertFile(\'caCert\')" class="tb-file-clear-btn md-icon-button md-primary" aria-label="{{ \'action.remove\' | translate }}"> <md-tooltip md-direction=top> {{ \'action.remove\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.remove\' | translate }}" class=material-icons>close</md-icon> </md-button> </div> <div class="alert tb-flow-drop" flow-drop> <label for=caCertSelect translate>tb.rulenode.drop-file</label> <input class=file-input flow-btn id=caCertSelect> </div> </div> </div> <div class=dropdown-messages> <div ng-if=!configuration.credentials.caCertFileName class=tb-error-message translate>tb.rulenode.no-file</div> <div ng-if=configuration.credentials.caCertFileName>{{configuration.credentials.caCertFileName}}</div> </div> <div class=tb-container ng-class="configuration.credentials.certFileName ? \'ng-valid\' : \'ng-invalid\'"> <label class=tb-label translate>tb.rulenode.cert</label> <div flow-init={singleFile:true} flow-file-added="certFileAdded($file, \'Cert\')" class=tb-file-select-container> <div class=tb-file-clear-container> <md-button ng-click="clearCertFile(\'Cert\')" class="tb-file-clear-btn md-icon-button md-primary" aria-label="{{ \'action.remove\' | translate }}"> <md-tooltip md-direction=top> {{ \'action.remove\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.remove\' | translate }}" class=material-icons>close</md-icon> </md-button> </div> <div class="alert tb-flow-drop" flow-drop> <label for=CertSelect translate>tb.rulenode.drop-file</label> <input class=file-input flow-btn id=CertSelect> </div> </div> </div> <div class=dropdown-messages> <div ng-if=!configuration.credentials.certFileName class=tb-error-message translate>tb.rulenode.no-file</div> <div ng-if=configuration.credentials.certFileName>{{configuration.credentials.certFileName}}</div> </div> <div class=tb-container ng-class="configuration.credentials.privateKeyFileName ? \'ng-valid\' : \'ng-invalid\'"> <label class=tb-label translate>tb.rulenode.private-key</label> <div flow-init={singleFile:true} flow-file-added="certFileAdded($file, \'privateKey\')" class=tb-file-select-container> <div class=tb-file-clear-container> <md-button ng-click="clearCertFile(\'privateKey\')" class="tb-file-clear-btn md-icon-button md-primary" aria-label="{{ \'action.remove\' | translate }}"> <md-tooltip md-direction=top> {{ \'action.remove\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.remove\' | translate }}" class=material-icons>close</md-icon> </md-button> </div> <div class="alert tb-flow-drop" flow-drop> <label for=privateKeySelect translate>tb.rulenode.drop-file</label> <input class=file-input flow-btn id=privateKeySelect> </div> </div> </div> <div class=dropdown-messages> <div ng-if=!configuration.credentials.privateKeyFileName class=tb-error-message translate>tb.rulenode.no-file</div> <div ng-if=configuration.credentials.privateKeyFileName>{{configuration.credentials.privateKeyFileName}}</div> </div> <md-input-container class=md-block> <label translate>tb.rulenode.private-key-password</label> <input type=password name=privateKeyPassword ng-model=configuration.credentials.password> </md-input-container> </section> </div> </md-expansion-panel-content> </md-expansion-panel-expanded> </md-expansion-panel> </md-expansion-panel-group> </section>'},function(e,t){e.exports=' <section ng-form name=rabbitMqConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.exchange-name-pattern</label> <input name=exchangeNamePattern ng-model=configuration.exchangeNamePattern> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.routing-key-pattern</label> <input name=routingKeyPattern ng-model=configuration.routingKeyPattern> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.message-properties</label> <md-select ng-model=configuration.messageProperties ng-disabled="$root.loading || readonly"> <md-option ng-repeat="property in messageProperties" ng-value=property> {{ property }} </md-option> </md-select> </md-input-container> <div layout-gt-sm=row> <md-input-container class=md-block flex=100 flex-gt-sm=60> <label translate>tb.rulenode.host</label> <input ng-required=true name=host ng-model=configuration.host> <div ng-messages=rabbitMqConfigForm.host.$error> <div ng-message=required translate>tb.rulenode.host-required</div> </div> </md-input-container> <md-input-container class=md-block flex=100 flex-gt-sm=40> <label translate>tb.rulenode.port</label> <input ng-required=true type=number step=1 name=port ng-model=configuration.port min=0 max=65535> <div ng-messages=rabbitMqConfigForm.port.$error> <div ng-message=required translate>tb.rulenode.port-required</div> <div ng-message=min translate>tb.rulenode.port-range</div> <div ng-message=max translate>tb.rulenode.port-range</div> </div> </md-input-container> </div> <md-input-container class=md-block> <label translate>tb.rulenode.virtual-host</label> <input name=virtualHost ng-model=configuration.virtualHost> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.username</label> <input name=virtualHost ng-model=configuration.username> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.password</label> <input name=virtualHost type=password ng-model=configuration.password> </md-input-container> <md-input-container class=md-block> <md-checkbox ng-disabled="$root.loading || readonly" aria-label="{{ \'tb.rulenode.automatic-recovery\' | translate }}" ng-model=ruleNode.automaticRecoveryEnabled>{{ \'tb.rulenode.automatic-recovery\' | translate }} </md-checkbox> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.connection-timeout-ms</label> <input type=number step=1 name=connectionTimeout ng-model=configuration.connectionTimeout min=0> <div ng-messages=rabbitMqConfigForm.connectionTimeout.$error> <div ng-message=min translate>tb.rulenode.min-connection-timeout-ms-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.handshake-timeout-ms</label> <input type=number step=1 name=handshakeTimeout ng-model=configuration.handshakeTimeout min=0> <div ng-messages=rabbitMqConfigForm.handshakeTimeout.$error> <div ng-message=min translate>tb.rulenode.min-handshake-timeout-ms-message</div> </div> </md-input-container> <label translate class=tb-title>tb.rulenode.client-properties</label> <tb-kv-map-config ng-model=configuration.clientProperties ng-required=false key-text="\'tb.rulenode.key\'" key-required-text="\'tb.rulenode.key-required\'" val-text="\'tb.rulenode.value\'" val-required-text="\'tb.rulenode.value-required\'"> </tb-kv-map-config> </section> '},function(e,t){e.exports=' <section ng-form name=restApiCallConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.endpoint-url-pattern</label> <input ng-required=true name=endpointUrlPattern ng-model=configuration.restEndpointUrlPattern> <div ng-messages=restApiCallConfigForm.endpointUrlPattern.$error> <div ng-message=required translate>tb.rulenode.endpoint-url-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.endpoint-url-pattern-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.request-method</label> <md-select ng-model=configuration.requestMethod ng-disabled=$root.loading> <md-option ng-repeat="type in ruleNodeTypes.httpRequestType" ng-value=type> {{ type }} </md-option> </md-select> </md-input-container> <label translate class=tb-title>tb.rulenode.headers</label> <div class=tb-hint translate>tb.rulenode.headers-hint</div> <tb-kv-map-config ng-model=configuration.headers ng-required=false key-text="\'tb.rulenode.header\'" key-required-text="\'tb.rulenode.header-required\'" val-text="\'tb.rulenode.value\'" val-required-text="\'tb.rulenode.value-required\'"> </tb-kv-map-config> </section> '},function(e,t){e.exports=" <section ng-form name=rpcReplyConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.request-id-metadata-attribute</label> <input name=requestIdMetaDataAttribute ng-model=configuration.requestIdMetaDataAttribute> </md-input-container> </section> "},function(e,t){e.exports=" <section ng-form name=rpcRequestConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.timeout-sec</label> <input ng-required=true type=number step=1 name=timeoutInSeconds ng-model=configuration.timeoutInSeconds min=0> <div ng-messages=rpcRequestConfigForm.timeoutInSeconds.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.timeout-required</div> <div ng-message=min translate>tb.rulenode.min-timeout-message</div> </div> </md-input-container> </section> "},function(e,t){e.exports=' <section ng-form name=sendEmailConfigForm layout=column> <md-checkbox ng-disabled="$root.loading || readonly" aria-label="{{ \'tb.rulenode.use-system-smtp-settings\' | translate }}" ng-model=configuration.useSystemSmtpSettings> {{ \'tb.rulenode.use-system-smtp-settings\' | translate }} </md-checkbox> <section layout=column ng-if=!configuration.useSystemSmtpSettings> <md-input-container class=md-block> <label translate>tb.rulenode.smtp-protocol</label> <md-select ng-disabled="$root.loading || readonly" ng-model=configuration.smtpProtocol> <md-option ng-repeat="smtpProtocol in smtpProtocols" value={{smtpProtocol}}> {{smtpProtocol.toUpperCase()}} </md-option> </md-select> </md-input-container> <div layout-gt-sm=row> <md-input-container class=md-block flex=100 flex-gt-sm=60> <label translate>tb.rulenode.smtp-host</label> <input ng-required=true name=smtpHost ng-model=configuration.smtpHost> <div ng-messages=sendEmailConfigForm.smtpHost.$error> <div translate ng-message=required>tb.rulenode.smtp-host-required</div> </div> </md-input-container> <md-input-container class=md-block flex=100 flex-gt-sm=40> <label translate>tb.rulenode.smtp-port</label> <input type=number step=1 min=1 max=65535 ng-required=true name=port ng-model=configuration.smtpPort> <div ng-messages=sendEmailConfigForm.port.$error> <div translate ng-message=required>tb.rulenode.smtp-port-required</div> <div translate ng-message=min>tb.rulenode.smtp-port-range</div> <div translate ng-message=max>tb.rulenode.smtp-port-range</div> </div> </md-input-container> </div> <md-input-container class=md-block> <label translate>tb.rulenode.timeout-msec</label> <input type=number step=1 min=0 ng-required=true name=timeout ng-model=configuration.timeout> <div ng-messages=sendEmailConfigForm.timeout.$error> <div translate ng-message=required>tb.rulenode.timeout-required</div> <div translate ng-message=min>tb.rulenode.min-timeout-msec-message</div> </div> </md-input-container> <md-checkbox ng-disabled="$root.loading || readonly" aria-label="{{ \'tb.rulenode.enable-tls\' | translate }}" ng-model=configuration.enableTls>{{ \'tb.rulenode.enable-tls\' | translate }}</md-checkbox> <md-input-container class=md-block> <label translate>tb.rulenode.username</label> <input name=username placeholder="{{ \'tb.rulenode.enter-username\' | translate }}" ng-model=configuration.username> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.password</label> <input name=password placeholder="{{ \'tb.rulenode.enter-password\' | translate }}" type=password ng-model=configuration.password> </md-input-container> </section> </section> '},function(e,t){e.exports=" <section ng-form name=snsConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.topic-arn-pattern</label> <input ng-required=true name=topicArnPattern ng-model=configuration.topicArnPattern> <div ng-messages=snsConfigForm.topicArnPattern.$error> <div ng-message=required translate>tb.rulenode.topic-arn-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.topic-arn-pattern-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.aws-access-key-id</label> <input ng-required=true name=accessKeyId ng-model=configuration.accessKeyId> <div ng-messages=snsConfigForm.accessKeyId.$error> <div ng-message=required translate>tb.rulenode.aws-access-key-id-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.aws-secret-access-key</label> <input ng-required=true name=secretAccessKey ng-model=configuration.secretAccessKey> <div ng-messages=snsConfigForm.secretAccessKey.$error> <div ng-message=required translate>tb.rulenode.aws-secret-access-key-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.aws-region</label> <input ng-required=true name=region ng-model=configuration.region> <div ng-messages=snsConfigForm.region.$error> <div ng-message=required translate>tb.rulenode.aws-region-required</div> </div> </md-input-container> </section> "},function(e,t){e.exports=' <section ng-form name=sqsConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.queue-type</label> <md-select ng-model=configuration.queueType ng-disabled="$root.loading || readonly"> <md-option ng-repeat="type in ruleNodeTypes.sqsQueueType" ng-value=type.value> {{ type.name | translate }} </md-option> </md-select> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.queue-url-pattern</label> <input ng-required=true name=queueUrlPattern ng-model=configuration.queueUrlPattern> <div ng-messages=sqsConfigForm.queueUrlPattern.$error> <div ng-message=required translate>tb.rulenode.queue-url-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.queue-url-pattern-hint</div> </md-input-container> <md-input-container class=md-block ng-if="configuration.queueType == ruleNodeTypes.sqsQueueType.STANDARD.value"> <label translate>tb.rulenode.delay-seconds</label> <input type=number step=1 name=delaySeconds ng-model=configuration.delaySeconds min=0 max=900> <div ng-messages=sqsConfigForm.delaySeconds.$error> <div ng-message=min translate>tb.rulenode.min-delay-seconds-message</div> <div ng-message=max translate>tb.rulenode.max-delay-seconds-message</div> </div> </md-input-container> <label translate class=tb-title>tb.rulenode.message-attributes</label> <div class=tb-hint translate>tb.rulenode.message-attributes-hint</div> <tb-kv-map-config ng-model=configuration.messageAttributes ng-required=false key-text="\'tb.rulenode.name\'" key-required-text="\'tb.rulenode.name-required\'" val-text="\'tb.rulenode.value\'" val-required-text="\'tb.rulenode.value-required\'"> </tb-kv-map-config> <md-input-container class=md-block> <label translate>tb.rulenode.aws-access-key-id</label> <input ng-required=true name=accessKeyId ng-model=configuration.accessKeyId> <div ng-messages=snsConfigForm.accessKeyId.$error> <div ng-message=required translate>tb.rulenode.aws-access-key-id-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.aws-secret-access-key</label> <input ng-required=true name=secretAccessKey ng-model=configuration.secretAccessKey> <div ng-messages=snsConfigForm.secretAccessKey.$error> <div ng-message=required translate>tb.rulenode.aws-secret-access-key-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.aws-region</label> <input ng-required=true name=region ng-model=configuration.region> <div ng-messages=snsConfigForm.region.$error> <div ng-message=required translate>tb.rulenode.aws-region-required</div> </div> </md-input-container> </section> '},function(e,t){e.exports=" <section ng-form name=timeseriesConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.default-ttl</label> <input ng-required=true type=number step=1 name=defaultTTL ng-model=configuration.defaultTTL min=0> <div ng-messages=timeseriesConfigForm.defaultTTL.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.default-ttl-required</div> <div ng-message=min translate>tb.rulenode.min-default-ttl-message</div> </div> </md-input-container> </section> "},function(e,t){e.exports=' <section layout=column> <div layout=row> <md-input-container class=md-block style=min-width:100px> <label translate>relation.direction</label> <md-select required ng-model=query.direction> <md-option ng-repeat="direction in types.entitySearchDirection" ng-value=direction> {{ (\'relation.search-direction.\' + direction) | translate}} </md-option> </md-select> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.max-relation-level</label> <input name=maxRelationLevel type=number min=1 step=1 placeholder="{{ \'tb.rulenode.unlimited-level\' | translate }}" ng-model=query.maxLevel aria-label="{{ \'tb.rulenode.max-relation-level\' | translate }}"> </md-input-container> </div> <div class=md-caption style=color:rgba(0,0,0,.57) translate>relation.relation-type</div> <tb-relation-type-autocomplete flex hide-label ng-model=query.relationType tb-required=false> </tb-relation-type-autocomplete> <div class="md-caption tb-required" style=color:rgba(0,0,0,.57) translate>device.device-types</div> <tb-entity-subtype-list tb-required=true entity-type=types.entityType.device ng-model=query.deviceTypes> </tb-entity-subtype-list> </section> '},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title tb-required\">tb.rulenode.attr-mapping</label> <md-checkbox aria-label=\"{{ 'tb.rulenode.latest-telemetry' | translate }}\" ng-model=configuration.telemetry>{{ 'tb.rulenode.latest-telemetry' | translate }} </md-checkbox> <tb-kv-map-config ng-model=configuration.attrMapping ng-required=true required-text=\"'tb.rulenode.attr-mapping-required'\" key-text=\"configuration.telemetry ? 'tb.rulenode.source-telemetry' : 'tb.rulenode.source-attribute'\" key-required-text=\"configuration.telemetry ? 'tb.rulenode.source-telemetry-required' : 'tb.rulenode.source-attribute-required'\" val-text=\"'tb.rulenode.target-attribute'\" val-required-text=\"'tb.rulenode.target-attribute-required'\"> </tb-kv-map-config> </section> ";
+},function(e,t){e.exports=' <section layout=column> <label translate class="tb-title tb-required">tb.rulenode.device-relations-query</label> <tb-device-relations-query-config style=padding-bottom:15px ng-model=configuration.deviceRelationsQuery> </tb-device-relations-query-config> <label translate class="tb-title no-padding">tb.rulenode.client-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.clientAttributeNames placeholder="{{\'tb.rulenode.client-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.shared-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.sharedAttributeNames placeholder="{{\'tb.rulenode.shared-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.server-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.serverAttributeNames placeholder="{{\'tb.rulenode.server-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.latest-timeseries</label> <md-chips ng-required=false readonly=readonly ng-model=configuration.latestTsKeyNames placeholder="{{\'tb.rulenode.latest-timeseries\' | translate}}" md-separator-keys=separatorKeys> </md-chips> </section> '},function(e,t){e.exports=' <section layout=column> <label translate class="tb-title no-padding">tb.rulenode.client-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.clientAttributeNames placeholder="{{\'tb.rulenode.client-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.shared-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.sharedAttributeNames placeholder="{{\'tb.rulenode.shared-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.server-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.serverAttributeNames placeholder="{{\'tb.rulenode.server-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.latest-timeseries</label> <md-chips ng-required=false readonly=readonly ng-model=configuration.latestTsKeyNames placeholder="{{\'tb.rulenode.latest-timeseries\' | translate}}" md-separator-keys=separatorKeys> </md-chips> </section> '},function(e,t){e.exports=' <section layout=column> <label translate class="tb-title tb-required">tb.rulenode.fields-mapping</label> <tb-kv-map-config ng-model=configuration.fieldsMapping ng-required=true required-text="\'tb.rulenode.fields-mapping-required\'" key-text="\'tb.rulenode.source-field\'" key-required-text="\'tb.rulenode.source-field-required\'" val-text="\'tb.rulenode.target-attribute\'" val-required-text="\'tb.rulenode.target-attribute-required\'"> </tb-kv-map-config> </section> '},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title tb-required\">tb.rulenode.relations-query</label> <tb-relations-query-config style=padding-bottom:15px ng-model=configuration.relationsQuery> </tb-relations-query-config> <label translate class=\"tb-title tb-required\">tb.rulenode.attr-mapping</label> <md-checkbox aria-label=\"{{ 'tb.rulenode.latest-telemetry' | translate }}\" ng-model=configuration.telemetry>{{ 'tb.rulenode.latest-telemetry' | translate }} </md-checkbox> <tb-kv-map-config ng-model=configuration.attrMapping ng-required=true required-text=\"'tb.rulenode.attr-mapping-required'\" key-text=\"configuration.telemetry ? 'tb.rulenode.source-telemetry' : 'tb.rulenode.source-attribute'\" key-required-text=\"configuration.telemetry ? 'tb.rulenode.source-telemetry-required' : 'tb.rulenode.source-attribute-required'\" val-text=\"'tb.rulenode.target-attribute'\" val-required-text=\"'tb.rulenode.target-attribute-required'\"> </tb-kv-map-config> </section> "},21,function(e,t){e.exports=" <section ng-form name=checkRelationConfigForm> <md-input-container class=md-block style=min-width:100px> <label translate>relation.direction</label> <md-select required ng-model=configuration.direction> <md-option ng-repeat=\"direction in types.entitySearchDirection\" ng-value=direction> {{ ('relation.search-direction.' + direction) | translate}} </md-option> </md-select> </md-input-container> <div layout=row class=tb-entity-select> <tb-entity-type-select style=min-width:100px the-form=checkRelationConfigForm tb-required=true ng-model=configuration.entityType> </tb-entity-type-select> <tb-entity-autocomplete flex ng-if=configuration.entityType the-form=checkRelationConfigForm tb-required=true entity-type=configuration.entityType ng-model=configuration.entityId> </tb-entity-autocomplete> </div> <tb-relation-type-autocomplete hide-label ng-model=configuration.relationType tb-required=true> </tb-relation-type-autocomplete> </section> "},function(e,t){e.exports=' <section layout=column> <label translate class="tb-title no-padding" ng-class="{\'tb-required\': required}">tb.rulenode.message-types-filter</label> <md-chips id=message_type_chips ng-required=required readonly=readonly ng-model=messageTypes md-autocomplete-snap md-transform-chip=transformMessageTypeChip($chip) md-require-match=false> <md-autocomplete id=message_type md-no-cache=true md-selected-item=selectedMessageType md-search-text=messageTypeSearchText md-items="item in messageTypesSearch(messageTypeSearchText)" md-item-text=item.name md-min-length=0 placeholder="{{\'tb.rulenode.message-type\' | translate }}" md-menu-class=tb-message-type-autocomplete> <span md-highlight-text=messageTypeSearchText md-highlight-flags=^i>{{item}}</span> <md-not-found> <div class=tb-not-found> <div class=tb-no-entries ng-if="!messageTypeSearchText || !messageTypeSearchText.length"> <span translate>tb.rulenode.no-message-types-found</span> </div> <div ng-if="messageTypeSearchText && messageTypeSearchText.length"> <span translate translate-values=\'{ messageType: "{{messageTypeSearchText | truncate:true:6:'...'}}" }\'>tb.rulenode.no-message-type-matching</span> <span> <a translate ng-click="createMessageType($event, \'#message_type_chips\')">tb.rulenode.create-new-message-type</a> </span> </div> </div> </md-not-found> </md-autocomplete> <md-chip-template> <span>{{$chip.name}}</span> </md-chip-template> </md-chips> <div class=tb-error-messages ng-messages=ngModelCtrl.$error role=alert> <div translate ng-message=messageTypes class=tb-error-message>tb.rulenode.message-types-required</div> </div> </section>'},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.filter</label> <tb-js-func ng-model=configuration.jsScript function-name=Filter function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row> <md-button ng-click=testScript($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-filter-function' | translate }} </md-button> </div> </section> "},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.switch</label> <tb-js-func ng-model=configuration.jsScript function-name=Switch function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row> <md-button ng-click=testScript($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-switch-function' | translate }} </md-button> </div> </section> "},function(e,t){e.exports=' <section class=tb-kv-map-config layout=column> <div class=header flex layout=row> <span class=cell flex translate>{{ keyText }}</span> <span class=cell flex translate>{{ valText }}</span> <span ng-show=!disabled style=width:52px> </span> </div> <div class=body> <div class=row ng-form name=kvForm flex layout=row layout-align="start center" ng-repeat="keyVal in kvList track by $index"> <md-input-container class="cell md-block" flex md-no-float> <input placeholder="{{ keyText | translate }}" ng-required=true name=key ng-model=keyVal.key> <div ng-messages=kvForm.key.$error> <div translate ng-message=required>{{keyRequiredText}}</div> </div> </md-input-container> <md-input-container class="cell md-block" flex md-no-float> <input placeholder="{{ valText | translate }}" ng-required=true name=value ng-model=keyVal.value> <div ng-messages=kvForm.value.$error> <div translate ng-message=required>{{valRequiredText}}</div> </div> </md-input-container> <md-button ng-show=!disabled ng-disabled=loading class="md-icon-button md-primary" ng-click=removeKeyVal($index) aria-label="{{ \'action.remove\' | translate }}"> <md-tooltip md-direction=top> {{ \'tb.key-val.remove-entry\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.delete\' | translate }}" class=material-icons> close </md-icon> </md-button> </div> </div> <div class=tb-error-messages ng-messages=ngModelCtrl.$error role=alert> <div translate ng-message=kvMap class=tb-error-message>{{requiredText}}</div> </div> <div> <md-button ng-show=!disabled ng-disabled=loading class="md-primary md-raised" ng-click=addKeyVal() aria-label="{{ \'action.add\' | translate }}"> <md-tooltip md-direction=top> {{ \'tb.key-val.add-entry\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.add\' | translate }}" class=material-icons> add </md-icon> {{ \'action.add\' | translate }} </md-button> </div> </section> '},function(e,t){e.exports=" <section layout=column> <div layout=row> <md-input-container class=md-block style=min-width:100px> <label translate>relation.direction</label> <md-select required ng-model=query.direction> <md-option ng-repeat=\"direction in types.entitySearchDirection\" ng-value=direction> {{ ('relation.search-direction.' + direction) | translate}} </md-option> </md-select> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.max-relation-level</label> <input name=maxRelationLevel type=number min=1 step=1 placeholder=\"{{ 'tb.rulenode.unlimited-level' | translate }}\" ng-model=query.maxLevel aria-label=\"{{ 'tb.rulenode.max-relation-level' | translate }}\"> </md-input-container> </div> <div class=md-caption style=padding-bottom:10px;color:rgba(0,0,0,.57) translate>relation.relation-filters</div> <tb-relation-filters ng-model=query.filters> </tb-relation-filters> </section> "},function(e,t){e.exports=' <section layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.originator-source</label> <md-select required ng-model=configuration.originatorSource> <md-option ng-repeat="source in ruleNodeTypes.originatorSource" ng-value=source.value> {{ source.name | translate}} </md-option> </md-select> </md-input-container> <section layout=column ng-if="configuration.originatorSource == ruleNodeTypes.originatorSource.RELATED.value"> <label translate class="tb-title tb-required">tb.rulenode.relations-query</label> <tb-relations-query-config style=padding-bottom:15px ng-model=configuration.relationsQuery> </tb-relations-query-config> </section> </section> '},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.transform</label> <tb-js-func ng-model=configuration.jsScript function-name=Transform function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row style=padding-bottom:15px> <md-button ng-click=testScript($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-transformer-function' | translate }} </md-button> </div> </section> "},function(e,t){e.exports=" <section ng-form name=toEmailConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.from-template</label> <textarea ng-required=true name=fromTemplate ng-model=configuration.fromTemplate rows=2></textarea> <div ng-messages=toEmailConfigForm.fromTemplate.$error> <div ng-message=required translate>tb.rulenode.from-template-required</div> </div> <div class=tb-hint translate>tb.rulenode.from-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.to-template</label> <textarea ng-required=true name=toTemplate ng-model=configuration.toTemplate rows=2></textarea> <div ng-messages=toEmailConfigForm.toTemplate.$error> <div ng-message=required translate>tb.rulenode.to-template-required</div> </div> <div class=tb-hint translate>tb.rulenode.mail-address-list-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.cc-template</label> <textarea name=ccTemplate ng-model=configuration.ccTemplate rows=2></textarea> <div class=tb-hint translate>tb.rulenode.mail-address-list-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.bcc-template</label> <textarea name=ccTemplate ng-model=configuration.bccTemplate rows=2></textarea> <div class=tb-hint translate>tb.rulenode.mail-address-list-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.subject-template</label> <textarea ng-required=true name=subjectTemplate ng-model=configuration.subjectTemplate rows=2></textarea> <div ng-messages=toEmailConfigForm.subjectTemplate.$error> <div ng-message=required translate>tb.rulenode.subject-template-required</div> </div> <div class=tb-hint translate>tb.rulenode.subject-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.body-template</label> <textarea ng-required=true name=bodyTemplate ng-model=configuration.bodyTemplate rows=6></textarea> <div ng-messages=toEmailConfigForm.bodyTemplate.$error> <div ng-message=required translate>tb.rulenode.body-template-required</div> </div> <div class=tb-hint translate>tb.rulenode.body-template-hint</div> </md-input-container> </section> "},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(5),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n,a){var r=function(r,i,l,s){var u=o.default;i.html(u),r.types=n,r.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(r.configuration)}),s.$render=function(){r.configuration=s.$viewValue},r.testDetailsBuildJs=function(e){var n=angular.copy(r.configuration.alarmDetailsBuildJs);a.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],r.ruleNodeId).then(function(e){r.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(i.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:r}}r.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(6),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n,a){var r=function(r,i,l,s){var u=o.default;i.html(u),r.types=n,r.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(r.configuration)}),s.$render=function(){r.configuration=s.$viewValue},r.testDetailsBuildJs=function(e){var n=angular.copy(r.configuration.alarmDetailsBuildJs);a.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],r.ruleNodeId).then(function(e){r.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(i.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:r}}r.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(7),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n,a){var r=function(r,i,l,s){var u=o.default;i.html(u),r.types=n,r.originator=null,r.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(r.configuration)}),s.$render=function(){r.configuration=s.$viewValue,r.configuration.originatorId&&r.configuration.originatorType?r.originator={id:r.configuration.originatorId,entityType:r.configuration.originatorType}:r.originator=null,r.$watch("originator",function(e,t){angular.equals(e,t)||(r.originator?(s.$viewValue.originatorId=r.originator.id,s.$viewValue.originatorType=r.originator.entityType):(s.$viewValue.originatorId=null,s.$viewValue.originatorType=null))},!0)},r.testScript=function(e){var n=angular.copy(r.configuration.jsScript);a.testNodeScript(e,n,"generate",t.instant("tb.rulenode.generator")+"","Generate",["prevMsg","prevMetadata","prevMsgType"],r.ruleNodeId).then(function(e){r.configuration.jsScript=e,s.$setDirty()})},e(i.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:r}}r.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r,n(1);var i=n(8),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(51),i=a(r),o=n(36),l=a(o),s=n(39),u=a(s),d=n(38),c=a(d),m=n(37),g=a(m),p=n(42),f=a(p),b=n(46),v=a(b),y=n(47),q=a(y),h=n(45),T=a(h),$=n(41),k=a($),w=n(49),C=a(w),_=n(50),x=a(_),E=n(44),M=a(E),S=n(43),N=a(S),V=n(48),P=a(V);t.default=angular.module("thingsboard.ruleChain.config.action",[]).directive("tbActionNodeTimeseriesConfig",i.default).directive("tbActionNodeAttributesConfig",l.default).directive("tbActionNodeGeneratorConfig",u.default).directive("tbActionNodeCreateAlarmConfig",c.default).directive("tbActionNodeClearAlarmConfig",g.default).directive("tbActionNodeLogConfig",f.default).directive("tbActionNodeRpcReplyConfig",v.default).directive("tbActionNodeRpcRequestConfig",q.default).directive("tbActionNodeRestApiCallConfig",T.default).directive("tbActionNodeKafkaConfig",k.default).directive("tbActionNodeSnsConfig",C.default).directive("tbActionNodeSqsConfig",x.default).directive("tbActionNodeRabbitMqConfig",M.default).directive("tbActionNodeMqttConfig",N.default).directive("tbActionNodeSendEmailConfig",P.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.ackValues=["all","-1","0","1"],t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(9),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s),a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},a.testScript=function(e){var r=angular.copy(a.configuration.jsScript);n.testNodeScript(e,r,"string",t.instant("tb.rulenode.to-string")+"","ToString",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,l.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}r.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(10),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s),a.$mdExpansionPanel=t,a.ruleNodeTypes=n,a.credentialsTypeChanged=function(){var e=a.configuration.credentials.type;a.configuration.credentials={},a.configuration.credentials.type=e,a.updateValidity()},a.certFileAdded=function(e,t){var n=new FileReader;n.onload=function(n){a.$apply(function(){if(n.target.result){l.$setDirty();var r=n.target.result;r&&r.length>0&&("caCert"==t&&(a.configuration.credentials.caCertFileName=e.name,a.configuration.credentials.caCert=r),"privateKey"==t&&(a.configuration.credentials.privateKeyFileName=e.name,a.configuration.credentials.privateKey=r),"Cert"==t&&(a.configuration.credentials.certFileName=e.name,a.configuration.credentials.cert=r)),a.updateValidity()}})},n.readAsText(e.file)},a.clearCertFile=function(e){l.$setDirty(),"caCert"==e&&(a.configuration.credentials.caCertFileName=null,a.configuration.credentials.caCert=null),"privateKey"==e&&(a.configuration.credentials.privateKeyFileName=null,a.configuration.credentials.privateKey=null),"Cert"==e&&(a.configuration.credentials.certFileName=null,a.configuration.credentials.cert=null),a.updateValidity()},a.updateValidity=function(){var e=!0,t=a.configuration.credentials;t.type==n.mqttCredentialTypes["cert.PEM"].value&&(t.caCert&&t.cert&&t.privateKey||(e=!1)),l.$setValidity("Certs",e)},a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:a}}r.$inject=["$compile","$mdExpansionPanel","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r,n(2);var i=n(11),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.messageProperties=[null,"BASIC","TEXT_PLAIN","MINIMAL_BASIC","MINIMAL_PERSISTENT_BASIC","PERSISTENT_BASIC","PERSISTENT_TEXT_PLAIN"],t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(12),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(13),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(14),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(15),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.smtpProtocols=["smtp","smtps"],t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(16),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(17),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}r.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(18),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(19),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||i.$setViewValue(n.query)}),i.$render=function(){n.query=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(20),o=a(i)},function(e,t){"use strict";function n(e){var t=function(t,n,a,r){n.html("<div></div>"),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}n.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(21),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(22),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(57),i=a(r),o=n(58),l=a(o),s=n(55),u=a(s),d=n(59),c=a(d),m=n(54),g=a(m),p=n(60),f=a(p);t.default=angular.module("thingsboard.ruleChain.config.enrichment",[]).directive("tbEnrichmentNodeOriginatorAttributesConfig",i.default).directive("tbEnrichmentNodeOriginatorFieldsConfig",l.default).directive("tbEnrichmentNodeDeviceAttributesConfig",u.default).directive("tbEnrichmentNodeRelatedAttributesConfig",c.default).directive("tbEnrichmentNodeCustomerAttributesConfig",g.default).directive("tbEnrichmentNodeTenantAttributesConfig",f.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(23),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(24),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(25),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(26),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(27),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(64),i=a(r),o=n(63),l=a(o),s=n(65),u=a(s),d=n(61),c=a(d);t.default=angular.module("thingsboard.ruleChain.config.filter",[]).directive("tbFilterNodeScriptConfig",i.default).directive("tbFilterNodeMessageTypeConfig",l.default).directive("tbFilterNodeSwitchConfig",u.default).directive("tbFilterNodeCheckRelationConfig",c.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){function s(){if(l.$viewValue){for(var e=[],t=0;t<a.messageTypes.length;t++)e.push(a.messageTypes[t].value);l.$viewValue.messageTypes=e,u()}}function u(){if(a.required){var e=!(!l.$viewValue.messageTypes||!l.$viewValue.messageTypes.length);l.$setValidity("messageTypes",e)}else l.$setValidity("messageTypes",!0)}var d=o.default;r.html(d),a.selectedMessageType=null,a.messageTypeSearchText=null,a.ngModelCtrl=l;var c=[];for(var m in n.messageType){var g={name:n.messageType[m].name,value:n.messageType[m].value};c.push(g)}a.transformMessageTypeChip=function(e){
+var n,a=t("filter")(c,{name:e},!0);return n=a&&a.length?angular.copy(a[0]):{name:e,value:e}},a.messageTypesSearch=function(e){var n=e?t("filter")(c,{name:e}):c;return n.map(function(e){return e.name})},a.createMessageType=function(e,t){var n=angular.element(t,r)[0].firstElementChild,a=angular.element(n),i=a.scope().$mdChipsCtrl.getChipBuffer();e.preventDefault(),e.stopPropagation(),a.scope().$mdChipsCtrl.appendChip(i.trim()),a.scope().$mdChipsCtrl.resetChipBuffer()},l.$render=function(){var e=l.$viewValue,t=[];if(e&&e.messageTypes)for(var r=0;r<e.messageTypes.length;r++){var i=e.messageTypes[r];n.messageType[i]?t.push(angular.copy(n.messageType[i])):t.push({name:i,value:i})}a.messageTypes=t,a.$watch("messageTypes",function(e,t){angular.equals(e,t)||s()},!0)},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",readonly:"=ngReadonly"},link:a}}r.$inject=["$compile","$filter","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r,n(3);var i=n(28),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s),a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},a.testScript=function(e){var r=angular.copy(a.configuration.jsScript);n.testNodeScript(e,r,"filter",t.instant("tb.rulenode.filter")+"","Filter",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,l.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}r.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(29),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s),a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},a.testScript=function(e){var r=angular.copy(a.configuration.jsScript);n.testNodeScript(e,r,"switch",t.instant("tb.rulenode.switch")+"","Switch",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,l.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}r.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(30),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){function i(e){e>-1&&t.kvList.splice(e,1)}function l(){t.kvList||(t.kvList=[]),t.kvList.push({key:"",value:""})}function s(){var e={};t.kvList.forEach(function(t){t.key&&(e[t.key]=t.value)}),r.$setViewValue(e),u()}function u(){var e=!0;t.required&&!t.kvList.length&&(e=!1),r.$setValidity("kvMap",e)}var d=o.default;n.html(d),t.ngModelCtrl=r,t.removeKeyVal=i,t.addKeyVal=l,t.kvList=[],t.$watch("query",function(e,n){angular.equals(e,n)||r.$setViewValue(t.query)}),r.$render=function(){if(r.$viewValue){var e=r.$viewValue;t.kvList.length=0;for(var n in e)t.kvList.push({key:n,value:e[n]})}t.$watch("kvList",function(e,t){angular.equals(e,t)||s()},!0),u()},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",disabled:"=ngDisabled",requiredText:"=",keyText:"=",keyRequiredText:"=",valText:"=",valRequiredText:"="},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(31),o=a(i);n(4)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||i.$setViewValue(n.query)}),i.$render=function(){n.query=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(32),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(33),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(68),i=a(r),o=n(70),l=a(o),s=n(71),u=a(s);t.default=angular.module("thingsboard.ruleChain.config.transform",[]).directive("tbTransformationNodeChangeOriginatorConfig",i.default).directive("tbTransformationNodeScriptConfig",l.default).directive("tbTransformationNodeToEmailConfig",u.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s),a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},a.testScript=function(e){var r=angular.copy(a.configuration.jsScript);n.testNodeScript(e,r,"update",t.instant("tb.rulenode.transformer")+"","Transform",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,l.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}r.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(34),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(35),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(75),i=a(r),o=n(62),l=a(o),s=n(56),u=a(s),d=n(69),c=a(d),m=n(40),g=a(m),p=n(53),f=a(p),b=n(67),v=a(b),y=n(52),q=a(y),h=n(66),T=a(h),$=n(74),k=a($);t.default=angular.module("thingsboard.ruleChain.config",[i.default,l.default,u.default,c.default,g.default]).directive("tbNodeEmptyConfig",f.default).directive("tbRelationsQueryConfig",v.default).directive("tbDeviceRelationsQueryConfig",q.default).directive("tbKvMapConfig",T.default).config(k.default).name},function(e,t){"use strict";function n(e){var t={tb:{rulenode:{filter:"Filter",switch:"Switch","message-type":"Message type","message-type-required":"Message type is required.","message-types-filter":"Message types filter","no-message-types-found":"No message types found","no-message-type-matching":"'{{messageType}}' not found.","create-new-message-type":"Create a new one!","message-types-required":"Message types are required.","client-attributes":"Client attributes","shared-attributes":"Shared attributes","server-attributes":"Server attributes","latest-timeseries":"Latest timeseries","relations-query":"Relations query","device-relations-query":"Device relations query","max-relation-level":"Max relation level","unlimited-level":"Unlimited level","latest-telemetry":"Latest telemetry","attr-mapping":"Attributes mapping","source-attribute":"Source attribute","source-attribute-required":"Source attribute is required.","source-telemetry":"Source telemetry","source-telemetry-required":"Source telemetry is required.","target-attribute":"Target attribute","target-attribute-required":"Target attribute is required.","attr-mapping-required":"At least one attribute mapping should be specified.","fields-mapping":"Fields mapping","fields-mapping-required":"At least one field mapping should be specified.","source-field":"Source field","source-field-required":"Source field is required.","originator-source":"Originator source","originator-customer":"Customer","originator-tenant":"Tenant","originator-related":"Related","clone-message":"Clone message",transform:"Transform","default-ttl":"Default TTL in seconds","default-ttl-required":"Default TTL is required.","min-default-ttl-message":"Only 0 minimum TTL is allowed.","message-count":"Message count (0 - unlimited)","message-count-required":"Message count is required.","min-message-count-message":"Only 0 minimum message count is allowed.","period-seconds":"Period in seconds","period-seconds-required":"Period is required.","min-period-seconds-message":"Only 1 second minimum period is allowed.",originator:"Originator","message-body":"Message body","message-metadata":"Message metadata",generate:"Generate","test-generator-function":"Test generator function",generator:"Generator","test-filter-function":"Test filter function","test-switch-function":"Test switch function","test-transformer-function":"Test transformer function",transformer:"Transformer","alarm-create-condition":"Alarm create condition","test-condition-function":"Test condition function","alarm-clear-condition":"Alarm clear condition","alarm-details-builder":"Alarm details builder","test-details-function":"Test details function","alarm-type":"Alarm type","alarm-type-required":"Alarm type is required.","alarm-severity":"Alarm severity","alarm-severity-required":"Alarm severity is required",propagate:"Propagate",condition:"Condition",details:"Details","to-string":"To string","test-to-string-function":"Test to string function","from-template":"From Template","from-template-required":"From Template is required","from-template-hint":"From address template, use <code>${metaKeyName}</code> to substitute variables from metadata","to-template":"To Template","to-template-required":"To Template is required","mail-address-list-template-hint":"Comma separated address list, use <code>${metaKeyName}</code> to substitute variables from metadata","cc-template":"Cc Template","bcc-template":"Bcc Template","subject-template":"Subject Template","subject-template-required":"Subject Template is required","subject-template-hint":"Mail subject template, use <code>${metaKeyName}</code> to substitute variables from metadata","body-template":"Body Template","body-template-required":"Body Template is required","body-template-hint":"Mail body template, use <code>${metaKeyName}</code> to substitute variables from metadata","request-id-metadata-attribute":"Request Id Metadata attribute name","timeout-sec":"Timeout in seconds","timeout-required":"Timeout is required","min-timeout-message":"Only 0 minimum timeout value is allowed.","endpoint-url-pattern":"Endpoint URL pattern","endpoint-url-pattern-required":"Endpoint URL pattern is required","endpoint-url-pattern-hint":"HTTP URL address pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","request-method":"Request method",headers:"Headers","headers-hint":"Use <code>${metaKeyName}</code> in header/value fields to substitute variables from metadata",header:"Header","header-required":"Header is required",value:"Value","value-required":"Value is required","topic-pattern":"Topic pattern","topic-pattern-required":"Topic pattern is required","mqtt-topic-pattern-hint":"MQTT topic pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","bootstrap-servers":"Bootstrap servers","bootstrap-servers-required":"Bootstrap servers value is required","other-properties":"Other properties",key:"Key","key-required":"Key is required",retries:"Automatically retry times if fails","min-retries-message":"Only 0 minimum retries is allowed.","batch-size-bytes":"Produces batch size in bytes","min-batch-size-bytes-message":"Only 0 minimum batch size is allowed.","linger-ms":"Time to buffer locally (ms)","min-linger-ms-message":"Only 0 ms minimum value is allowed.","buffer-memory-bytes":"Client buffer max size in bytes","min-buffer-memory-message":"Only 0 minimum buffer size is allowed.",acks:"Number of acknowledgments","key-serializer":"Key serializer","key-serializer-required":"Key serializer is required","value-serializer":"Value serializer","value-serializer-required":"Value serializer is required","topic-arn-pattern":"Topic ARN pattern","topic-arn-pattern-required":"Topic ARN pattern is required","topic-arn-pattern-hint":"Topic ARN pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","aws-access-key-id":"AWS Access Key ID","aws-access-key-id-required":"AWS Access Key ID is required","aws-secret-access-key":"AWS Secret Access Key","aws-secret-access-key-required":"AWS Secret Access Key is required","aws-region":"AWS Region","aws-region-required":"AWS Region is required","exchange-name-pattern":"Exchange name pattern","routing-key-pattern":"Routing key pattern","message-properties":"Message properties",host:"Host","host-required":"Host is required",port:"Port","port-required":"Port is required","port-range":"Port should be in a range from 1 to 65535.","virtual-host":"Virtual host",username:"Username",password:"Password","automatic-recovery":"Automatic recovery","connection-timeout-ms":"Connection timeout (ms)","min-connection-timeout-ms-message":"Only 0 ms minimum value is allowed.","handshake-timeout-ms":"Handshake timeout (ms)","min-handshake-timeout-ms-message":"Only 0 ms minimum value is allowed.","client-properties":"Client properties","queue-url-pattern":"Queue URL pattern","queue-url-pattern-required":"Queue URL pattern is required","queue-url-pattern-hint":"Queue URL pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","delay-seconds":"Delay (seconds)","min-delay-seconds-message":"Only 0 seconds minimum value is allowed.","max-delay-seconds-message":"Only 900 seconds maximum value is allowed.",name:"Name","name-required":"Name is required","queue-type":"Queue type","sqs-queue-standard":"Standard","sqs-queue-fifo":"FIFO","message-attributes":"Message attributes","message-attributes-hint":"Use <code>${metaKeyName}</code> in name/value fields to substitute variables from metadata","connect-timeout":"Connection timeout (sec)","connect-timeout-required":"Connection timeout is required.","connect-timeout-range":"Connection timeout should be in a range from 1 to 200.","client-id":"Client ID","clean-session":"Clean session","enable-ssl":"Enable SSL",credentials:"Credentials","credentials-type":"Credentials type","credentials-type-required":"Credentials type is required.","credentials-anonymous":"Anonymous","credentials-basic":"Basic","credentials-pem":"PEM","username-required":"Username is required.","password-required":"Password is required.","ca-cert":"CA certificate file *","private-key":"Private key file *",cert:"Certificate file *","no-file":"No file selected.","drop-file":"Drop a file or click to select a file to upload.","private-key-password":"Private key password","use-system-smtp-settings":"Use system SMTP settings","smtp-protocol":"Protocol","smtp-host":"SMTP host","smtp-host-required":"SMTP host is required.","smtp-port":"SMTP port","smtp-port-required":"You must supply a smtp port.","smtp-port-range":"SMTP port should be in a range from 1 to 65535.","timeout-msec":"Timeout ms","min-timeout-msec-message":"Only 0 ms minimum value is allowed.","enter-username":"Enter username","enter-password":"Enter password","enable-tls":"Enable TLS"},"key-val":{key:"Key",value:"Value","remove-entry":"Remove entry","add-entry":"Add entry"}}};angular.merge(e.en_US,t)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){(0,o.default)(t);for(var n in t){var a=t[n];e.translations(n,a)}}r.$inject=["$translateProvider","locales"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(73),o=a(i)},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=angular.module("thingsboard.ruleChain.config.types",[]).constant("ruleNodeTypes",{messageType:{POST_ATTRIBUTES_REQUEST:{name:"Post attributes",value:"POST_ATTRIBUTES_REQUEST"},POST_TELEMETRY_REQUEST:{name:"Post telemetry",value:"POST_TELEMETRY_REQUEST"},TO_SERVER_RPC_REQUEST:{name:"RPC Request from Device",value:"TO_SERVER_RPC_REQUEST"},RPC_CALL_FROM_SERVER_TO_DEVICE:{name:"RPC Request to Device",value:"RPC_CALL_FROM_SERVER_TO_DEVICE"},ACTIVITY_EVENT:{name:"Activity Event",value:"ACTIVITY_EVENT"},INACTIVITY_EVENT:{name:"Inactivity Event",value:"INACTIVITY_EVENT"},CONNECT_EVENT:{name:"Connect Event",value:"CONNECT_EVENT"},DISCONNECT_EVENT:{name:"Disconnect Event",value:"DISCONNECT_EVENT"},ENTITY_CREATED:{name:"Entity Created",value:"ENTITY_CREATED"},ENTITY_UPDATED:{name:"Entity Updated",value:"ENTITY_UPDATED"},ENTITY_DELETED:{name:"Entity Deleted",value:"ENTITY_DELETED"},ENTITY_ASSIGNED:{name:"Entity Assigned",value:"ENTITY_ASSIGNED"},ENTITY_UNASSIGNED:{name:"Entity Unassigned",value:"ENTITY_UNASSIGNED"},ATTRIBUTES_UPDATED:{name:"Attributes Updated",value:"ATTRIBUTES_UPDATED"},ATTRIBUTES_DELETED:{name:"Attributes Deleted",value:"ATTRIBUTES_DELETED"}},originatorSource:{CUSTOMER:{name:"tb.rulenode.originator-customer",value:"CUSTOMER"},TENANT:{name:"tb.rulenode.originator-tenant",value:"TENANT"},RELATED:{name:"tb.rulenode.originator-related",value:"RELATED"}},httpRequestType:["GET","POST","PUT","DELETE"],sqsQueueType:{STANDARD:{name:"tb.rulenode.sqs-queue-standard",value:"STANDARD"},FIFO:{name:"tb.rulenode.sqs-queue-fifo",value:"FIFO"}},mqttCredentialTypes:{anonymous:{value:"anonymous",name:"tb.rulenode.credentials-anonymous"},basic:{value:"basic",name:"tb.rulenode.credentials-basic"},"cert.PEM":{value:"cert.PEM",name:"tb.rulenode.credentials-pem"}}}).name}]));
//# sourceMappingURL=rulenode-core-config.js.map
\ No newline at end of file
tools/pom.xml 2(+1 -1)
diff --git a/tools/pom.xml b/tools/pom.xml
index 916baa1..89263d3 100644
--- a/tools/pom.xml
+++ b/tools/pom.xml
@@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
- <version>2.0.1</version>
+ <version>2.0.2</version>
<artifactId>thingsboard</artifactId>
</parent>
<groupId>org.thingsboard</groupId>
transport/coap/pom.xml 2(+1 -1)
diff --git a/transport/coap/pom.xml b/transport/coap/pom.xml
index b9c0d95..41e6fc9 100644
--- a/transport/coap/pom.xml
+++ b/transport/coap/pom.xml
@@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
- <version>2.0.1</version>
+ <version>2.0.2</version>
<artifactId>transport</artifactId>
</parent>
<groupId>org.thingsboard.transport</groupId>
diff --git a/transport/coap/src/test/java/org/thingsboard/server/transport/coap/CoapServerTest.java b/transport/coap/src/test/java/org/thingsboard/server/transport/coap/CoapServerTest.java
index 4815056..e2fd7cd 100644
--- a/transport/coap/src/test/java/org/thingsboard/server/transport/coap/CoapServerTest.java
+++ b/transport/coap/src/test/java/org/thingsboard/server/transport/coap/CoapServerTest.java
@@ -130,6 +130,13 @@ public class CoapServerTest {
}
}
}
+
+ @Override
+ public void onDeviceAdded(Device device) {
+
+ }
+
+
};
}
transport/http/pom.xml 2(+1 -1)
diff --git a/transport/http/pom.xml b/transport/http/pom.xml
index ba944ae..367b707 100644
--- a/transport/http/pom.xml
+++ b/transport/http/pom.xml
@@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
- <version>2.0.1</version>
+ <version>2.0.2</version>
<artifactId>transport</artifactId>
</parent>
<groupId>org.thingsboard.transport</groupId>
transport/mqtt/pom.xml 2(+1 -1)
diff --git a/transport/mqtt/pom.xml b/transport/mqtt/pom.xml
index fbdf2ea..a2d1371 100644
--- a/transport/mqtt/pom.xml
+++ b/transport/mqtt/pom.xml
@@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
- <version>2.0.1</version>
+ <version>2.0.2</version>
<artifactId>transport</artifactId>
</parent>
<groupId>org.thingsboard.transport</groupId>
diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java
index f666bb8..cc825b9 100644
--- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java
+++ b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java
@@ -92,6 +92,7 @@ public class GatewaySessionCtx {
device.setType(deviceType);
device = deviceService.saveDevice(device);
relationService.saveRelationAsync(new EntityRelation(gateway.getId(), device.getId(), "Created"));
+ processor.onDeviceAdded(device);
}
GatewayDeviceSessionCtx ctx = new GatewayDeviceSessionCtx(this, device);
devices.put(deviceName, ctx);
@@ -154,6 +155,7 @@ public class GatewaySessionCtx {
GatewayDeviceSessionCtx deviceSessionCtx = devices.get(deviceName);
processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(),
new BasicAdaptorToSessionActorMsg(deviceSessionCtx, new ToDeviceRpcResponseMsg(requestId, data))));
+ ack(mqttMsg);
} else {
throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json);
}
transport/pom.xml 2(+1 -1)
diff --git a/transport/pom.xml b/transport/pom.xml
index 8f07bd4..72aee87 100644
--- a/transport/pom.xml
+++ b/transport/pom.xml
@@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
- <version>2.0.1</version>
+ <version>2.0.2</version>
<artifactId>thingsboard</artifactId>
</parent>
<groupId>org.thingsboard</groupId>
ui/package.json 2(+1 -1)
diff --git a/ui/package.json b/ui/package.json
index 85ee4b8..83bca57 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -1,7 +1,7 @@
{
"name": "thingsboard",
"private": true,
- "version": "2.0.1",
+ "version": "2.0.2",
"description": "Thingsboard UI",
"licenses": [
{
ui/pom.xml 2(+1 -1)
diff --git a/ui/pom.xml b/ui/pom.xml
index fa5f90f..e836feb 100644
--- a/ui/pom.xml
+++ b/ui/pom.xml
@@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
- <version>2.0.1</version>
+ <version>2.0.2</version>
<artifactId>thingsboard</artifactId>
</parent>
<groupId>org.thingsboard</groupId>
ui/src/app/locale/locale.constant.js 38(+19 -19)
diff --git a/ui/src/app/locale/locale.constant.js b/ui/src/app/locale/locale.constant.js
index 5bb599c..29f6f8e 100644
--- a/ui/src/app/locale/locale.constant.js
+++ b/ui/src/app/locale/locale.constant.js
@@ -512,7 +512,7 @@ export default angular.module('thingsboard.locale', [])
"configuration-error": "Configuration error",
"alias-resolution-error-title": "Dashboard aliases configuration error",
"invalid-aliases-config": "Unable to find any devices matching to some of the aliases filter.<br/>" +
- "Please contact your administrator in order to resolve this issue.",
+ "Please contact your administrator in order to resolve this issue.",
"select-devices": "Select devices",
"assignedToCustomer": "Assigned to customer",
"assignedToCustomers": "Assigned to customers",
@@ -897,13 +897,13 @@ export default angular.module('thingsboard.locale', [])
"opc-identity": "Identity",
"opc-keystore": "Keystore",
"opc-type": "Type",
- "opc-keystore-type":"Type",
- "opc-keystore-location":"Location *",
- "opc-keystore-password":"Password",
- "opc-keystore-alias":"Alias",
- "opc-keystore-key-password":"Key password",
- "opc-device-node-pattern":"Device node pattern",
- "opc-device-name-pattern":"Device name pattern",
+ "opc-keystore-type": "Type",
+ "opc-keystore-location": "Location *",
+ "opc-keystore-password": "Password",
+ "opc-keystore-alias": "Alias",
+ "opc-keystore-key-password": "Key password",
+ "opc-device-node-pattern": "Device node pattern",
+ "opc-device-name-pattern": "Device name pattern",
"modbus-server": "Servers/slaves",
"modbus-add-server": "Add server/slave",
"modbus-add-server-prompt": "Please add server/slave",
@@ -918,7 +918,7 @@ export default angular.module('thingsboard.locale', [])
"modbus-stopbits-range": "Stop bits should be in a range from 1 to 2.",
"modbus-unit-id": "Unit ID",
"modbus-unit-id-range": "Unit ID should be in a range from 1 to 247.",
- "modbus-device-name":"Device name",
+ "modbus-device-name": "Device name",
"modbus-poll-period": "Poll period (ms)",
"modbus-attributes-poll-period": "Attributes poll period (ms)",
"modbus-timeseries-poll-period": "Timeseries poll period (ms)",
@@ -941,8 +941,8 @@ export default angular.module('thingsboard.locale', [])
"not-available": "Not available"
},
- "export-extensions-configuration":"Export extensions configuration",
- "import-extensions-configuration":"Import extensions configuration",
+ "export-extensions-configuration": "Export extensions configuration",
+ "import-extensions-configuration": "Import extensions configuration",
"import-extensions": "Import extensions",
"import-extension": "Import extension",
"export-extension": "Export extension",
@@ -1445,14 +1445,6 @@ export default angular.module('thingsboard.locale', [])
"material-icons": "Material icons",
"show-all": "Show all icons"
},
- "language": {
- "language": "Language",
- "en_US": "English",
- "ko_KR": "Korean",
- "zh_CN": "Chinese",
- "ru_RU": "Russian",
- "es_ES": "Spanish"
- },
"custom": {
"widget-action": {
"action-cell-button": "Action cell button",
@@ -1460,6 +1452,14 @@ export default angular.module('thingsboard.locale', [])
"marker-click": "On marker click",
"tooltip-tag-action": "Tooltip tag action"
}
+ },
+ "language": {
+ "language": "Language",
+ "en_US": "English",
+ "ko_KR": "Korean",
+ "zh_CN": "Chinese",
+ "ru_RU": "Russian",
+ "es_ES": "Spanish"
}
}
}
ui/src/app/locale/locale.constant-es.js 1957(+1237 -720)
diff --git a/ui/src/app/locale/locale.constant-es.js b/ui/src/app/locale/locale.constant-es.js
index af8b5b1..c6d2f96 100644
--- a/ui/src/app/locale/locale.constant-es.js
+++ b/ui/src/app/locale/locale.constant-es.js
@@ -13,804 +13,1321 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
- export default function addLocaleSpanish(locales) {
+export default function addLocaleSpanish(locales) {
var es_ES = {
- "access": {
- "unauthorized": "No autorizado",
- "unauthorized-access": "Acceso no autorizado",
- "unauthorized-access-text": "Debes iniciar sesión para tener acceso a este recurso!",
- "access-forbidden": "Acceso Prohibido",
- "access-forbidden-text": "No tienes derechos para acceder a esta ubicación!<br/>Intenta iniciar sesión con otro usuario si todavía quieres acceder a esta ubicación.",
- "refresh-token-expired": "La sesión ha expirado",
- "refresh-token-failed": "No se puede actualizar la sesión"
+ "access": {
+ "unauthorized": "No autorizado",
+ "unauthorized-access": "Acceso no autorizado",
+ "unauthorized-access-text": "Debes iniciar sesión para tener acceso a este recurso!",
+ "access-forbidden": "Acceso Prohibido",
+ "access-forbidden-text": "No tienes derechos para acceder a esta ubicación!<br/>Intenta iniciar sesión con otro usuario si todavía quieres acceder a esta ubicación.",
+ "refresh-token-expired": "La sesión ha expirado",
+ "refresh-token-failed": "No se puede actualizar la sesión"
},
"action": {
- "activate": "Activar",
- "suspend": "Suspender",
- "save": "Guardar",
- "saveAs": "Guardar como",
- "cancel": "Cancelar",
- "ok": "OK",
- "delete": "Borrar",
- "add": "Agregar",
- "yes": "Si",
- "no": "No",
- "update": "Actualizar",
- "remove": "Eliminar",
- "search": "Buscar",
- "assign": "Asignar",
- "unassign": "Cancelar asignación",
- "share": "Compartir",
- "make-private": "Hacer privado",
- "apply": "Aplicar",
- "apply-changes": "Aplicar cambios",
- "edit-mode": "Modo Edición",
- "enter-edit-mode": "Modo Edición",
- "decline-changes": "Descartar cambios",
- "close": "Cerrar",
- "back": "Atrás",
- "run": "Correr",
- "sign-in": "Regístrate!",
- "edit": "Editar",
- "view": "Ver",
- "create": "Crear",
- "drag": "Arrastrar",
- "refresh": "Refrescar",
- "undo": "Deshacer",
- "copy": "Copiar",
- "paste": "Pegar",
- "import": "Importar",
- "export": "Exportar",
- "share-via": "Compartir vía {{provider}}"
+ "activate": "Activar",
+ "suspend": "Suspender",
+ "save": "Guardar",
+ "saveAs": "Guardar como",
+ "cancel": "Cancelar",
+ "ok": "OK",
+ "delete": "Borrar",
+ "add": "Agregar",
+ "yes": "Si",
+ "no": "No",
+ "update": "Actualizar",
+ "remove": "Eliminar",
+ "search": "Buscar",
+ "assign": "Asignar",
+ "unassign": "Cancelar asignación",
+ "share": "Compartir",
+ "make-private": "Hacer privado",
+ "apply": "Aplicar",
+ "apply-changes": "Aplicar cambios",
+ "edit-mode": "Modo Edición",
+ "enter-edit-mode": "Modo Edición",
+ "decline-changes": "Descartar cambios",
+ "close": "Cerrar",
+ "back": "Atrás",
+ "run": "Correr",
+ "sign-in": "Regístrate!",
+ "edit": "Editar",
+ "view": "Ver",
+ "create": "Crear",
+ "drag": "Arrastrar",
+ "refresh": "Refrescar",
+ "undo": "Deshacer",
+ "copy": "Copiar",
+ "paste": "Pegar",
+ "import": "Importar",
+ "export": "Exportar",
+ "share-via": "Compartir vía {{provider}}"
},
"aggregation": {
- "aggregation": "Agregación",
- "function": "Función de Agregación",
- "limit": "Valores Max",
- "group-interval": "Intervalo de agrupación",
- "min": "Min",
- "max": "Max",
- "avg": "Promedio",
- "sum": "Suma",
- "count": "Cuenta",
- "none": "Ninguno"
+ "aggregation": "Agregación",
+ "function": "Función de Agregación",
+ "limit": "Valores Max",
+ "group-interval": "Intervalo de agrupación",
+ "min": "Min",
+ "max": "Max",
+ "avg": "Promedio",
+ "sum": "Suma",
+ "count": "Cuenta",
+ "none": "Ninguno"
},
"admin": {
- "general": "General",
- "general-settings": "Ajustes General",
- "outgoing-mail": "Mail de Salida",
- "outgoing-mail-settings": "Ajustes del Mail de Salida",
- "system-settings": "Sistema",
- "test-mail-sent": "Mail de prueba enviado correctamente!",
- "base-url": "URL Base",
- "base-url-required": "URL Base requerida.",
- "mail-from": "Mail Desde",
- "mail-from-required": "Mail Desde requerido.",
- "smtp-protocol": "Protocolo SMTP",
- "smtp-host": "Host SMTP",
- "smtp-host-required": "Host SMTP requerido.",
- "smtp-port": "Puerto SMTP",
- "smtp-port-required": "Debe ingresar un Puerto SMTP.",
- "smtp-port-invalid": "No parece un Puerto SMTP valido.",
- "timeout-msec": "Timeout (ms)",
- "timeout-required": "Timeout requerido.",
- "timeout-invalid": "No parece un Timeout valido.",
- "enable-tls": "Habilitar TLS",
- "send-test-mail": "Enviar mail de prueba"
+ "general": "General",
+ "general-settings": "Ajustes General",
+ "outgoing-mail": "Mail de Salida",
+ "outgoing-mail-settings": "Ajustes del Mail de Salida",
+ "system-settings": "Sistema",
+ "test-mail-sent": "Mail de prueba enviado correctamente!",
+ "base-url": "URL Base",
+ "base-url-required": "URL Base requerida.",
+ "mail-from": "Mail Desde",
+ "mail-from-required": "Mail Desde requerido.",
+ "smtp-protocol": "Protocolo SMTP",
+ "smtp-host": "Host SMTP",
+ "smtp-host-required": "Host SMTP requerido.",
+ "smtp-port": "Puerto SMTP",
+ "smtp-port-required": "Debe ingresar un Puerto SMTP.",
+ "smtp-port-invalid": "No parece un Puerto SMTP valido.",
+ "timeout-msec": "Timeout (ms)",
+ "timeout-required": "Timeout requerido.",
+ "timeout-invalid": "No parece un Timeout valido.",
+ "enable-tls": "Habilitar TLS",
+ "send-test-mail": "Enviar mail de prueba"
+ },
+ "alarm": { // TODO
+ "alarm": "Alarm",
+ "alarms": "Alarms",
+ "select-alarm": "Select alarm",
+ "no-alarms-matching": "No alarms matching '{{entity}}' were found.",
+ "alarm-required": "Alarm is required",
+ "alarm-status": "Alarm status",
+ "search-status": {
+ "ANY": "Any",
+ "ACTIVE": "Active",
+ "CLEARED": "Cleared",
+ "ACK": "Acknowledged",
+ "UNACK": "Unacknowledged"
+ },
+ "display-status": {
+ "ACTIVE_UNACK": "Active Unacknowledged",
+ "ACTIVE_ACK": "Active Acknowledged",
+ "CLEARED_UNACK": "Cleared Unacknowledged",
+ "CLEARED_ACK": "Cleared Acknowledged"
+ },
+ "no-alarms-prompt": "No alarms found",
+ "created-time": "Created time",
+ "type": "Type",
+ "severity": "Severity",
+ "originator": "Originator",
+ "originator-type": "Originator type",
+ "details": "Details",
+ "status": "Status",
+ "alarm-details": "Alarm details",
+ "start-time": "Start time",
+ "end-time": "End time",
+ "ack-time": "Acknowledged time",
+ "clear-time": "Cleared time",
+ "severity-critical": "Critical",
+ "severity-major": "Major",
+ "severity-minor": "Minor",
+ "severity-warning": "Warning",
+ "severity-indeterminate": "Indeterminate",
+ "acknowledge": "Acknowledge",
+ "clear": "Clear",
+ "search": "Search alarms",
+ "selected-alarms": "{ count, select, 1 {1 alarm} other {# alarms} } selected",
+ "no-data": "No data to display",
+ "polling-interval": "Alarms polling interval (sec)",
+ "polling-interval-required": "Alarms polling interval is required.",
+ "min-polling-interval-message": "At least 1 sec polling interval is allowed.",
+ "aknowledge-alarms-title": "Acknowledge { count, select, 1 {1 alarm} other {# alarms} }",
+ "aknowledge-alarms-text": "Are you sure you want to acknowledge { count, select, 1 {1 alarm} other {# alarms} }?",
+ "clear-alarms-title": "Clear { count, select, 1 {1 alarm} other {# alarms} }",
+ "clear-alarms-text": "Are you sure you want to clear { count, select, 1 {1 alarm} other {# alarms} }?"
+ },
+ "alias": { // TODO
+ "add": "Add alias",
+ "edit": "Edit alias",
+ "name": "Alias name",
+ "name-required": "Alias name is required",
+ "duplicate-alias": "Alias with same name is already exists.",
+ "filter-type-single-entity": "Single entity",
+ "filter-type-entity-list": "Entity list",
+ "filter-type-entity-name": "Entity name",
+ "filter-type-state-entity": "Entity from dashboard state",
+ "filter-type-state-entity-description": "Entity taken from dashboard state parameters",
+ "filter-type-asset-type": "Asset type",
+ "filter-type-asset-type-description": "Assets of type '{{assetType}}'",
+ "filter-type-asset-type-and-name-description": "Assets of type '{{assetType}}' and with name starting with '{{prefix}}'",
+ "filter-type-device-type": "Device type",
+ "filter-type-device-type-description": "Devices of type '{{deviceType}}'",
+ "filter-type-device-type-and-name-description": "Devices of type '{{deviceType}}' and with name starting with '{{prefix}}'",
+ "filter-type-relations-query": "Relations query",
+ "filter-type-relations-query-description": "{{entities}} that have {{relationType}} relation {{direction}} {{rootEntity}}",
+ "filter-type-asset-search-query": "Asset search query",
+ "filter-type-asset-search-query-description": "Assets with types {{assetTypes}} that have {{relationType}} relation {{direction}} {{rootEntity}}",
+ "filter-type-device-search-query": "Device search query",
+ "filter-type-device-search-query-description": "Devices with types {{deviceTypes}} that have {{relationType}} relation {{direction}} {{rootEntity}}",
+ "entity-filter": "Entity filter",
+ "resolve-multiple": "Resolve as multiple entities",
+ "filter-type": "Filter type",
+ "filter-type-required": "Filter type is required.",
+ "entity-filter-no-entity-matched": "No entities matching specified filter were found.",
+ "no-entity-filter-specified": "No entity filter specified",
+ "root-state-entity": "Use dashboard state entity as root",
+ "root-entity": "Root entity",
+ "state-entity-parameter-name": "State entity parameter name",
+ "default-state-entity": "Default state entity",
+ "default-entity-parameter-name": "By default",
+ "max-relation-level": "Max relation level",
+ "unlimited-level": "Unlimited level",
+ "state-entity": "Dashboard state entity",
+ "all-entities": "All entities",
+ "any-relation": "any"
+ },
+ "asset": { // TODO
+ "asset": "Asset",
+ "assets": "Assets",
+ "management": "Asset management",
+ "view-assets": "View Assets",
+ "add": "Add Asset",
+ "assign-to-customer": "Assign to customer",
+ "assign-asset-to-customer": "Assign Asset(s) To Customer",
+ "assign-asset-to-customer-text": "Please select the assets to assign to the customer",
+ "no-assets-text": "No assets found",
+ "assign-to-customer-text": "Please select the customer to assign the asset(s)",
+ "public": "Public",
+ "assignedToCustomer": "Assigned to customer",
+ "make-public": "Make asset public",
+ "make-private": "Make asset private",
+ "unassign-from-customer": "Unassign from customer",
+ "delete": "Delete asset",
+ "asset-public": "Asset is public",
+ "asset-type": "Asset type",
+ "asset-type-required": "Asset type is required.",
+ "select-asset-type": "Select asset type",
+ "enter-asset-type": "Enter asset type",
+ "any-asset": "Any asset",
+ "no-asset-types-matching": "No asset types matching '{{entitySubtype}}' were found.",
+ "asset-type-list-empty": "No asset types selected.",
+ "asset-types": "Asset types",
+ "name": "Name",
+ "name-required": "Name is required.",
+ "description": "Description",
+ "type": "Type",
+ "type-required": "Type is required.",
+ "details": "Details",
+ "events": "Events",
+ "add-asset-text": "Add new asset",
+ "asset-details": "Asset details",
+ "assign-assets": "Assign assets",
+ "assign-assets-text": "Assign { count, select, 1 {1 asset} other {# assets} } to customer",
+ "delete-assets": "Delete assets",
+ "unassign-assets": "Unassign assets",
+ "unassign-assets-action-title": "Unassign { count, select, 1 {1 asset} other {# assets} } from customer",
+ "assign-new-asset": "Assign new asset",
+ "delete-asset-title": "Are you sure you want to delete the asset '{{assetName}}'?",
+ "delete-asset-text": "Be careful, after the confirmation the asset and all related data will become unrecoverable.",
+ "delete-assets-title": "Are you sure you want to delete { count, select, 1 {1 asset} other {# assets} }?",
+ "delete-assets-action-title": "Delete { count, select, 1 {1 asset} other {# assets} }",
+ "delete-assets-text": "Be careful, after the confirmation all selected assets will be removed and all related data will become unrecoverable.",
+ "make-public-asset-title": "Are you sure you want to make the asset '{{assetName}}' public?",
+ "make-public-asset-text": "After the confirmation the asset and all its data will be made public and accessible by others.",
+ "make-private-asset-title": "Are you sure you want to make the asset '{{assetName}}' private?",
+ "make-private-asset-text": "After the confirmation the asset and all its data will be made private and won't be accessible by others.",
+ "unassign-asset-title": "Are you sure you want to unassign the asset '{{assetName}}'?",
+ "unassign-asset-text": "After the confirmation the asset will be unassigned and won't be accessible by the customer.",
+ "unassign-asset": "Unassign asset",
+ "unassign-assets-title": "Are you sure you want to unassign { count, select, 1 {1 asset} other {# assets} }?",
+ "unassign-assets-text": "After the confirmation all selected assets will be unassigned and won't be accessible by the customer.",
+ "copyId": "Copy asset Id",
+ "idCopiedMessage": "Asset Id has been copied to clipboard",
+ "select-asset": "Select asset",
+ "no-assets-matching": "No assets matching '{{entity}}' were found.",
+ "asset-required": "Asset is required",
+ "name-starts-with": "Asset name starts with"
},
"attribute": {
- "attributes": "Atributos",
- "latest-telemetry": "Última telemetría",
- "attributes-scope": "Alcance de los atributos del dispositivo",
- "scope-latest-telemetry": "Última telemetría",
- "scope-client": "Atributos del Cliente",
- "scope-server": "Atributos del Servidor",
- "scope-shared": "Atributos Compartidos",
- "add": "Agregar atributo",
- "key": "Clave",
- "key-required": "Clave del atributo requerida.",
- "value": "Valor",
- "value-required": "Valor del atributo requerido.",
- "delete-attributes-title": "¿Estás seguro que quieres eliminar { count, select, 1 {1 atributo} other {# atributos} }?",
- "delete-attributes-text": "Ten cuidado, luego de confirmar el atributo será eliminado, y la información relacionada será irrecuperable.",
- "delete-attributes": "Borrar atributo",
- "enter-attribute-value": "Ingresar valor del atributo",
- "show-on-widget": "Mostrar en Widget",
- "widget-mode": "Widget",
- "next-widget": "Widget siguiente",
- "prev-widget": "Widget anterior",
- "add-to-dashboard": "Agregar al Panel",
- "add-widget-to-dashboard": "Agregar widget al Panel",
- "selected-attributes": "{ count, select, 1 {1 atributo} other {# atributos} } seleccionados",
- "selected-telemetry": "{ count, select, 1 {1 unidad de telemetría } other {# unidades de telemetría} } seleccionadas."
+ "attributes": "Atributos",
+ "latest-telemetry": "Última telemetría",
+ "attributes-scope": "Alcance de los atributos del dispositivo",
+ "scope-latest-telemetry": "Última telemetría",
+ "scope-client": "Atributos del Cliente",
+ "scope-server": "Atributos del Servidor",
+ "scope-shared": "Atributos Compartidos",
+ "add": "Agregar atributo",
+ "key": "Clave",
+ "key-required": "Clave del atributo requerida.",
+ "value": "Valor",
+ "value-required": "Valor del atributo requerido.",
+ "delete-attributes-title": "¿Estás seguro que quieres eliminar { count, select, 1 {1 atributo} other {# atributos} }?",
+ "delete-attributes-text": "Ten cuidado, luego de confirmar el atributo será eliminado, y la información relacionada será irrecuperable.",
+ "delete-attributes": "Borrar atributo",
+ "enter-attribute-value": "Ingresar valor del atributo",
+ "show-on-widget": "Mostrar en Widget",
+ "widget-mode": "Widget",
+ "next-widget": "Widget siguiente",
+ "prev-widget": "Widget anterior",
+ "add-to-dashboard": "Agregar al Panel",
+ "add-widget-to-dashboard": "Agregar widget al Panel",
+ "selected-attributes": "{ count, select, 1 {1 atributo} other {# atributos} } seleccionados",
+ "selected-telemetry": "{ count, select, 1 {1 unidad de telemetría } other {# unidades de telemetría} } seleccionadas."
+ },
+ "audit-log": { // TODO
+ "audit": "Audit",
+ "audit-logs": "Audit Logs",
+ "timestamp": "Timestamp",
+ "entity-type": "Entity Type",
+ "entity-name": "Entity Name",
+ "user": "User",
+ "type": "Type",
+ "status": "Status",
+ "details": "Details",
+ "type-added": "Added",
+ "type-deleted": "Deleted",
+ "type-updated": "Updated",
+ "type-attributes-updated": "Attributes updated",
+ "type-attributes-deleted": "Attributes deleted",
+ "type-rpc-call": "RPC call",
+ "type-credentials-updated": "Credentials updated",
+ "type-assigned-to-customer": "Assigned to Customer",
+ "type-unassigned-from-customer": "Unassigned from Customer",
+ "type-activated": "Activated",
+ "type-suspended": "Suspended",
+ "type-credentials-read": "Credentials read",
+ "type-attributes-read": "Attributes read",
+ "status-success": "Success",
+ "status-failure": "Failure",
+ "audit-log-details": "Audit log details",
+ "no-audit-logs-prompt": "No logs found",
+ "action-data": "Action data",
+ "failure-details": "Failure details",
+ "search": "Search audit logs",
+ "clear-search": "Clear search"
},
"confirm-on-exit": {
- "message": "Tienes cambios sin guardar. ¿Estás seguro que quieres abandonar la página?",
- "html-message": "Tienes cambios sin guardar.<br/>¿Estás seguro que quieres abandonar la página?",
- "title": "Cambios sin guardar"
+ "message": "Tienes cambios sin guardar. ¿Estás seguro que quieres abandonar la página?",
+ "html-message": "Tienes cambios sin guardar.<br/>¿Estás seguro que quieres abandonar la página?",
+ "title": "Cambios sin guardar"
},
"contact": {
- "country": "País",
- "city": "Ciudad",
- "state": "Estado/Provincia",
- "postal-code": "Código Postal",
- "postal-code-invalid": "Solo se permiten dígitos.",
- "address": "Dirección",
- "address2": "Dirección 2",
- "phone": "Teléfono",
- "email": "Email",
- "no-address": "Sin Dirección"
+ "country": "País",
+ "city": "Ciudad",
+ "state": "Estado/Provincia",
+ "postal-code": "Código Postal",
+ "postal-code-invalid": "Solo se permiten dígitos.",
+ "address": "Dirección",
+ "address2": "Dirección 2",
+ "phone": "Teléfono",
+ "email": "Email",
+ "no-address": "Sin Dirección"
},
"common": {
- "username": "Usuario",
- "password": "Contraseña",
- "enter-username": "Ingresa el nombre de usuario.",
- "enter-password": "Ingresa la contraseña",
- "enter-search": "Ingresa búsqueda"
+ "username": "Usuario",
+ "password": "Contraseña",
+ "enter-username": "Ingresa el nombre de usuario.",
+ "enter-password": "Ingresa la contraseña",
+ "enter-search": "Ingresa búsqueda"
+ },
+ "content-type": { // TODO
+ "json": "Json",
+ "text": "Text",
+ "binary": "Binary (Base64)"
},
"customer": {
- "customers": "Clientes",
- "management": "Gestión de Clientes",
- "dashboard": "Panel del Cliente",
- "dashboards": "Paneles del Cliente",
- "devices": "Panel del Cliente",
- "public-dashboards": "Paneles Públicos",
- "public-devices": "Dispositivos Públicos",
- "add": "Agregar cliente",
- "delete": "Borrar cliente",
- "manage-customer-users": "Gestionar usuarios del cliente",
- "manage-customer-devices": "Gestionar dispositivos del cliente",
- "manage-customer-dashboards": "Gestionar paneles del cliente",
- "manage-public-devices": "Gestionar dispositivos públicos",
- "manage-public-dashboards": "Gestionar paneles públicos",
- "add-customer-text": "Agregar nuevo cliente",
- "no-customers-text": "No se encontrar clientes",
- "customer-details": "Detalles del cliente",
- "delete-customer-title": "¿Estás seguro que quieres eliminar el cliente '{{customerTitle}}'?",
- "delete-customer-text": "Ten cuidado, luego de confirmar el cliente será eliminado y toda la información relacionada será irrecuperable.",
- "delete-customers-title": "¿Estás seguro que quieres eliminar { count, select, 1 {1 cliente} other {# clientes} }?",
- "delete-customers-action-title": "Borrar { count, select, 1 {1 cliente} other {# clientes} }",
- "delete-customers-text": "Ten cuidado, luego de confirmar todos los clientes seleccionados serán eliminados y su información relacionada será irrecuperable.",
- "manage-users": "Gestionar usuarios",
- "manage-devices": "Gestionar dispositivos",
- "manage-dashboards": "Gestionar paneles",
- "title": "Título",
- "title-required": "Título requerido.",
- "description": "Descripción"
+ "customers": "Clientes",
+ "management": "Gestión de Clientes",
+ "dashboard": "Panel del Cliente",
+ "dashboards": "Paneles del Cliente",
+ "devices": "Panel del Cliente",
+ "public-dashboards": "Paneles Públicos",
+ "public-devices": "Dispositivos Públicos",
+ "add": "Agregar cliente",
+ "delete": "Borrar cliente",
+ "manage-customer-users": "Gestionar usuarios del cliente",
+ "manage-customer-devices": "Gestionar dispositivos del cliente",
+ "manage-customer-dashboards": "Gestionar paneles del cliente",
+ "manage-public-devices": "Gestionar dispositivos públicos",
+ "manage-public-dashboards": "Gestionar paneles públicos",
+ "add-customer-text": "Agregar nuevo cliente",
+ "no-customers-text": "No se encontrar clientes",
+ "customer-details": "Detalles del cliente",
+ "delete-customer-title": "¿Estás seguro que quieres eliminar el cliente '{{customerTitle}}'?",
+ "delete-customer-text": "Ten cuidado, luego de confirmar el cliente será eliminado y toda la información relacionada será irrecuperable.",
+ "delete-customers-title": "¿Estás seguro que quieres eliminar { count, select, 1 {1 cliente} other {# clientes} }?",
+ "delete-customers-action-title": "Borrar { count, select, 1 {1 cliente} other {# clientes} }",
+ "delete-customers-text": "Ten cuidado, luego de confirmar todos los clientes seleccionados serán eliminados y su información relacionada será irrecuperable.",
+ "manage-users": "Gestionar usuarios",
+ "manage-devices": "Gestionar dispositivos",
+ "manage-dashboards": "Gestionar paneles",
+ "title": "Título",
+ "title-required": "Título requerido.",
+ "description": "Descripción"
},
"datetime": {
- "date-from": "Fecha desde",
- "time-from": "Tiempo desde",
- "date-to": "Fecha hasta",
- "time-to": "Tiempo hasta"
+ "date-from": "Fecha desde",
+ "time-from": "Tiempo desde",
+ "date-to": "Fecha hasta",
+ "time-to": "Tiempo hasta"
},
"dashboard": {
- "dashboard": "Panel",
- "dashboards": "Paneles",
- "management": "Gestión de Paneles",
- "view-dashboards": "Ver paneles",
- "add": "Agregar Panel",
- "assign-dashboard-to-customer": "Asignar panel(es) a cliente",
- "assign-dashboard-to-customer-text": "Por favor, seleccione algún panel para asignar al Cliente.",
- "assign-to-customer-text": "Por favor, seleccione algún cliente para asignar al(los) panel(es).",
- "assign-to-customer": "Asignar a cliente",
- "unassign-from-customer": "Desasignar del cliente",
- "make-public": "Hacer panel público",
- "make-private": "Hacer panel privado",
- "no-dashboards-text": "Ningún panel encontrado",
- "no-widgets": "Ningún widget configurado",
- "add-widget": "Agregar nuevo widget",
- "title": "Titulo",
- "select-widget-title": "Seleccionar widget",
- "select-widget-subtitle": "Lista de tipos de widgets",
- "delete": "Eliminar panel",
- "title-required": "Título requerido.",
- "description": "Descripción",
- "details": "Detalles",
- "dashboard-details": "Detalles del panel",
- "add-dashboard-text": "Agregar nuevo panel",
- "assign-dashboards": "Asignar paneles",
- "assign-new-dashboard": "Asignar nuevo panel",
- "assign-dashboards-text": "Asignar { count, select, 1 {1 panel} other {# paneles} } al cliente",
- "delete-dashboards": "Eliminar paneles",
- "unassign-dashboards": "Desasignar paneles",
- "unassign-dashboards-action-title": "Desasignar { count, select, 1 {1 paneles} other {# paneles} } del cliente",
- "delete-dashboard-title": "¿Estás seguro que quieres eliminar el panel '{{dashboardTitle}}'?",
- "delete-dashboard-text": "Ten cuidado, el panel seleccionado será eliminado y la información relacionada sera irrecuperable.",
- "delete-dashboards-title": "¿Estás seguro que quieres eliminar { count, select, 1 {1 panel} other {# paneles} }?",
- "delete-dashboards-action-title": "Eliminar { count, select, 1 {1 panel} other {# paneles} }",
- "delete-dashboards-text": "Ten cuidado, los paneles seleccionados serán eliminados y la información relacionada será irrecuperable.",
- "unassign-dashboard-title": "¿Estás seguro que quieres desasignar el panel '{{dashboardTitle}}'?",
- "unassign-dashboard-text": "Luego de confirmar, el panel será desasignado y no podrá ser accesible por el cliente.",
- "unassign-dashboard": "Desasignar panel",
- "unassign-dashboards-title": "¿Estás seguro que quieres desasignar { count, select, 1 {1 panel} other {# paneles} }?",
- "unassign-dashboards-text": "Luego de confirmar, los paneles seleccionados serán desasignados y no podrán ser accesibles por el cliente.",
- "public-dashboard-title": "El panel ahora es público",
- "public-dashboard-text": "Tu panel <b>{{dashboardTitle}}</b> es ahora público y podrá ser accedido desde: <a href='{{publicLink}}' target='_blank'>aquí</a>:",
- "public-dashboard-notice": "<b>Nota:</b> No olvides hacer públicos los dispositivos relacionados para acceder a sus datos.",
- "make-private-dashboard-title": "¿Estás seguro que quieres hacer el panel '{{dashboardTitle}}' privado?",
- "make-private-dashboard-text": "Luego de confirmar, el panel será privado y no podrá ser accesible por otros.",
- "make-private-dashboard": "Hacer panel privado",
- "socialshare-text": "'{{dashboardTitle}}' powered by ThingsBoard",
- "socialshare-title": "'{{dashboardTitle}}' powered by ThingsBoard",
- "select-dashboard": "Seleccionar panel",
- "no-dashboards-matching": "Panel '{{entity}}' no encontrado.",
- "dashboard-required": "Panel requerido.",
- "select-existing": "Seleccionar paneles existentes",
- "create-new": "Crear nuevo panel",
- "new-dashboard-title": "Nuevo título",
- "open-dashboard": "Abrir panel",
- "set-background": "Definir fondo",
- "background-color": "Color de fondo",
- "background-image": "Imagen de fondo",
- "background-size-mode": "Modo tamaño de fondo",
- "no-image": "No se ha seleccionado ningúna imagen",
- "drop-image": "Suelte una imagen o haga clic para seleccionar un archivo para cargar.",
- "settings": "Ajustes",
- "columns-count": "Número de columnas",
- "columns-count-required": "Número de columnas requerido.",
- "min-columns-count-message": "Solo se permite un número mínimo de 10 columnas.",
- "max-columns-count-message": "Solo se permite un número máximo de 1000 columnas.",
- "widgets-margins": "Margen entre widgets",
- "horizontal-margin": "Margen horizontal",
- "horizontal-margin-required": "Margen horizontal requerido.",
- "min-horizontal-margin-message": "Solo se permite margen horizontal mínimo de 0.",
- "max-horizontal-margin-message": "Solo se permite margen horizontal máximo de 50.",
- "vertical-margin": "Margen vertical",
- "vertical-margin-required": "Margen vertical requerido.",
- "min-vertical-margin-message": "Solo se permite margen vertical mínimo de 0.",
- "max-vertical-margin-message": "Solo se permite margen vertical máximo de 50.",
- "display-title": "Mostrar título del panel",
- "title-color": "Color del título",
- "display-device-selection": "Mostrar selección de dispositivo",
- "display-dashboard-timewindow": "Mostrar ventana de tiempo",
- "display-dashboard-export": "Mostrar exportar",
- "import": "Importar panel",
- "export": "Exportar panel",
- "export-failed-error": "Imposible exportar panel: {{error}}",
- "create-new-dashboard": "Crear nuevo panel",
- "dashboard-file": "Archivo del panel",
- "invalid-dashboard-file-error": "Imposible importar panel: Estructura de datos inválida.",
- "dashboard-import-missing-aliases-title": "Configurar alias utilizados por el panel importado",
- "create-new-widget": "Crear nuevo widget",
- "import-widget": "Importar widget",
- "widget-file": "Archivo de widget",
- "invalid-widget-file-error": "Imposible importar widget: Estructura de datos inválida.",
- "widget-import-missing-aliases-title": "Configurar alias utilizados por el widget",
- "open-toolbar": "Abrir toolbar del panel",
- "close-toolbar": "Cerrar toolbar",
- "configuration-error": "Error de configuración",
- "alias-resolution-error-title": "Error de configuración de alias del panel",
- "invalid-aliases-config": "No se puede encontrar ningún dispositivo que coincida con algunos de los alias de filtro.<br/>" +
- "Póngase en contacto con su administrador para resolver este problema.",
- "select-devices": "Seleccionar dispositivos",
- "assignedToCustomer": "Asignado al cliente",
- "public": "Público",
- "public-link": "Link público",
- "copy-public-link": "Copiar link público",
- "public-link-copied-message": "El link público del panel se ha copiado al portapapeles"
+ "dashboard": "Panel",
+ "dashboards": "Paneles",
+ "management": "Gestión de Paneles",
+ "view-dashboards": "Ver paneles",
+ "add": "Agregar Panel",
+ "assign-dashboard-to-customer": "Asignar panel(es) a cliente",
+ "assign-dashboard-to-customer-text": "Por favor, seleccione algún panel para asignar al Cliente.",
+ "assign-to-customer-text": "Por favor, seleccione algún cliente para asignar al(los) panel(es).",
+ "assign-to-customer": "Asignar a cliente",
+ "unassign-from-customer": "Desasignar del cliente",
+ "make-public": "Hacer panel público",
+ "make-private": "Hacer panel privado",
+ "no-dashboards-text": "Ningún panel encontrado",
+ "no-widgets": "Ningún widget configurado",
+ "add-widget": "Agregar nuevo widget",
+ "title": "Titulo",
+ "select-widget-title": "Seleccionar widget",
+ "select-widget-subtitle": "Lista de tipos de widgets",
+ "delete": "Eliminar panel",
+ "title-required": "Título requerido.",
+ "description": "Descripción",
+ "details": "Detalles",
+ "dashboard-details": "Detalles del panel",
+ "add-dashboard-text": "Agregar nuevo panel",
+ "assign-dashboards": "Asignar paneles",
+ "assign-new-dashboard": "Asignar nuevo panel",
+ "assign-dashboards-text": "Asignar { count, select, 1 {1 panel} other {# paneles} } al cliente",
+ "delete-dashboards": "Eliminar paneles",
+ "unassign-dashboards": "Desasignar paneles",
+ "unassign-dashboards-action-title": "Desasignar { count, select, 1 {1 paneles} other {# paneles} } del cliente",
+ "delete-dashboard-title": "¿Estás seguro que quieres eliminar el panel '{{dashboardTitle}}'?",
+ "delete-dashboard-text": "Ten cuidado, el panel seleccionado será eliminado y la información relacionada sera irrecuperable.",
+ "delete-dashboards-title": "¿Estás seguro que quieres eliminar { count, select, 1 {1 panel} other {# paneles} }?",
+ "delete-dashboards-action-title": "Eliminar { count, select, 1 {1 panel} other {# paneles} }",
+ "delete-dashboards-text": "Ten cuidado, los paneles seleccionados serán eliminados y la información relacionada será irrecuperable.",
+ "unassign-dashboard-title": "¿Estás seguro que quieres desasignar el panel '{{dashboardTitle}}'?",
+ "unassign-dashboard-text": "Luego de confirmar, el panel será desasignado y no podrá ser accesible por el cliente.",
+ "unassign-dashboard": "Desasignar panel",
+ "unassign-dashboards-title": "¿Estás seguro que quieres desasignar { count, select, 1 {1 panel} other {# paneles} }?",
+ "unassign-dashboards-text": "Luego de confirmar, los paneles seleccionados serán desasignados y no podrán ser accesibles por el cliente.",
+ "public-dashboard-title": "El panel ahora es público",
+ "public-dashboard-text": "Tu panel <b>{{dashboardTitle}}</b> es ahora público y podrá ser accedido desde: <a href='{{publicLink}}' target='_blank'>aquí</a>:",
+ "public-dashboard-notice": "<b>Nota:</b> No olvides hacer públicos los dispositivos relacionados para acceder a sus datos.",
+ "make-private-dashboard-title": "¿Estás seguro que quieres hacer el panel '{{dashboardTitle}}' privado?",
+ "make-private-dashboard-text": "Luego de confirmar, el panel será privado y no podrá ser accesible por otros.",
+ "make-private-dashboard": "Hacer panel privado",
+ "socialshare-text": "'{{dashboardTitle}}' powered by ThingsBoard",
+ "socialshare-title": "'{{dashboardTitle}}' powered by ThingsBoard",
+ "select-dashboard": "Seleccionar panel",
+ "no-dashboards-matching": "Panel '{{entity}}' no encontrado.",
+ "dashboard-required": "Panel requerido.",
+ "select-existing": "Seleccionar paneles existentes",
+ "create-new": "Crear nuevo panel",
+ "new-dashboard-title": "Nuevo título",
+ "open-dashboard": "Abrir panel",
+ "set-background": "Definir fondo",
+ "background-color": "Color de fondo",
+ "background-image": "Imagen de fondo",
+ "background-size-mode": "Modo tamaño de fondo",
+ "no-image": "No se ha seleccionado ningúna imagen",
+ "drop-image": "Suelte una imagen o haga clic para seleccionar un archivo para cargar.",
+ "settings": "Ajustes",
+ "columns-count": "Número de columnas",
+ "columns-count-required": "Número de columnas requerido.",
+ "min-columns-count-message": "Solo se permite un número mínimo de 10 columnas.",
+ "max-columns-count-message": "Solo se permite un número máximo de 1000 columnas.",
+ "widgets-margins": "Margen entre widgets",
+ "horizontal-margin": "Margen horizontal",
+ "horizontal-margin-required": "Margen horizontal requerido.",
+ "min-horizontal-margin-message": "Solo se permite margen horizontal mínimo de 0.",
+ "max-horizontal-margin-message": "Solo se permite margen horizontal máximo de 50.",
+ "vertical-margin": "Margen vertical",
+ "vertical-margin-required": "Margen vertical requerido.",
+ "min-vertical-margin-message": "Solo se permite margen vertical mínimo de 0.",
+ "max-vertical-margin-message": "Solo se permite margen vertical máximo de 50.",
+ "display-title": "Mostrar título del panel",
+ "title-color": "Color del título",
+ "display-device-selection": "Mostrar selección de dispositivo",
+ "display-dashboard-timewindow": "Mostrar ventana de tiempo",
+ "display-dashboard-export": "Mostrar exportar",
+ "import": "Importar panel",
+ "export": "Exportar panel",
+ "export-failed-error": "Imposible exportar panel: {{error}}",
+ "create-new-dashboard": "Crear nuevo panel",
+ "dashboard-file": "Archivo del panel",
+ "invalid-dashboard-file-error": "Imposible importar panel: Estructura de datos inválida.",
+ "dashboard-import-missing-aliases-title": "Configurar alias utilizados por el panel importado",
+ "create-new-widget": "Crear nuevo widget",
+ "import-widget": "Importar widget",
+ "widget-file": "Archivo de widget",
+ "invalid-widget-file-error": "Imposible importar widget: Estructura de datos inválida.",
+ "widget-import-missing-aliases-title": "Configurar alias utilizados por el widget",
+ "open-toolbar": "Abrir toolbar del panel",
+ "close-toolbar": "Cerrar toolbar",
+ "configuration-error": "Error de configuración",
+ "alias-resolution-error-title": "Error de configuración de alias del panel",
+ "invalid-aliases-config": "No se puede encontrar ningún dispositivo que coincida con algunos de los alias de filtro.<br/>" +
+ "Póngase en contacto con su administrador para resolver este problema.",
+ "select-devices": "Seleccionar dispositivos",
+ "assignedToCustomer": "Asignado al cliente",
+ "public": "Público",
+ "public-link": "Link público",
+ "copy-public-link": "Copiar link público",
+ "public-link-copied-message": "El link público del panel se ha copiado al portapapeles"
},
"datakey": {
- "settings": "Ajustes",
- "advanced": "Avanzado",
- "label": "Etiqueta",
- "color": "Color",
- "data-generation-func": "Función de generación de datos",
- "use-data-post-processing-func": "Usar funcíon de post-procesamiendo de datos",
- "configuration": "Ajustes de clave de datos",
- "timeseries": "Serie de tiempos",
- "attributes": "Atributos",
- "timeseries-required": "Series de tiempo del dispositivo requerido.",
- "timeseries-or-attributes-required": "Series de tiempo/Atributos requeridos.",
- "function-types": "Tipos de funciones",
- "function-types-required": "Tipos de funciones requerido."
+ "settings": "Ajustes",
+ "advanced": "Avanzado",
+ "label": "Etiqueta",
+ "color": "Color",
+ "data-generation-func": "Función de generación de datos",
+ "use-data-post-processing-func": "Usar funcíon de post-procesamiendo de datos",
+ "configuration": "Ajustes de clave de datos",
+ "timeseries": "Serie de tiempos",
+ "attributes": "Atributos",
+ "timeseries-required": "Series de tiempo del dispositivo requerido.",
+ "timeseries-or-attributes-required": "Series de tiempo/Atributos requeridos.",
+ "function-types": "Tipos de funciones",
+ "function-types-required": "Tipos de funciones requerido."
},
"datasource": {
- "type": "Típo de fuente de datos",
- "add-datasource-prompt": "Por favor, agrega una fuente de datos"
+ "type": "Típo de fuente de datos",
+ "add-datasource-prompt": "Por favor, agrega una fuente de datos"
},
"details": {
- "edit-mode": "Modo Edición",
- "toggle-edit-mode": "Ir a Modo Edición"
+ "edit-mode": "Modo Edición",
+ "toggle-edit-mode": "Ir a Modo Edición"
},
"device": {
- "device": "Dispositivo",
- "device-required": "Dispositivo requerido.",
- "devices": "Dispositivos",
- "management": "Gestión de Dispositivos",
- "view-devices": "Ver dispositivos",
- "device-alias": "Alias de dispositivo",
- "aliases": "Alias de dispositivos",
- "no-alias-matching": "'{{alias}}' no encontrado.",
- "no-aliases-found": "Ningún alias encontrado.",
- "no-key-matching": "'{{key}}' no encontrado.",
- "no-keys-found": "Ninguna clave encontrada.",
- "create-new-alias": "Crear nuevo alias!",
- "create-new-key": "Crear nueva clave!",
- "duplicate-alias-error": "Alias duplicado '{{alias}}'.<br> El alias de los dispositivos deben ser únicos dentro del panel.",
- "configure-alias": "Configurar alias '{{alias}}'",
- "no-devices-matching": "No se encontró dispositivo '{{entity}}'",
- "alias": "Alias",
- "alias-required": "Alias de dispositivo requerido.",
- "remove-alias": "Eliminar alias",
- "add-alias": "Agregar alias",
- "name-starts-with": "Nombre empieza con",
- "device-list": "Lista de dispositivos",
- "use-device-name-filter": "Usar filtro",
- "device-list-empty": "Ningún dispositivo seleccionado.",
- "device-name-filter-required": "Nombre de filtro requerido.",
- "device-name-filter-no-device-matched": "Ningún dispositivo encontrado que comience con '{{device}}'.",
- "add": "Agregar dispositivo",
- "assign-to-customer": "Asignar a cliente",
- "assign-device-to-customer": "Asignar dispositivo(s) a Cliente",
- "assign-device-to-customer-text": "Por favor, seleccione los dispositivos que serán asignados al cliente",
- "make-public": "Hacer dispositivo público",
- "make-private": "Hacer dispositivo privado",
- "no-devices-text": "Ningún dispositivo encontrado",
- "assign-to-customer-text": "Por favor, seleccione el cliente para asignar el(los) dispositivo(s)",
- "device-details": "Detalles del dispositivo",
- "add-device-text": "Agregar nuevo dispositivo",
- "credentials": "Credenciales",
- "manage-credentials": "Gestionar credenciales",
- "delete": "Eliminar dispositivo",
- "assign-devices": "Asignar dispositivo",
- "assign-devices-text": "Asignar { count, select, 1 {1 dispositivo} other {# dispositivos} } al cliente",
- "delete-devices": "Eliminar dispositivo",
- "unassign-from-customer": "Desasignar del cliente",
- "unassign-devices": "Desasignar dispositivos",
- "unassign-devices-action-title": "Desasignar { count, select, 1 {1 dispositivo} other {# dispositivos} } del cliente",
- "assign-new-device": "Asignar nuevo dispositivo",
- "make-public-device-title": "¿Estás seguro que quieres hacer el dispositivo '{{deviceName}}' público?",
- "make-public-device-text": "Luego de confirmar, el dispositivo y la información relacionada serán públicos y podrá ser accesible por otros.",
- "make-private-device-title": "¿Estás seguro que quieres hacer el dispositivo '{{deviceName}}' privado?",
- "make-private-device-text": "Luego de confirmar, el dispositivo y la información relacionada serán privados y no podrá ser accesible por otros.",
- "view-credentials": "Ver credenciales",
- "delete-device-title": "¿Estás seguro que quieres eliminar el dispositivo '{{deviceName}}'?",
- "delete-device-text": "Ten cuidado, luego de confirmar los dispositivos serán eliminados y la información relacionada será irrecuperable.",
- "delete-devices-title": "¿Estás seguro que quieres eliminar { count, select, 1 {1 dispositivo} other {# dispositivos} }?",
- "delete-devices-action-title": "Eliminar { count, select, 1 {1 dispositivo} other {# dispositivos} }",
- "delete-devices-text": "Ten cuidado, luego de confirmar los dispositivos seleccionados serán eliminados y la información relacionada será irrecuperable.",
- "unassign-device-title": "¿Estás seguro que quieres desasignar el dispositivo '{{deviceName}}'?",
- "unassign-device-text": "Luego de confirmar el dispositivo será desasignado y no podrá ser accesible por el cliente.",
- "unassign-device": "Desasignar dispositivo",
- "unassign-devices-title": "¿Estás seguro que quieres desasignar { count, select, 1 {1 dispositivo} other {# dispositivos} }?",
- "unassign-devices-text": "Luego de confirmar los dispositivos seleccionados serán desasignados y no podrán ser accedidos por el cliente.",
- "device-credentials": "Credenciales del dispositivo",
- "credentials-type": "Tipo de credencial",
- "access-token": "Access token",
- "access-token-required": "Access token requerido.",
- "access-token-invalid": "Access token debe tener entre 1 a 20 caracteres.",
- "rsa-key": "Clave pública RSA",
- "rsa-key-required": "Clave pública RSA requerida.",
- "secret": "Secreta",
- "secret-required": "Secreta requerida.",
- "name": "Nombre",
- "name-required": "Nombre requerido.",
- "description": "Descripción",
- "events": "Eventos",
- "details": "Detalles",
- "copyId": "Copiar ID",
- "copyAccessToken": "Copiar access token",
- "idCopiedMessage": "Id del dispositivo copiado al portapapeles",
- "accessTokenCopiedMessage": "Access token del dispositivo copiado al portapapeles",
- "assignedToCustomer": "Asignado al cliente",
- "unable-delete-device-alias-title": "Imposible eliminar alias del dispositivo",
- "unable-delete-device-alias-text": "Alias '{{deviceAlias}}' no puede ser eliminado. Esta siendo usado por el(los) widget(s):<br/>{{widgetsList}}",
- "is-gateway": "Es gateway",
- "public": "Público",
- "device-public": "Dispositivo público"
+ "device": "Dispositivo",
+ "device-required": "Dispositivo requerido.",
+ "devices": "Dispositivos",
+ "management": "Gestión de Dispositivos",
+ "view-devices": "Ver dispositivos",
+ "device-alias": "Alias de dispositivo",
+ "aliases": "Alias de dispositivos",
+ "no-alias-matching": "'{{alias}}' no encontrado.",
+ "no-aliases-found": "Ningún alias encontrado.",
+ "no-key-matching": "'{{key}}' no encontrado.",
+ "no-keys-found": "Ninguna clave encontrada.",
+ "create-new-alias": "Crear nuevo alias!",
+ "create-new-key": "Crear nueva clave!",
+ "duplicate-alias-error": "Alias duplicado '{{alias}}'.<br> El alias de los dispositivos deben ser únicos dentro del panel.",
+ "configure-alias": "Configurar alias '{{alias}}'",
+ "no-devices-matching": "No se encontró dispositivo '{{entity}}'",
+ "alias": "Alias",
+ "alias-required": "Alias de dispositivo requerido.",
+ "remove-alias": "Eliminar alias",
+ "add-alias": "Agregar alias",
+ "name-starts-with": "Nombre empieza con",
+ "device-list": "Lista de dispositivos",
+ "use-device-name-filter": "Usar filtro",
+ "device-list-empty": "Ningún dispositivo seleccionado.",
+ "device-name-filter-required": "Nombre de filtro requerido.",
+ "device-name-filter-no-device-matched": "Ningún dispositivo encontrado que comience con '{{device}}'.",
+ "add": "Agregar dispositivo",
+ "assign-to-customer": "Asignar a cliente",
+ "assign-device-to-customer": "Asignar dispositivo(s) a Cliente",
+ "assign-device-to-customer-text": "Por favor, seleccione los dispositivos que serán asignados al cliente",
+ "make-public": "Hacer dispositivo público",
+ "make-private": "Hacer dispositivo privado",
+ "no-devices-text": "Ningún dispositivo encontrado",
+ "assign-to-customer-text": "Por favor, seleccione el cliente para asignar el(los) dispositivo(s)",
+ "device-details": "Detalles del dispositivo",
+ "add-device-text": "Agregar nuevo dispositivo",
+ "credentials": "Credenciales",
+ "manage-credentials": "Gestionar credenciales",
+ "delete": "Eliminar dispositivo",
+ "assign-devices": "Asignar dispositivo",
+ "assign-devices-text": "Asignar { count, select, 1 {1 dispositivo} other {# dispositivos} } al cliente",
+ "delete-devices": "Eliminar dispositivo",
+ "unassign-from-customer": "Desasignar del cliente",
+ "unassign-devices": "Desasignar dispositivos",
+ "unassign-devices-action-title": "Desasignar { count, select, 1 {1 dispositivo} other {# dispositivos} } del cliente",
+ "assign-new-device": "Asignar nuevo dispositivo",
+ "make-public-device-title": "¿Estás seguro que quieres hacer el dispositivo '{{deviceName}}' público?",
+ "make-public-device-text": "Luego de confirmar, el dispositivo y la información relacionada serán públicos y podrá ser accesible por otros.",
+ "make-private-device-title": "¿Estás seguro que quieres hacer el dispositivo '{{deviceName}}' privado?",
+ "make-private-device-text": "Luego de confirmar, el dispositivo y la información relacionada serán privados y no podrá ser accesible por otros.",
+ "view-credentials": "Ver credenciales",
+ "delete-device-title": "¿Estás seguro que quieres eliminar el dispositivo '{{deviceName}}'?",
+ "delete-device-text": "Ten cuidado, luego de confirmar los dispositivos serán eliminados y la información relacionada será irrecuperable.",
+ "delete-devices-title": "¿Estás seguro que quieres eliminar { count, select, 1 {1 dispositivo} other {# dispositivos} }?",
+ "delete-devices-action-title": "Eliminar { count, select, 1 {1 dispositivo} other {# dispositivos} }",
+ "delete-devices-text": "Ten cuidado, luego de confirmar los dispositivos seleccionados serán eliminados y la información relacionada será irrecuperable.",
+ "unassign-device-title": "¿Estás seguro que quieres desasignar el dispositivo '{{deviceName}}'?",
+ "unassign-device-text": "Luego de confirmar el dispositivo será desasignado y no podrá ser accesible por el cliente.",
+ "unassign-device": "Desasignar dispositivo",
+ "unassign-devices-title": "¿Estás seguro que quieres desasignar { count, select, 1 {1 dispositivo} other {# dispositivos} }?",
+ "unassign-devices-text": "Luego de confirmar los dispositivos seleccionados serán desasignados y no podrán ser accedidos por el cliente.",
+ "device-credentials": "Credenciales del dispositivo",
+ "credentials-type": "Tipo de credencial",
+ "access-token": "Access token",
+ "access-token-required": "Access token requerido.",
+ "access-token-invalid": "Access token debe tener entre 1 a 20 caracteres.",
+ "rsa-key": "Clave pública RSA",
+ "rsa-key-required": "Clave pública RSA requerida.",
+ "secret": "Secreta",
+ "secret-required": "Secreta requerida.",
+ "name": "Nombre",
+ "name-required": "Nombre requerido.",
+ "description": "Descripción",
+ "events": "Eventos",
+ "details": "Detalles",
+ "copyId": "Copiar ID",
+ "copyAccessToken": "Copiar access token",
+ "idCopiedMessage": "Id del dispositivo copiado al portapapeles",
+ "accessTokenCopiedMessage": "Access token del dispositivo copiado al portapapeles",
+ "assignedToCustomer": "Asignado al cliente",
+ "unable-delete-device-alias-title": "Imposible eliminar alias del dispositivo",
+ "unable-delete-device-alias-text": "Alias '{{deviceAlias}}' no puede ser eliminado. Esta siendo usado por el(los) widget(s):<br/>{{widgetsList}}",
+ "is-gateway": "Es gateway",
+ "public": "Público",
+ "device-public": "Dispositivo público"
},
"dialog": {
- "close": "Cerrar cuadro de diálogo"
+ "close": "Cerrar cuadro de diálogo"
},
"error": {
- "unable-to-connect": "Imposible conectar con el servidor! Por favor, revise su conexión a internet.",
- "unhandled-error-code": "Código de error no manejado: {{errorCode}}",
- "unknown-error": "Error desconocido"
+ "unable-to-connect": "Imposible conectar con el servidor! Por favor, revise su conexión a internet.",
+ "unhandled-error-code": "Código de error no manejado: {{errorCode}}",
+ "unknown-error": "Error desconocido"
+ },
+ "entity": { // TODO
+ "entity": "Entity",
+ "entities": "Entities",
+ "aliases": "Entity aliases",
+ "entity-alias": "Entity alias",
+ "unable-delete-entity-alias-title": "Unable to delete entity alias",
+ "unable-delete-entity-alias-text": "Entity alias '{{entityAlias}}' can't be deleted as it used by the following widget(s):<br/>{{widgetsList}}",
+ "duplicate-alias-error": "Duplicate alias found '{{alias}}'.<br>Entity aliases must be unique whithin the dashboard.",
+ "missing-entity-filter-error": "Filter is missing for alias '{{alias}}'.",
+ "configure-alias": "Configure '{{alias}}' alias",
+ "alias": "Alias",
+ "alias-required": "Entity alias is required.",
+ "remove-alias": "Remove entity alias",
+ "add-alias": "Add entity alias",
+ "entity-list": "Entity list",
+ "entity-type": "Entity type",
+ "entity-types": "Entity types",
+ "entity-type-list": "Entity type list",
+ "any-entity": "Any entity",
+ "enter-entity-type": "Enter entity type",
+ "no-entities-matching": "No entities matching '{{entity}}' were found.",
+ "no-entity-types-matching": "No entity types matching '{{entityType}}' were found.",
+ "name-starts-with": "Name starts with",
+ "use-entity-name-filter": "Use filter",
+ "entity-list-empty": "No entities selected.",
+ "entity-type-list-empty": "No entity types selected.",
+ "entity-name-filter-required": "Entity name filter is required.",
+ "entity-name-filter-no-entity-matched": "No entities starting with '{{entity}}' were found.",
+ "all-subtypes": "All",
+ "select-entities": "Select entities",
+ "no-aliases-found": "No aliases found.",
+ "no-alias-matching": "'{{alias}}' not found.",
+ "create-new-alias": "Create a new one!",
+ "key": "Key",
+ "key-name": "Key name",
+ "no-keys-found": "No keys found.",
+ "no-key-matching": "'{{key}}' not found.",
+ "create-new-key": "Create a new one!",
+ "type": "Type",
+ "type-required": "Entity type is required.",
+ "type-device": "Device",
+ "type-devices": "Devices",
+ "list-of-devices": "{ count, select, 1 {One device} other {List of # devices} }",
+ "device-name-starts-with": "Devices whose names start with '{{prefix}}'",
+ "type-asset": "Asset",
+ "type-assets": "Assets",
+ "list-of-assets": "{ count, select, 1 {One asset} other {List of # assets} }",
+ "asset-name-starts-with": "Assets whose names start with '{{prefix}}'",
+ "type-rule": "Rule",
+ "type-rules": "Rules",
+ "list-of-rules": "{ count, select, 1 {One rule} other {List of # rules} }",
+ "rule-name-starts-with": "Rules whose names start with '{{prefix}}'",
+ "type-plugin": "Plugin",
+ "type-plugins": "Plugins",
+ "list-of-plugins": "{ count, select, 1 {One plugin} other {List of # plugins} }",
+ "plugin-name-starts-with": "Plugins whose names start with '{{prefix}}'",
+ "type-tenant": "Tenant",
+ "type-tenants": "Tenants",
+ "list-of-tenants": "{ count, select, 1 {One tenant} other {List of # tenants} }",
+ "tenant-name-starts-with": "Tenants whose names start with '{{prefix}}'",
+ "type-customer": "Customer",
+ "type-customers": "Customers",
+ "list-of-customers": "{ count, select, 1 {One customer} other {List of # customers} }",
+ "customer-name-starts-with": "Customers whose names start with '{{prefix}}'",
+ "type-user": "User",
+ "type-users": "Users",
+ "list-of-users": "{ count, select, 1 {One user} other {List of # users} }",
+ "user-name-starts-with": "Users whose names start with '{{prefix}}'",
+ "type-dashboard": "Dashboard",
+ "type-dashboards": "Dashboards",
+ "list-of-dashboards": "{ count, select, 1 {One dashboard} other {List of # dashboards} }",
+ "dashboard-name-starts-with": "Dashboards whose names start with '{{prefix}}'",
+ "type-alarm": "Alarm",
+ "type-alarms": "Alarms",
+ "list-of-alarms": "{ count, select, 1 {One alarms} other {List of # alarms} }",
+ "alarm-name-starts-with": "Alarms whose names start with '{{prefix}}'",
+ "type-rulechain": "Rule chain",
+ "type-rulechains": "Rule chains",
+ "list-of-rulechains": "{ count, select, 1 {One rule chain} other {List of # rule chains} }",
+ "rulechain-name-starts-with": "Rule chains whose names start with '{{prefix}}'",
+ "type-current-customer": "Current Customer",
+ "search": "Search entities",
+ "selected-entities": "{ count, select, 1 {1 entity} other {# entities} } selected",
+ "entity-name": "Entity name",
+ "details": "Entity details",
+ "no-entities-prompt": "No entities found",
+ "no-data": "No data to display"
},
"event": {
- "event-type": "Tipo de evento",
- "type-error": "Error",
- "type-lc-event": "Ciclo de vida",
- "type-stats": "Estadísticas",
- "no-events-prompt": "Ningún evento encontrado.",
- "error": "Error",
- "alarm": "Alarma",
- "event-time": "Hora del evento",
- "server": "Servidor",
- "body": "Cuerpo",
- "method": "Método",
- "event": "Evento",
- "status": "Status",
- "success": "Éxito",
- "failed": "Fallo",
- "messages-processed": "Mensajes procesados",
- "errors-occurred": "Ocurrieron errores"
+ "event-type": "Tipo de evento",
+ "type-error": "Error",
+ "type-lc-event": "Ciclo de vida",
+ "type-stats": "Estadísticas",
+ "no-events-prompt": "Ningún evento encontrado.",
+ "error": "Error",
+ "alarm": "Alarma",
+ "event-time": "Hora del evento",
+ "server": "Servidor",
+ "body": "Cuerpo",
+ "method": "Método",
+ "event": "Evento",
+ "status": "Status",
+ "success": "Éxito",
+ "failed": "Fallo",
+ "messages-processed": "Mensajes procesados",
+ "errors-occurred": "Ocurrieron errores"
+ },
+ "extension": { // TODO
+ "extensions": "Extensions",
+ "selected-extensions": "{ count, select, 1 {1 extension} other {# extensions} } selected",
+ "type": "Type",
+ "key": "Key",
+ "value": "Value",
+ "id": "Id",
+ "extension-id": "Extension id",
+ "extension-type": "Extension type",
+ "transformer-json": "JSON *",
+ "unique-id-required": "Current extension id already exists.",
+ "delete": "Delete extension",
+ "add": "Add extension",
+ "edit": "Edit extension",
+ "delete-extension-title": "Are you sure you want to delete the extension '{{extensionId}}'?",
+ "delete-extension-text": "Be careful, after the confirmation the extension and all related data will become unrecoverable.",
+ "delete-extensions-title": "Are you sure you want to delete { count, select, 1 {1 extension} other {# extensions} }?",
+ "delete-extensions-text": "Be careful, after the confirmation all selected extensions will be removed.",
+ "converters": "Converters",
+ "converter-id": "Converter id",
+ "configuration": "Configuration",
+ "converter-configurations": "Converter configurations",
+ "token": "Security token",
+ "add-converter": "Add converter",
+ "add-config": "Add converter configuration",
+ "device-name-expression": "Device name expression",
+ "device-type-expression": "Device type expression",
+ "custom": "Custom",
+ "to-double": "To Double",
+ "transformer": "Transformer",
+ "json-required": "Transformer json is required.",
+ "json-parse": "Unable to parse transformer json.",
+ "attributes": "Attributes",
+ "add-attribute": "Add attribute",
+ "add-map": "Add mapping element",
+ "timeseries": "Timeseries",
+ "add-timeseries": "Add timeseries",
+ "field-required": "Field is required",
+ "brokers": "Brokers",
+ "add-broker": "Add broker",
+ "host": "Host",
+ "port": "Port",
+ "port-range": "Port should be in a range from 1 to 65535.",
+ "ssl": "Ssl",
+ "credentials": "Credentials",
+ "username": "Username",
+ "password": "Password",
+ "retry-interval": "Retry interval in milliseconds",
+ "anonymous": "Anonymous",
+ "basic": "Basic",
+ "pem": "PEM",
+ "ca-cert": "CA certificate file *",
+ "private-key": "Private key file *",
+ "cert": "Certificate file *",
+ "no-file": "No file selected.",
+ "drop-file": "Drop a file or click to select a file to upload.",
+ "mapping": "Mapping",
+ "topic-filter": "Topic filter",
+ "converter-type": "Converter type",
+ "converter-json": "Json",
+ "json-name-expression": "Device name json expression",
+ "topic-name-expression": "Device name topic expression",
+ "json-type-expression": "Device type json expression",
+ "topic-type-expression": "Device type topic expression",
+ "attribute-key-expression": "Attribute key expression",
+ "attr-json-key-expression": "Attribute key json expression",
+ "attr-topic-key-expression": "Attribute key topic expression",
+ "request-id-expression": "Request id expression",
+ "request-id-json-expression": "Request id json expression",
+ "request-id-topic-expression": "Request id topic expression",
+ "response-topic-expression": "Response topic expression",
+ "value-expression": "Value expression",
+ "topic": "Topic",
+ "timeout": "Timeout in milliseconds",
+ "converter-json-required": "Converter json is required.",
+ "converter-json-parse": "Unable to parse converter json.",
+ "filter-expression": "Filter expression",
+ "connect-requests": "Connect requests",
+ "add-connect-request": "Add connect request",
+ "disconnect-requests": "Disconnect requests",
+ "add-disconnect-request": "Add disconnect request",
+ "attribute-requests": "Attribute requests",
+ "add-attribute-request": "Add attribute request",
+ "attribute-updates": "Attribute updates",
+ "add-attribute-update": "Add attribute update",
+ "server-side-rpc": "Server side RPC",
+ "add-server-side-rpc-request": "Add server-side RPC request",
+ "device-name-filter": "Device name filter",
+ "attribute-filter": "Attribute filter",
+ "method-filter": "Method filter",
+ "request-topic-expression": "Request topic expression",
+ "response-timeout": "Response timeout in milliseconds",
+ "topic-expression": "Topic expression",
+ "client-scope": "Client scope",
+ "add-device": "Add device",
+ "opc-server": "Servers",
+ "opc-add-server": "Add server",
+ "opc-add-server-prompt": "Please add server",
+ "opc-application-name": "Application name",
+ "opc-application-uri": "Application uri",
+ "opc-scan-period-in-seconds": "Scan period in seconds",
+ "opc-security": "Security",
+ "opc-identity": "Identity",
+ "opc-keystore": "Keystore",
+ "opc-type": "Type",
+ "opc-keystore-type": "Type",
+ "opc-keystore-location": "Location *",
+ "opc-keystore-password": "Password",
+ "opc-keystore-alias": "Alias",
+ "opc-keystore-key-password": "Key password",
+ "opc-device-node-pattern": "Device node pattern",
+ "opc-device-name-pattern": "Device name pattern",
+ "modbus-server": "Servers/slaves",
+ "modbus-add-server": "Add server/slave",
+ "modbus-add-server-prompt": "Please add server/slave",
+ "modbus-transport": "Transport",
+ "modbus-port-name": "Serial port name",
+ "modbus-encoding": "Encoding",
+ "modbus-parity": "Parity",
+ "modbus-baudrate": "Baud rate",
+ "modbus-databits": "Data bits",
+ "modbus-stopbits": "Stop bits",
+ "modbus-databits-range": "Data bits should be in a range from 7 to 8.",
+ "modbus-stopbits-range": "Stop bits should be in a range from 1 to 2.",
+ "modbus-unit-id": "Unit ID",
+ "modbus-unit-id-range": "Unit ID should be in a range from 1 to 247.",
+ "modbus-device-name": "Device name",
+ "modbus-poll-period": "Poll period (ms)",
+ "modbus-attributes-poll-period": "Attributes poll period (ms)",
+ "modbus-timeseries-poll-period": "Timeseries poll period (ms)",
+ "modbus-poll-period-range": "Poll period should be positive value.",
+ "modbus-tag": "Tag",
+ "modbus-function": "Function",
+ "modbus-register-address": "Register address",
+ "modbus-register-address-range": "Register address should be in a range from 0 to 65535.",
+ "modbus-register-bit-index": "Bit index",
+ "modbus-register-bit-index-range": "Bit index should be in a range from 0 to 15.",
+ "modbus-register-count": "Register count",
+ "modbus-register-count-range": "Register count should be a positive value.",
+ "modbus-byte-order": "Byte order",
+
+ "sync": {
+ "status": "Status",
+ "sync": "Sync",
+ "not-sync": "Not sync",
+ "last-sync-time": "Last sync time",
+ "not-available": "Not available"
+ },
+
+ "export-extensions-configuration": "Export extensions configuration",
+ "import-extensions-configuration": "Import extensions configuration",
+ "import-extensions": "Import extensions",
+ "import-extension": "Import extension",
+ "export-extension": "Export extension",
+ "file": "Extensions file",
+ "invalid-file-error": "Invalid extension file"
},
"fullscreen": {
- "expand": "Expandir a Pantalla Completa",
- "exit": "Salir de Pantalla Completa",
- "toggle": "Cambiar el modo de Pantalla Completa",
- "fullscreen": "Pantalla Completa"
+ "expand": "Expandir a Pantalla Completa",
+ "exit": "Salir de Pantalla Completa",
+ "toggle": "Cambiar el modo de Pantalla Completa",
+ "fullscreen": "Pantalla Completa"
},
"function": {
- "function": "Función"
+ "function": "Función"
},
"grid": {
- "delete-item-title": "¿Estás seguro que quieres eliminar este item?",
- "delete-item-text": "Ten cuidado, luego de confirmar el item será eliminado y la información relacionada será irrecuperable.",
- "delete-items-title": "¿Estás seguro que quieres eliminar { count, select, 1 {1 item} other {# items} }?",
- "delete-items-action-title": "Eliminar { count, select, 1 {1 item} other {# items} }",
- "delete-items-text": "Ten cuidado, luego de confirmar los items seleccionados serán eliminados y la información relacionada será irrecuperable.",
- "add-item-text": "Agregar nuevo item",
- "no-items-text": "Ningún item encontrado",
- "item-details": "Detalles del item",
- "delete-item": "Borrar Item",
- "delete-items": "Borrar Items",
- "scroll-to-top": "Ir hacia arriba"
+ "delete-item-title": "¿Estás seguro que quieres eliminar este item?",
+ "delete-item-text": "Ten cuidado, luego de confirmar el item será eliminado y la información relacionada será irrecuperable.",
+ "delete-items-title": "¿Estás seguro que quieres eliminar { count, select, 1 {1 item} other {# items} }?",
+ "delete-items-action-title": "Eliminar { count, select, 1 {1 item} other {# items} }",
+ "delete-items-text": "Ten cuidado, luego de confirmar los items seleccionados serán eliminados y la información relacionada será irrecuperable.",
+ "add-item-text": "Agregar nuevo item",
+ "no-items-text": "Ningún item encontrado",
+ "item-details": "Detalles del item",
+ "delete-item": "Borrar Item",
+ "delete-items": "Borrar Items",
+ "scroll-to-top": "Ir hacia arriba"
},
"help": {
- "goto-help-page": "Ir a Página de Ayuda"
+ "goto-help-page": "Ir a Página de Ayuda"
},
"home": {
- "home": "Principal",
- "profile": "Perfil",
- "logout": "Salir",
- "menu": "Menu",
- "avatar": "Avatar",
- "open-user-menu": "Abrir menú de usuario"
+ "home": "Principal",
+ "profile": "Perfil",
+ "logout": "Salir",
+ "menu": "Menu",
+ "avatar": "Avatar",
+ "open-user-menu": "Abrir menú de usuario"
},
"import": {
- "no-file": "Ningún archivo seleccionado",
- "drop-file": "Arrastra un archivo JSON o clickea para seleccionar uno."
+ "no-file": "Ningún archivo seleccionado",
+ "drop-file": "Arrastra un archivo JSON o clickea para seleccionar uno."
},
"item": {
- "selected": "Seleccionado"
+ "selected": "Seleccionado"
},
"js-func": {
- "no-return-error": "La función debe retornar un valor!",
- "return-type-mismatch": "La función debe retornar un valor de tipo: '{{type}}'!"
+ "no-return-error": "La función debe retornar un valor!",
+ "return-type-mismatch": "La función debe retornar un valor de tipo: '{{type}}'!"
+ },
+ "key-val": { // TODO
+ "key": "Key",
+ "value": "Value",
+ "remove-entry": "Remove entry",
+ "add-entry": "Add entry",
+ "no-data": "No entries"
+ },
+ "layout": { // TODO
+ "layout": "Layout",
+ "manage": "Manage layouts",
+ "settings": "Layout settings",
+ "color": "Color",
+ "main": "Main",
+ "right": "Right",
+ "select": "Select target layout"
},
"legend": {
- "position": "Posición de leyenda",
- "show-max": "Mostrar máximo",
- "show-min": "Mostrar mínimo",
- "show-avg": "Mostrar promedio",
- "show-total": "Mostrar total",
- "settings": "Ajustes de leyenda.",
- "min": "min",
- "max": "max",
- "avg": "prom",
- "total": "total"
+ "position": "Posición de leyenda",
+ "show-max": "Mostrar máximo",
+ "show-min": "Mostrar mínimo",
+ "show-avg": "Mostrar promedio",
+ "show-total": "Mostrar total",
+ "settings": "Ajustes de leyenda.",
+ "min": "min",
+ "max": "max",
+ "avg": "prom",
+ "total": "total"
},
"login": {
- "login": "Ingresar",
- "request-password-reset": "Pedir restablecer contraseña",
- "reset-password": "Restablecer contraseña",
- "create-password": "Crear contraseña",
- "passwords-mismatch-error": "Las contraseñas deben ser las mismas!",
- "password-again": "Reingresa la contraseña",
- "sign-in": "Iniciar sesión",
- "username": "Usuario (email)",
- "remember-me": "Recordar",
- "forgot-password": "¿Olvidaste tu contraseña?",
- "password-reset": "Restablecer Contraseña",
- "new-password": "Nueva contraseña",
- "new-password-again": "Repita la nueva contraseña",
- "password-link-sent-message": "Se ha enviado el enlace de restablecimiento de contraseña con éxito!",
- "email": "Email"
- },
- "plugin": {
- "plugins": "Plugins",
- "delete": "Eliminar plugin",
- "activate": "Activar plugin",
- "suspend": "Suspender plugin",
- "active": "Activo",
- "suspended": "Suspendido",
- "name": "Nombre",
- "name-required": "Nombre requerido.",
- "description": "Descripción",
- "add": "Agregar Plugin",
- "delete-plugin-title": "¿Estás seguro que quieres eliminar el plugin '{{pluginName}}'?",
- "delete-plugin-text": "Ten cuidado, luego de confirmar el plugin será eliminado y la información relacionada será irrecuperable.",
- "delete-plugins-title": "¿Estás seguro que quieres eliminar { count, select, 1 {1 plugin} other {# plugins} }?",
- "delete-plugins-action-title": "Eliminar { count, select, 1 {1 plugin} other {# plugins} }",
- "delete-plugins-text": "Ten cuidado, luego de confirmar todos los plugins seleccionados serán eliminados y la información relacionada será irrecuperable.",
- "add-plugin-text": "Agregar nuevo plugin",
- "no-plugins-text": "Ningún plugin encontrado",
- "plugin-details": "Detalles",
- "api-token": "API token",
- "api-token-required": "API token requerido.",
- "type": "Tipo del plugin",
- "type-required": "Tipo requerido.",
- "configuration": "Ajustes del plugin",
- "system": "Sistema",
- "select-plugin": "plugin",
- "plugin": "Plugin",
- "no-plugins-matching": "No se encontraron plugins: '{{entity}}'",
- "plugin-required": "Plugin requerido.",
- "plugin-require-match": "Por favor, elija un plugin existente.",
- "events": "Eventos",
- "details": "Detalles",
- "import": "Importar plugin",
- "export": "Exportar plugin",
- "export-failed-error": "Imposible exportar plugin: {{error}}",
- "create-new-plugin": "Crear nuevo plugin",
- "plugin-file": "Archivo",
- "invalid-plugin-file-error": "Imposible de importar plugin: Estructura de datos inválida."
+ "login": "Ingresar",
+ "request-password-reset": "Pedir restablecer contraseña",
+ "reset-password": "Restablecer contraseña",
+ "create-password": "Crear contraseña",
+ "passwords-mismatch-error": "Las contraseñas deben ser las mismas!",
+ "password-again": "Reingresa la contraseña",
+ "sign-in": "Iniciar sesión",
+ "username": "Usuario (email)",
+ "remember-me": "Recordar",
+ "forgot-password": "¿Olvidaste tu contraseña?",
+ "password-reset": "Restablecer Contraseña",
+ "new-password": "Nueva contraseña",
+ "new-password-again": "Repita la nueva contraseña",
+ "password-link-sent-message": "Se ha enviado el enlace de restablecimiento de contraseña con éxito!",
+ "email": "Email"
},
"position": {
- "top": "Arriba",
- "bottom": "Abajo",
- "left": "Izquierda",
- "right": "Derecha"
+ "top": "Arriba",
+ "bottom": "Abajo",
+ "left": "Izquierda",
+ "right": "Derecha"
},
"profile": {
- "profile": "Perfil",
- "change-password": "Cambiar contraseña",
- "current-password": "Contraseña actual"
- },
- "rule": {
- "rules": "Reglas",
- "delete": "Eliminar regla",
- "activate": "Activar regla",
- "suspend": "Suspender regla",
- "active": "Activada",
- "suspended": "Suspendida",
- "name": "Nombre",
- "name-required": "Nombre requerido.",
- "description": "Descripción",
- "add": "Agregar Regla",
- "delete-rule-title": "¿Estás seguro que quieres eliminar la regla '{{ruleName}}'?",
- "delete-rule-text": "Ten cuidado, luego de confirmar la regla será eliminada y la información relacionada será irrecuperable.",
- "delete-rules-title": "¿Estás seguro que quieres eliminar { count, select, 1 {1 regla} other {# reglas} }?",
- "delete-rules-action-title": "Eliminar { count, select, 1 {1 regla} other {# reglas} }",
- "delete-rules-text": "Ten cuidado, luego de confirmar todas las reglas seleccionadas serán borradas y la información relacionada será irrecuperable.",
- "add-rule-text": "Agregar nueva regla",
- "no-rules-text": "Ninguna regla encontrada",
- "rule-details": "Detalles",
- "filters": "Filtros",
- "filter": "Filtro",
- "add-filter-prompt": "Por favor, ingresa un filtro",
- "remove-filter": "Eliminar filtro",
- "add-filter": "Agregar filtro",
- "filter-name": "Nombre",
- "filter-type": "Tipo",
- "edit-filter": "Editar filtro",
- "view-filter": "Ver filtro",
- "component-name": "Nombre",
- "component-name-required": "Nombre requerido.",
- "component-type": "Tipo",
- "component-type-required": "Tipo requerido.",
- "processor": "Procesador",
- "no-processor-configured": "Ningún procesador encontrado",
- "create-processor": "Crear procesador",
- "processor-name": "Nombre",
- "processor-type": "Tipo",
- "plugin-action": "Acción del Plugin",
- "action-name": "Nombre",
- "action-type": "Tipo",
- "create-action-prompt": "Por favor, crea una acción.",
- "create-action": "Crear acción",
- "details": "Detalles",
- "events": "Eventos",
- "system": "Sistema",
- "import": "Importar regla",
- "export": "Exportar regla",
- "export-failed-error": "Imposible de exportar regla: {{error}}",
- "create-new-rule": "Crear nueva regla",
- "rule-file": "Archivo",
- "invalid-rule-file-error": "Imposible de importar regla: Estructura de datos inválida."
- },
- "rule-plugin": {
- "management": "Gestión de Reglas y Plugins"
+ "profile": "Perfil",
+ "change-password": "Cambiar contraseña",
+ "current-password": "Contraseña actual"
+ },
+ "relation": { // TODO
+ "relations": "Relations",
+ "direction": "Direction",
+ "search-direction": {
+ "FROM": "From",
+ "TO": "To"
+ },
+ "direction-type": {
+ "FROM": "from",
+ "TO": "to"
+ },
+ "from-relations": "Outbound relations",
+ "to-relations": "Inbound relations",
+ "selected-relations": "{ count, select, 1 {1 relation} other {# relations} } selected",
+ "type": "Type",
+ "to-entity-type": "To entity type",
+ "to-entity-name": "To entity name",
+ "from-entity-type": "From entity type",
+ "from-entity-name": "From entity name",
+ "to-entity": "To entity",
+ "from-entity": "From entity",
+ "delete": "Delete relation",
+ "relation-type": "Relation type",
+ "relation-type-required": "Relation type is required.",
+ "any-relation-type": "Any type",
+ "add": "Add relation",
+ "edit": "Edit relation",
+ "delete-to-relation-title": "Are you sure you want to delete relation to the entity '{{entityName}}'?",
+ "delete-to-relation-text": "Be careful, after the confirmation the entity '{{entityName}}' will be unrelated from the current entity.",
+ "delete-to-relations-title": "Are you sure you want to delete { count, select, 1 {1 relation} other {# relations} }?",
+ "delete-to-relations-text": "Be careful, after the confirmation all selected relations will be removed and corresponding entities will be unrelated from the current entity.",
+ "delete-from-relation-title": "Are you sure you want to delete relation from the entity '{{entityName}}'?",
+ "delete-from-relation-text": "Be careful, after the confirmation current entity will be unrelated from the entity '{{entityName}}'.",
+ "delete-from-relations-title": "Are you sure you want to delete { count, select, 1 {1 relation} other {# relations} }?",
+ "delete-from-relations-text": "Be careful, after the confirmation all selected relations will be removed and current entity will be unrelated from the corresponding entities.",
+ "remove-relation-filter": "Remove relation filter",
+ "add-relation-filter": "Add relation filter",
+ "any-relation": "Any relation",
+ "relation-filters": "Relation filters",
+ "additional-info": "Additional info (JSON)",
+ "invalid-additional-info": "Unable to parse additional info json."
+ },
+ "rulechain": { // TODO
+ "rulechain": "Rule chain",
+ "rulechains": "Rule chains",
+ "root": "Root",
+ "delete": "Delete rule chain",
+ "name": "Name",
+ "name-required": "Name is required.",
+ "description": "Description",
+ "add": "Add Rule Chain",
+ "set-root": "Make rule chain root",
+ "set-root-rulechain-title": "Are you sure you want to make the rule chain '{{ruleChainName}}' root?",
+ "set-root-rulechain-text": "After the confirmation the rule chain will become root and will handle all incoming transport messages.",
+ "delete-rulechain-title": "Are you sure you want to delete the rule chain '{{ruleChainName}}'?",
+ "delete-rulechain-text": "Be careful, after the confirmation the rule chain and all related data will become unrecoverable.",
+ "delete-rulechains-title": "Are you sure you want to delete { count, select, 1 {1 rule chain} other {# rule chains} }?",
+ "delete-rulechains-action-title": "Delete { count, select, 1 {1 rule chain} other {# rule chains} }",
+ "delete-rulechains-text": "Be careful, after the confirmation all selected rule chains will be removed and all related data will become unrecoverable.",
+ "add-rulechain-text": "Add new rule chain",
+ "no-rulechains-text": "No rule chains found",
+ "rulechain-details": "Rule chain details",
+ "details": "Details",
+ "events": "Events",
+ "system": "System",
+ "import": "Import rule chain",
+ "export": "Export rule chain",
+ "export-failed-error": "Unable to export rule chain: {{error}}",
+ "create-new-rulechain": "Create new rule chain",
+ "rulechain-file": "Rule chain file",
+ "invalid-rulechain-file-error": "Unable to import rule chain: Invalid rule chain data structure.",
+ "copyId": "Copy rule chain Id",
+ "idCopiedMessage": "Rule chain Id has been copied to clipboard",
+ "select-rulechain": "Select rule chain",
+ "no-rulechains-matching": "No rule chains matching '{{entity}}' were found.",
+ "rulechain-required": "Rule chain is required",
+ "management": "Rules management",
+ "debug-mode": "Debug mode"
+ },
+ "rulenode": { // TODO
+ "details": "Details",
+ "events": "Events",
+ "search": "Search nodes",
+ "open-node-library": "Open node library",
+ "add": "Add rule node",
+ "name": "Name",
+ "name-required": "Name is required.",
+ "type": "Type",
+ "description": "Description",
+ "delete": "Delete rule node",
+ "select-all-objects": "Select all nodes and connections",
+ "deselect-all-objects": "Deselect all nodes and connections",
+ "delete-selected-objects": "Delete selected nodes and connections",
+ "delete-selected": "Delete selected",
+ "select-all": "Select all",
+ "copy-selected": "Copy selected",
+ "deselect-all": "Deselect all",
+ "rulenode-details": "Rule node details",
+ "debug-mode": "Debug mode",
+ "configuration": "Configuration",
+ "link": "Link",
+ "link-details": "Rule node link details",
+ "add-link": "Add link",
+ "link-label": "Link label",
+ "link-label-required": "Link label is required.",
+ "custom-link-label": "Custom link label",
+ "custom-link-label-required": "Custom link label is required.",
+ "type-filter": "Filter",
+ "type-filter-details": "Filter incoming messages with configured conditions",
+ "type-enrichment": "Enrichment",
+ "type-enrichment-details": "Add additional information into Message Metadata",
+ "type-transformation": "Transformation",
+ "type-transformation-details": "Change Message payload and Metadata",
+ "type-action": "Action",
+ "type-action-details": "Perform special action",
+ "type-external": "External",
+ "type-external-details": "Interacts with external system",
+ "type-rule-chain": "Rule Chain",
+ "type-rule-chain-details": "Forwards incoming messages to specified Rule Chain",
+ "type-input": "Input",
+ "type-input-details": "Logical input of Rule Chain, forwards incoming messages to next related Rule Node",
+ "directive-is-not-loaded": "Defined configuration directive '{{directiveName}}' is not available.",
+ "ui-resources-load-error": "Failed to load configuration ui resources.",
+ "invalid-target-rulechain": "Unable to resolve target rule chain!",
+ "test-script-function": "Test script function",
+ "message": "Message",
+ "message-type": "Message type",
+ "message-type-required": "Message type is required",
+ "metadata": "Metadata",
+ "metadata-required": "Metadata entries can't be empty.",
+ "output": "Output",
+ "test": "Test",
+ "help": "Help"
},
"tenant": {
- "tenants": "Tenants",
- "management": "Gestión de Tenant",
- "add": "Agregar Tenant",
- "admins": "Admins",
- "manage-tenant-admins": "Gestionar administradores tenant",
- "delete": "Eliminar tenant",
- "add-tenant-text": "Agregar nuevo tenant",
- "no-tenants-text": "Ningún tenant encontrado",
- "tenant-details": "Detalles del Tenant",
- "delete-tenant-title": "¿Estás seguro que quieres eliminar el tenant '{{tenantTitle}}'?",
- "delete-tenant-text": "Ten cuidado, luego de confirmar el tenant será eliminado y la información relacionada será irrecuperable.",
- "delete-tenants-title": "¿Estás seguro que quieres eliminar { count, select, 1 {1 tenant} other {# tenants} }?",
- "delete-tenants-action-title": "Eliminar { count, select, 1 {1 tenant} other {# tenants} }",
- "delete-tenants-text": "Ten cuidado, luego de confirmar los tenants seleccionados serán eliminados y la información relacionada será irrecuperable.",
- "title": "Título",
- "title-required": "Título requerido.",
- "description": "Descripción"
+ "tenants": "Tenants",
+ "management": "Gestión de Tenant",
+ "add": "Agregar Tenant",
+ "admins": "Admins",
+ "manage-tenant-admins": "Gestionar administradores tenant",
+ "delete": "Eliminar tenant",
+ "add-tenant-text": "Agregar nuevo tenant",
+ "no-tenants-text": "Ningún tenant encontrado",
+ "tenant-details": "Detalles del Tenant",
+ "delete-tenant-title": "¿Estás seguro que quieres eliminar el tenant '{{tenantTitle}}'?",
+ "delete-tenant-text": "Ten cuidado, luego de confirmar el tenant será eliminado y la información relacionada será irrecuperable.",
+ "delete-tenants-title": "¿Estás seguro que quieres eliminar { count, select, 1 {1 tenant} other {# tenants} }?",
+ "delete-tenants-action-title": "Eliminar { count, select, 1 {1 tenant} other {# tenants} }",
+ "delete-tenants-text": "Ten cuidado, luego de confirmar los tenants seleccionados serán eliminados y la información relacionada será irrecuperable.",
+ "title": "Título",
+ "title-required": "Título requerido.",
+ "description": "Descripción"
},
"timeinterval": {
- "seconds-interval": "{ seconds, select, 1 {1 segundo} other {# segundos} }",
- "minutes-interval": "{ minutes, select, 1 {1 minuto} other {# minutos} }",
- "hours-interval": "{ hours, select, 1 {1 hora} other {# horas} }",
- "days-interval": "{ days, select, 1 {1 día} other {# días} }",
- "days": "Días",
- "hours": "Horas",
- "minutes": "Minutos",
- "seconds": "Segundos",
- "advanced": "Avanzado"
+ "seconds-interval": "{ seconds, select, 1 {1 segundo} other {# segundos} }",
+ "minutes-interval": "{ minutes, select, 1 {1 minuto} other {# minutos} }",
+ "hours-interval": "{ hours, select, 1 {1 hora} other {# horas} }",
+ "days-interval": "{ days, select, 1 {1 día} other {# días} }",
+ "days": "Días",
+ "hours": "Horas",
+ "minutes": "Minutos",
+ "seconds": "Segundos",
+ "advanced": "Avanzado"
},
"timewindow": {
- "days": "{ days, select, 1 { día } other {# días } }",
- "hours": "{ hours, select, 0 { horas } 1 {1 hora } other {# horas } }",
- "minutes": "{ minutes, select, 0 { minutos } 1 {1 minuto } other {# minutos } }",
- "seconds": "{ seconds, select, 0 { segundos } 1 {1 segundo } other {# segundos } }",
- "realtime": "Tiempo-real",
- "history": "Histórico",
- "last-prefix": "último",
- "period": "desde {{ startTime }} hasta {{ endTime }}",
- "edit": "Editar ventana de tiempo",
- "date-range": "Rango de fechas",
- "last": "Últimos",
- "time-period": "Período de tiempo"
+ "days": "{ days, select, 1 { día } other {# días } }",
+ "hours": "{ hours, select, 0 { horas } 1 {1 hora } other {# horas } }",
+ "minutes": "{ minutes, select, 0 { minutos } 1 {1 minuto } other {# minutos } }",
+ "seconds": "{ seconds, select, 0 { segundos } 1 {1 segundo } other {# segundos } }",
+ "realtime": "Tiempo-real",
+ "history": "Histórico",
+ "last-prefix": "último",
+ "period": "desde {{ startTime }} hasta {{ endTime }}",
+ "edit": "Editar ventana de tiempo",
+ "date-range": "Rango de fechas",
+ "last": "Últimos",
+ "time-period": "Período de tiempo"
},
"user": {
- "users": "Usuarios",
- "customer-users": "Usuarios del Cliente",
- "tenant-admins": "Tenant Admins",
- "sys-admin": "Administrador del Sistema",
- "tenant-admin": "Administrador Tenant",
- "customer": "Cliente",
- "anonymous": "Anónimo",
- "add": "Agregar usuario",
- "delete": "Eliminar usuario",
- "add-user-text": "Agregar nuevo usuario",
- "no-users-text": "Ningún usuario encontrado",
- "user-details": "Detalles del usuario",
- "delete-user-title": "¿Estás seguro que quieres eliminar el usuario '{{userEmail}}'?",
- "delete-user-text": "Ten cuidado, luego de confirmar el usuario seleccionado será eliminado y la información relacionada será irrecuperable.",
- "delete-users-title": "¿Estás seguro que quieres eliminar { count, select, 1 {1 usuario} other {# usuarios} }?",
- "delete-users-action-title": "Borrar { count, select, 1 {1 usuario} other {# usuarios} }",
- "delete-users-text": "Ten cuidado, luego de confirmar los usuarios seleccionados serán eliminados y la información relacionada será irrecuperable.",
- "activation-email-sent-message": "Mail de activación enviado con éxito!",
- "resend-activation": "Reenviar activación",
- "email": "Email",
- "email-required": "Email requerido.",
- "first-name": "Nombre",
- "last-name": "Apellido",
- "description": "Descripción",
- "default-dashboard": "Panel por defecto",
- "always-fullscreen": "Siempre en pantalla completa"
+ "users": "Usuarios",
+ "customer-users": "Usuarios del Cliente",
+ "tenant-admins": "Tenant Admins",
+ "sys-admin": "Administrador del Sistema",
+ "tenant-admin": "Administrador Tenant",
+ "customer": "Cliente",
+ "anonymous": "Anónimo",
+ "add": "Agregar usuario",
+ "delete": "Eliminar usuario",
+ "add-user-text": "Agregar nuevo usuario",
+ "no-users-text": "Ningún usuario encontrado",
+ "user-details": "Detalles del usuario",
+ "delete-user-title": "¿Estás seguro que quieres eliminar el usuario '{{userEmail}}'?",
+ "delete-user-text": "Ten cuidado, luego de confirmar el usuario seleccionado será eliminado y la información relacionada será irrecuperable.",
+ "delete-users-title": "¿Estás seguro que quieres eliminar { count, select, 1 {1 usuario} other {# usuarios} }?",
+ "delete-users-action-title": "Borrar { count, select, 1 {1 usuario} other {# usuarios} }",
+ "delete-users-text": "Ten cuidado, luego de confirmar los usuarios seleccionados serán eliminados y la información relacionada será irrecuperable.",
+ "activation-email-sent-message": "Mail de activación enviado con éxito!",
+ "resend-activation": "Reenviar activación",
+ "email": "Email",
+ "email-required": "Email requerido.",
+ "first-name": "Nombre",
+ "last-name": "Apellido",
+ "description": "Descripción",
+ "default-dashboard": "Panel por defecto",
+ "always-fullscreen": "Siempre en pantalla completa"
},
"value": {
- "type": "Tipo de valor",
- "string": "Cadena de texto",
- "string-value": "Valor de cadena de texto",
- "integer": "Nro entero",
- "integer-value": "Valor de nro entero",
- "invalid-integer-value": "Valor inválido",
- "double": "Nro decimal",
- "double-value": "Valor nro decimal",
- "boolean": "Booleano",
- "boolean-value": "Valor booleano",
- "false": "Falso",
- "true": "Verdadero"
+ "type": "Tipo de valor",
+ "string": "Cadena de texto",
+ "string-value": "Valor de cadena de texto",
+ "integer": "Nro entero",
+ "integer-value": "Valor de nro entero",
+ "invalid-integer-value": "Valor inválido",
+ "double": "Nro decimal",
+ "double-value": "Valor nro decimal",
+ "boolean": "Booleano",
+ "boolean-value": "Valor booleano",
+ "false": "Falso",
+ "true": "Verdadero"
},
"widget": {
- "widget-library": "Bibloteca de Widgets",
- "widget-bundle": "Paquetes de Widgets",
- "select-widgets-bundle": "Seleccionar paquete de widgets",
- "management": "Gestión de Widgets",
- "editor": "Editor de widgets",
- "widget-type-not-found": "Problema al cargar la configuración del widget.<br>Probablemente asociado\n El tipo de widget fue eliminado.",
- "widget-type-load-error": "Widget no pudo ser cargado debido a estos errores:",
- "remove": "Eliminar widget",
- "edit": "Editar widget",
- "remove-widget-title": "¿Estás seguro que quieres eliminar el widget '{{widgetTitle}}'?",
- "remove-widget-text": "Luego de confirmar el widget será eliminado y toda la información relacionada será irrecuperable..",
- "timeseries": "Series de tiempo",
- "latest-values": "Últimos valores",
- "rpc": "Widget de control",
- "static": "Widget estático",
- "select-widget-type": "Seleccionar tipo de widget",
- "missing-widget-title-error": "El titulo del widget debe ser especificado!",
- "widget-saved": "Widget guardado",
- "unable-to-save-widget-error": "Imposible guardar widget! Tiene errores!",
- "save": "Guardar widget",
- "saveAs": "Guardar widget como",
- "save-widget-type-as": "Guardar tipo de widget como",
- "save-widget-type-as-text": "Por favor, ingrese un nuevo titulo y/o seleccione un paquete de destino.",
- "toggle-fullscreen": "Cambiar a pantalla completa",
- "run": "Correr widget",
- "title": "Titulo",
- "title-required": "Titulo requerido.",
- "type": "Tipo",
- "resources": "Recursos",
- "resource-url": "JavaScript/CSS URL",
- "remove-resource": "Eliminar recurso",
- "add-resource": "Agregar recurso",
- "html": "HTML",
- "tidy": "Tidy",
- "css": "CSS",
- "settings-schema": "Esquema de configuración",
- "datakey-settings-schema": "Esquema de configuración de clave de datos",
- "javascript": "Javascript",
- "remove-widget-type-title": "¿Estás seguro que quieres eliminar el tipo del widget '{{widgetName}}'?",
- "remove-widget-type-text": "Luego de confirmar el tipo será eliminado y la información relacionada será irrecuperable.",
- "remove-widget-type": "Eliminar tipo de widget.",
- "add-widget-type": "Agregar nuevo tipo de widget",
- "widget-type-load-failed-error": "Error al cargar el tipo de widget!",
- "widget-template-load-failed-error": "Error al cargar el template del widget!",
- "add": "Agregar Widget",
- "undo": "Deshacer cambios",
- "export": "Exportar widget"
+ "widget-library": "Bibloteca de Widgets",
+ "widget-bundle": "Paquetes de Widgets",
+ "select-widgets-bundle": "Seleccionar paquete de widgets",
+ "management": "Gestión de Widgets",
+ "editor": "Editor de widgets",
+ "widget-type-not-found": "Problema al cargar la configuración del widget.<br>Probablemente asociado\n El tipo de widget fue eliminado.",
+ "widget-type-load-error": "Widget no pudo ser cargado debido a estos errores:",
+ "remove": "Eliminar widget",
+ "edit": "Editar widget",
+ "remove-widget-title": "¿Estás seguro que quieres eliminar el widget '{{widgetTitle}}'?",
+ "remove-widget-text": "Luego de confirmar el widget será eliminado y toda la información relacionada será irrecuperable..",
+ "timeseries": "Series de tiempo",
+ "latest-values": "Últimos valores",
+ "rpc": "Widget de control",
+ "static": "Widget estático",
+ "select-widget-type": "Seleccionar tipo de widget",
+ "missing-widget-title-error": "El titulo del widget debe ser especificado!",
+ "widget-saved": "Widget guardado",
+ "unable-to-save-widget-error": "Imposible guardar widget! Tiene errores!",
+ "save": "Guardar widget",
+ "saveAs": "Guardar widget como",
+ "save-widget-type-as": "Guardar tipo de widget como",
+ "save-widget-type-as-text": "Por favor, ingrese un nuevo titulo y/o seleccione un paquete de destino.",
+ "toggle-fullscreen": "Cambiar a pantalla completa",
+ "run": "Correr widget",
+ "title": "Titulo",
+ "title-required": "Titulo requerido.",
+ "type": "Tipo",
+ "resources": "Recursos",
+ "resource-url": "JavaScript/CSS URL",
+ "remove-resource": "Eliminar recurso",
+ "add-resource": "Agregar recurso",
+ "html": "HTML",
+ "tidy": "Tidy",
+ "css": "CSS",
+ "settings-schema": "Esquema de configuración",
+ "datakey-settings-schema": "Esquema de configuración de clave de datos",
+ "javascript": "Javascript",
+ "remove-widget-type-title": "¿Estás seguro que quieres eliminar el tipo del widget '{{widgetName}}'?",
+ "remove-widget-type-text": "Luego de confirmar el tipo será eliminado y la información relacionada será irrecuperable.",
+ "remove-widget-type": "Eliminar tipo de widget.",
+ "add-widget-type": "Agregar nuevo tipo de widget",
+ "widget-type-load-failed-error": "Error al cargar el tipo de widget!",
+ "widget-template-load-failed-error": "Error al cargar el template del widget!",
+ "add": "Agregar Widget",
+ "undo": "Deshacer cambios",
+ "export": "Exportar widget"
+ },
+ "widget-action": { // TODO
+ "header-button": "Widget header button",
+ "open-dashboard-state": "Navigate to new dashboard state",
+ "update-dashboard-state": "Update current dashboard state",
+ "open-dashboard": "Navigate to other dashboard",
+ "custom": "Custom action",
+ "target-dashboard-state": "Target dashboard state",
+ "target-dashboard-state-required": "Target dashboard state is required",
+ "set-entity-from-widget": "Set entity from widget",
+ "target-dashboard": "Target dashboard",
+ "open-right-layout": "Open right dashboard layout (mobile view)"
},
"widgets-bundle": {
- "current": "Paquete actual",
- "widgets-bundles": "Paquete de Widgets",
- "add": "Agregar paquete de widgets",
- "delete": "Eliminar paquete de widgets",
- "title": "Título",
- "title-required": "Título requerido.",
- "add-widgets-bundle-text": "Agregar nuevo paquete de widgets",
- "no-widgets-bundles-text": "Ningún paquete de widgets encontrado",
- "empty": "Paquete de widgets vacío.",
- "details": "Detalles",
- "widgets-bundle-details": "Detalles del paquete de Widgets",
- "delete-widgets-bundle-title": "¿Estás seguro que desea eliminar el paquete de widgets '{{widgetsBundleTitle}}'?",
- "delete-widgets-bundle-text": "Ten cuidado, luego de confirmar todos los paquetes seleccionados serán eliminados y su información relacionada será irrecuperable.",
- "delete-widgets-bundles-title": "¿Estás seguro que deseas eliminar { count, select, 1 {1 paquete de widgets} other {# paquetes de widgets} }?",
- "delete-widgets-bundles-action-title": "Eliminar { count, select, 1 {1 paquete de widgets} other {# paquetes de widgets} }",
- "delete-widgets-bundles-text": "Ten cuidado, luego de confirmar todos los paquetes seleccionados serán eliminados y la información relacionada será irrecuperable.",
- "no-widgets-bundles-matching": "Ningún paquete '{{widgetsBundle}}' encontrado.",
- "widgets-bundle-required": "Paquete de widget requerido.",
- "system": "Sistema",
- "import": "Importar paquete de widgets",
- "export": "Exportar paquete de widgets",
- "export-failed-error": "Imposible exportar paquete de widgets: {{error}}",
- "create-new-widgets-bundle": "Crear nuevo paquete de widgets",
- "widgets-bundle-file": "Archivo de paquete de widgets",
- "invalid-widgets-bundle-file-error": "Imposible importar paquete de widgets: Estructura de datos inválida."
+ "current": "Paquete actual",
+ "widgets-bundles": "Paquete de Widgets",
+ "add": "Agregar paquete de widgets",
+ "delete": "Eliminar paquete de widgets",
+ "title": "Título",
+ "title-required": "Título requerido.",
+ "add-widgets-bundle-text": "Agregar nuevo paquete de widgets",
+ "no-widgets-bundles-text": "Ningún paquete de widgets encontrado",
+ "empty": "Paquete de widgets vacío.",
+ "details": "Detalles",
+ "widgets-bundle-details": "Detalles del paquete de Widgets",
+ "delete-widgets-bundle-title": "¿Estás seguro que desea eliminar el paquete de widgets '{{widgetsBundleTitle}}'?",
+ "delete-widgets-bundle-text": "Ten cuidado, luego de confirmar todos los paquetes seleccionados serán eliminados y su información relacionada será irrecuperable.",
+ "delete-widgets-bundles-title": "¿Estás seguro que deseas eliminar { count, select, 1 {1 paquete de widgets} other {# paquetes de widgets} }?",
+ "delete-widgets-bundles-action-title": "Eliminar { count, select, 1 {1 paquete de widgets} other {# paquetes de widgets} }",
+ "delete-widgets-bundles-text": "Ten cuidado, luego de confirmar todos los paquetes seleccionados serán eliminados y la información relacionada será irrecuperable.",
+ "no-widgets-bundles-matching": "Ningún paquete '{{widgetsBundle}}' encontrado.",
+ "widgets-bundle-required": "Paquete de widget requerido.",
+ "system": "Sistema",
+ "import": "Importar paquete de widgets",
+ "export": "Exportar paquete de widgets",
+ "export-failed-error": "Imposible exportar paquete de widgets: {{error}}",
+ "create-new-widgets-bundle": "Crear nuevo paquete de widgets",
+ "widgets-bundle-file": "Archivo de paquete de widgets",
+ "invalid-widgets-bundle-file-error": "Imposible importar paquete de widgets: Estructura de datos inválida."
},
"widget-config": {
- "data": "Datos",
- "settings": "Ajustes",
- "advanced": "Avanzado",
- "title": "Titulo",
- "general-settings": "Ajustes generales",
- "display-title": "Mostrar titulo",
- "drop-shadow": "Sombra",
- "enable-fullscreen": "Habilitar pantalla completa",
- "background-color": "Color de fondo",
- "text-color": "Color del texto",
- "padding": "Relleno",
- "title-style": "Estilo de título",
- "mobile-mode-settings": "Ajustes mobile.",
- "order": "Orden",
- "height": "Altura",
- "units": "Caracter especial a mostrar en el siguiente valor",
- "decimals": "Números de dígitos después de la coma",
- "timewindow": "Ventana de tiempo",
- "use-dashboard-timewindow": "Usar ventana de tiempo del Panel",
- "display-legend": "Mostrar leyenda",
- "datasources": "Set de datos",
- "datasource-type": "Tipo",
- "datasource-parameters": "Parámetros",
- "remove-datasource": "Eliminar set de datos",
- "add-datasource": "Agregar set de datos",
- "target-device": "Dispositivo destino"
+ "data": "Datos",
+ "settings": "Ajustes",
+ "advanced": "Avanzado",
+ "title": "Titulo",
+ "general-settings": "Ajustes generales",
+ "display-title": "Mostrar titulo",
+ "drop-shadow": "Sombra",
+ "enable-fullscreen": "Habilitar pantalla completa",
+ "background-color": "Color de fondo",
+ "text-color": "Color del texto",
+ "padding": "Relleno",
+ "title-style": "Estilo de título",
+ "mobile-mode-settings": "Ajustes mobile.",
+ "order": "Orden",
+ "height": "Altura",
+ "units": "Caracter especial a mostrar en el siguiente valor",
+ "decimals": "Números de dígitos después de la coma",
+ "timewindow": "Ventana de tiempo",
+ "use-dashboard-timewindow": "Usar ventana de tiempo del Panel",
+ "display-legend": "Mostrar leyenda",
+ "datasources": "Set de datos",
+ "datasource-type": "Tipo",
+ "datasource-parameters": "Parámetros",
+ "remove-datasource": "Eliminar set de datos",
+ "add-datasource": "Agregar set de datos",
+ "target-device": "Dispositivo destino"
},
"widget-type": {
- "import": "Importar tipo de widget",
- "export": "Exportar tipo de widget",
- "export-failed-error": "Imposible exportar tipo de widget: {{error}}",
- "create-new-widget-type": "Crear nuevo tipo de widget",
- "widget-type-file": "Tipo de archivo del widget",
- "invalid-widget-type-file-error": "Imposible de importar tipo de widget: Estructura de datos inválida."
+ "import": "Importar tipo de widget",
+ "export": "Exportar tipo de widget",
+ "export-failed-error": "Imposible exportar tipo de widget: {{error}}",
+ "create-new-widget-type": "Crear nuevo tipo de widget",
+ "widget-type-file": "Tipo de archivo del widget",
+ "invalid-widget-type-file-error": "Imposible de importar tipo de widget: Estructura de datos inválida."
+ },
+ "icon": { // TODO
+ "icon": "Icon",
+ "select-icon": "Select icon",
+ "material-icons": "Material icons",
+ "show-all": "Show all icons"
+ },
+ "custom": { // TODO
+ "widget-action": {
+ "action-cell-button": "Action cell button",
+ "row-click": "On row click",
+ "marker-click": "On marker click",
+ "tooltip-tag-action": "Tooltip tag action"
+ }
},
"language": {
- "language": "Lenguaje",
- "en_US": "Inglés",
- "ko_KR": "Coreano",
- "zh_CN": "Chino",
- "ru_RU": "Ruso",
- "es_ES": "Español"
+ "language": "Lenguaje",
+ "en_US": "Inglés",
+ "ko_KR": "Coreano",
+ "zh_CN": "Chino",
+ "ru_RU": "Ruso",
+ "es_ES": "Español"
}
- };
- angular.extend(locales, {'es_ES': es_ES});
+ };
+ angular.extend(locales, { 'es_ES': es_ES });
}
\ No newline at end of file
ui/src/app/locale/locale.constant-ko.js 773(+672 -101)
diff --git a/ui/src/app/locale/locale.constant-ko.js b/ui/src/app/locale/locale.constant-ko.js
index 9273037..9112157 100644
--- a/ui/src/app/locale/locale.constant-ko.js
+++ b/ui/src/app/locale/locale.constant-ko.js
@@ -38,8 +38,11 @@ export default function addLocaleKorean(locales) {
"update": "업데이트",
"remove": "제거",
"search": "검색",
+ "clear-search": "Clear search", // TODO
"assign": "할당",
"unassign": "비할당",
+ "share": "Share", // TODO
+ "make-private": "Make private", // TODO
"apply": "적용",
"apply-changes": "변경사항 적용",
"edit-mode": "수정 모드",
@@ -57,8 +60,11 @@ export default function addLocaleKorean(locales) {
"undo": "취소",
"copy": "복사",
"paste": "붙여넣기",
+ "copy-reference": "Copy reference", // TODO
+ "paste-reference": "Paste reference", // TODO
"import": "가져오기",
- "export": "내보내기"
+ "export": "내보내기",
+ "share-via": "Share via {{provider}}" // TODO
},
"aggregation": {
"aggregation": "집합",
@@ -95,6 +101,160 @@ export default function addLocaleKorean(locales) {
"enable-tls": "TLS 사용",
"send-test-mail": "테스트 메일 보내기"
},
+
+ "alarm": { // TODO
+ "alarm": "Alarm",
+ "alarms": "Alarms",
+ "select-alarm": "Select alarm",
+ "no-alarms-matching": "No alarms matching '{{entity}}' were found.",
+ "alarm-required": "Alarm is required",
+ "alarm-status": "Alarm status",
+ "search-status": {
+ "ANY": "Any",
+ "ACTIVE": "Active",
+ "CLEARED": "Cleared",
+ "ACK": "Acknowledged",
+ "UNACK": "Unacknowledged"
+ },
+ "display-status": {
+ "ACTIVE_UNACK": "Active Unacknowledged",
+ "ACTIVE_ACK": "Active Acknowledged",
+ "CLEARED_UNACK": "Cleared Unacknowledged",
+ "CLEARED_ACK": "Cleared Acknowledged"
+ },
+ "no-alarms-prompt": "No alarms found",
+ "created-time": "Created time",
+ "type": "Type",
+ "severity": "Severity",
+ "originator": "Originator",
+ "originator-type": "Originator type",
+ "details": "Details",
+ "status": "Status",
+ "alarm-details": "Alarm details",
+ "start-time": "Start time",
+ "end-time": "End time",
+ "ack-time": "Acknowledged time",
+ "clear-time": "Cleared time",
+ "severity-critical": "Critical",
+ "severity-major": "Major",
+ "severity-minor": "Minor",
+ "severity-warning": "Warning",
+ "severity-indeterminate": "Indeterminate",
+ "acknowledge": "Acknowledge",
+ "clear": "Clear",
+ "search": "Search alarms",
+ "selected-alarms": "{ count, select, 1 {1 alarm} other {# alarms} } selected",
+ "no-data": "No data to display",
+ "polling-interval": "Alarms polling interval (sec)",
+ "polling-interval-required": "Alarms polling interval is required.",
+ "min-polling-interval-message": "At least 1 sec polling interval is allowed.",
+ "aknowledge-alarms-title": "Acknowledge { count, select, 1 {1 alarm} other {# alarms} }",
+ "aknowledge-alarms-text": "Are you sure you want to acknowledge { count, select, 1 {1 alarm} other {# alarms} }?",
+ "clear-alarms-title": "Clear { count, select, 1 {1 alarm} other {# alarms} }",
+ "clear-alarms-text": "Are you sure you want to clear { count, select, 1 {1 alarm} other {# alarms} }?"
+ },
+ "alias": { // TODO
+ "add": "Add alias",
+ "edit": "Edit alias",
+ "name": "Alias name",
+ "name-required": "Alias name is required",
+ "duplicate-alias": "Alias with same name is already exists.",
+ "filter-type-single-entity": "Single entity",
+ "filter-type-entity-list": "Entity list",
+ "filter-type-entity-name": "Entity name",
+ "filter-type-state-entity": "Entity from dashboard state",
+ "filter-type-state-entity-description": "Entity taken from dashboard state parameters",
+ "filter-type-asset-type": "Asset type",
+ "filter-type-asset-type-description": "Assets of type '{{assetType}}'",
+ "filter-type-asset-type-and-name-description": "Assets of type '{{assetType}}' and with name starting with '{{prefix}}'",
+ "filter-type-device-type": "Device type",
+ "filter-type-device-type-description": "Devices of type '{{deviceType}}'",
+ "filter-type-device-type-and-name-description": "Devices of type '{{deviceType}}' and with name starting with '{{prefix}}'",
+ "filter-type-relations-query": "Relations query",
+ "filter-type-relations-query-description": "{{entities}} that have {{relationType}} relation {{direction}} {{rootEntity}}",
+ "filter-type-asset-search-query": "Asset search query",
+ "filter-type-asset-search-query-description": "Assets with types {{assetTypes}} that have {{relationType}} relation {{direction}} {{rootEntity}}",
+ "filter-type-device-search-query": "Device search query",
+ "filter-type-device-search-query-description": "Devices with types {{deviceTypes}} that have {{relationType}} relation {{direction}} {{rootEntity}}",
+ "entity-filter": "Entity filter",
+ "resolve-multiple": "Resolve as multiple entities",
+ "filter-type": "Filter type",
+ "filter-type-required": "Filter type is required.",
+ "entity-filter-no-entity-matched": "No entities matching specified filter were found.",
+ "no-entity-filter-specified": "No entity filter specified",
+ "root-state-entity": "Use dashboard state entity as root",
+ "root-entity": "Root entity",
+ "state-entity-parameter-name": "State entity parameter name",
+ "default-state-entity": "Default state entity",
+ "default-entity-parameter-name": "By default",
+ "max-relation-level": "Max relation level",
+ "unlimited-level": "Unlimited level",
+ "state-entity": "Dashboard state entity",
+ "all-entities": "All entities",
+ "any-relation": "any"
+ },
+ "asset": { // TODO
+ "asset": "Asset",
+ "assets": "Assets",
+ "management": "Asset management",
+ "view-assets": "View Assets",
+ "add": "Add Asset",
+ "assign-to-customer": "Assign to customer",
+ "assign-asset-to-customer": "Assign Asset(s) To Customer",
+ "assign-asset-to-customer-text": "Please select the assets to assign to the customer",
+ "no-assets-text": "No assets found",
+ "assign-to-customer-text": "Please select the customer to assign the asset(s)",
+ "public": "Public",
+ "assignedToCustomer": "Assigned to customer",
+ "make-public": "Make asset public",
+ "make-private": "Make asset private",
+ "unassign-from-customer": "Unassign from customer",
+ "delete": "Delete asset",
+ "asset-public": "Asset is public",
+ "asset-type": "Asset type",
+ "asset-type-required": "Asset type is required.",
+ "select-asset-type": "Select asset type",
+ "enter-asset-type": "Enter asset type",
+ "any-asset": "Any asset",
+ "no-asset-types-matching": "No asset types matching '{{entitySubtype}}' were found.",
+ "asset-type-list-empty": "No asset types selected.",
+ "asset-types": "Asset types",
+ "name": "Name",
+ "name-required": "Name is required.",
+ "description": "Description",
+ "type": "Type",
+ "type-required": "Type is required.",
+ "details": "Details",
+ "events": "Events",
+ "add-asset-text": "Add new asset",
+ "asset-details": "Asset details",
+ "assign-assets": "Assign assets",
+ "assign-assets-text": "Assign { count, select, 1 {1 asset} other {# assets} } to customer",
+ "delete-assets": "Delete assets",
+ "unassign-assets": "Unassign assets",
+ "unassign-assets-action-title": "Unassign { count, select, 1 {1 asset} other {# assets} } from customer",
+ "assign-new-asset": "Assign new asset",
+ "delete-asset-title": "Are you sure you want to delete the asset '{{assetName}}'?",
+ "delete-asset-text": "Be careful, after the confirmation the asset and all related data will become unrecoverable.",
+ "delete-assets-title": "Are you sure you want to delete { count, select, 1 {1 asset} other {# assets} }?",
+ "delete-assets-action-title": "Delete { count, select, 1 {1 asset} other {# assets} }",
+ "delete-assets-text": "Be careful, after the confirmation all selected assets will be removed and all related data will become unrecoverable.",
+ "make-public-asset-title": "Are you sure you want to make the asset '{{assetName}}' public?",
+ "make-public-asset-text": "After the confirmation the asset and all its data will be made public and accessible by others.",
+ "make-private-asset-title": "Are you sure you want to make the asset '{{assetName}}' private?",
+ "make-private-asset-text": "After the confirmation the asset and all its data will be made private and won't be accessible by others.",
+ "unassign-asset-title": "Are you sure you want to unassign the asset '{{assetName}}'?",
+ "unassign-asset-text": "After the confirmation the asset will be unassigned and won't be accessible by the customer.",
+ "unassign-asset": "Unassign asset",
+ "unassign-assets-title": "Are you sure you want to unassign { count, select, 1 {1 asset} other {# assets} }?",
+ "unassign-assets-text": "After the confirmation all selected assets will be unassigned and won't be accessible by the customer.",
+ "copyId": "Copy asset Id",
+ "idCopiedMessage": "Asset Id has been copied to clipboard",
+ "select-asset": "Select asset",
+ "no-assets-matching": "No assets matching '{{entity}}' were found.",
+ "asset-required": "Asset is required",
+ "name-starts-with": "Asset name starts with"
+ },
"attribute": {
"attributes": "속성",
"latest-telemetry": "최근 데이터",
@@ -104,9 +264,9 @@ export default function addLocaleKorean(locales) {
"scope-server": "서버 속성",
"scope-shared": "공유 속성",
"add": "속성 추가",
- "key": "Key",
+ "key": "Key", // TODO
"key-required": "속성 key를 입력하세요.",
- "value": "Value",
+ "value": "Value", // TODO
"value-required": "속성 value를 입력하세요.",
"delete-attributes-title": "{ count, select, 1 {속성} other {여러 속성들을} } 삭제하시겠습니까??",
"delete-attributes-text": "모든 선택된 속성들이 제거 될 것이므로 주의하십시오.",
@@ -121,6 +281,38 @@ export default function addLocaleKorean(locales) {
"selected-attributes": "{ count, select, 1 {속성 1개} other {속성 #개} } 선택됨",
"selected-telemetry": "{ count, select, 1 {최근 데이터 1개} other {최근 데이터 #개} } 선택됨"
},
+ "audit-log": { // TODO
+ "audit": "Audit",
+ "audit-logs": "Audit Logs",
+ "timestamp": "Timestamp",
+ "entity-type": "Entity Type",
+ "entity-name": "Entity Name",
+ "user": "User",
+ "type": "Type",
+ "status": "Status",
+ "details": "Details",
+ "type-added": "Added",
+ "type-deleted": "Deleted",
+ "type-updated": "Updated",
+ "type-attributes-updated": "Attributes updated",
+ "type-attributes-deleted": "Attributes deleted",
+ "type-rpc-call": "RPC call",
+ "type-credentials-updated": "Credentials updated",
+ "type-assigned-to-customer": "Assigned to Customer",
+ "type-unassigned-from-customer": "Unassigned from Customer",
+ "type-activated": "Activated",
+ "type-suspended": "Suspended",
+ "type-credentials-read": "Credentials read",
+ "type-attributes-read": "Attributes read",
+ "status-success": "Success",
+ "status-failure": "Failure",
+ "audit-log-details": "Audit log details",
+ "no-audit-logs-prompt": "No logs found",
+ "action-data": "Action data",
+ "failure-details": "Failure details",
+ "search": "Search audit logs",
+ "clear-search": "Clear search"
+ },
"confirm-on-exit": {
"message": "변경 사항을 저장하지 않았습니다. 이 페이지를 나가시겠습니까?",
"html-message": "변경 사항을 저장하지 않았습니다.<br/>이 페이지를 나가시겠습니까?",
@@ -145,6 +337,11 @@ export default function addLocaleKorean(locales) {
"enter-password": "비밀번호를 입력하세요.",
"enter-search": "검색어 입력"
},
+ "content-type": { // TODO
+ "json": "Json",
+ "text": "Text",
+ "binary": "Binary (Base64)"
+ },
"customer": {
"customers": "커스터머",
"management": "커스터머 관리",
@@ -156,6 +353,10 @@ export default function addLocaleKorean(locales) {
"manage-customer-users": "커스터머 사용자 관리",
"manage-customer-devices": "커스터머 디바이스 관리",
"manage-customer-dashboards": "커스터머 대시보드 관리",
+ "manage-public-devices": "Manage public devices", // TODO
+ "manage-public-dashboards": "Manage public dashboards", // TODO
+ "manage-customer-assets": "Manage customer assets", // TODO
+ "manage-public-assets": "Manage public assets", // TODO
"add-customer-text": "커스터머 추가",
"no-customers-text": "커스터머가 없습니다.",
"customer-details": "커스터머 상세정보",
@@ -169,7 +370,17 @@ export default function addLocaleKorean(locales) {
"manage-dashboards": "대시보드 관리",
"title": "타이틀",
"title-required": "타이틀을 입력하세요.",
- "description": "설명"
+ "description": "설명",
+ "details": "Details",
+ "events": "Events",
+ "copyId": "Copy customer Id",
+ "idCopiedMessage": "Customer Id has been copied to clipboard",
+ "select-customer": "Select customer",
+ "no-customers-matching": "No customers matching '{{entity}}' were found.",
+ "customer-required": "Customer is required",
+ "select-default-customer": "Select default customer",
+ "default-customer": "Default customer",
+ "default-customer-required": "Default customer is required in order to debug dashboard on Tenant level"
},
"datetime": {
"date-from": "시작 날짜",
@@ -277,11 +488,15 @@ export default function addLocaleKorean(locales) {
"attributes": "Attributes",
"timeseries-required": "디바이스 timeseries 를 입력하세요.",
"timeseries-or-attributes-required": "디바이스 timeseries/attributes 를 입력하세요.",
+ "maximum-timeseries-or-attributes": "Maximum { count, select, 1 {1 timeseries/attribute is allowed.} other {# timeseries/attributes are allowed} }", // TODO
+ "alarm-fields-required": "Alarm fields are required.", // TODO
"function-types": "함수 유형",
- "function-types-required": "함수 유형을 입력하세요."
+ "function-types-required": "함수 유형을 입력하세요.",
+ "maximum-function-types": "Maximum { count, select, 1 {1 function type is allowed.} other {# function types are allowed} }" // TODO
},
"datasource": {
"type": "데이터소스 유형",
+ "name": "Name", // TODO
"add-datasource-prompt": "데이터소스를 추가하세요."
},
"details": {
@@ -375,11 +590,101 @@ export default function addLocaleKorean(locales) {
"unhandled-error-code": "처리되지 않은 오류 코드: {{errorCode}}",
"unknown-error": "알 수 없는 오류"
},
+ "entity": { // TODO
+ "entity": "Entity",
+ "entities": "Entities",
+ "aliases": "Entity aliases",
+ "entity-alias": "Entity alias",
+ "unable-delete-entity-alias-title": "Unable to delete entity alias",
+ "unable-delete-entity-alias-text": "Entity alias '{{entityAlias}}' can't be deleted as it used by the following widget(s):<br/>{{widgetsList}}",
+ "duplicate-alias-error": "Duplicate alias found '{{alias}}'.<br>Entity aliases must be unique whithin the dashboard.",
+ "missing-entity-filter-error": "Filter is missing for alias '{{alias}}'.",
+ "configure-alias": "Configure '{{alias}}' alias",
+ "alias": "Alias",
+ "alias-required": "Entity alias is required.",
+ "remove-alias": "Remove entity alias",
+ "add-alias": "Add entity alias",
+ "entity-list": "Entity list",
+ "entity-type": "Entity type",
+ "entity-types": "Entity types",
+ "entity-type-list": "Entity type list",
+ "any-entity": "Any entity",
+ "enter-entity-type": "Enter entity type",
+ "no-entities-matching": "No entities matching '{{entity}}' were found.",
+ "no-entity-types-matching": "No entity types matching '{{entityType}}' were found.",
+ "name-starts-with": "Name starts with",
+ "use-entity-name-filter": "Use filter",
+ "entity-list-empty": "No entities selected.",
+ "entity-type-list-empty": "No entity types selected.",
+ "entity-name-filter-required": "Entity name filter is required.",
+ "entity-name-filter-no-entity-matched": "No entities starting with '{{entity}}' were found.",
+ "all-subtypes": "All",
+ "select-entities": "Select entities",
+ "no-aliases-found": "No aliases found.",
+ "no-alias-matching": "'{{alias}}' not found.",
+ "create-new-alias": "Create a new one!",
+ "key": "Key",
+ "key-name": "Key name",
+ "no-keys-found": "No keys found.",
+ "no-key-matching": "'{{key}}' not found.",
+ "create-new-key": "Create a new one!",
+ "type": "Type",
+ "type-required": "Entity type is required.",
+ "type-device": "Device",
+ "type-devices": "Devices",
+ "list-of-devices": "{ count, select, 1 {One device} other {List of # devices} }",
+ "device-name-starts-with": "Devices whose names start with '{{prefix}}'",
+ "type-asset": "Asset",
+ "type-assets": "Assets",
+ "list-of-assets": "{ count, select, 1 {One asset} other {List of # assets} }",
+ "asset-name-starts-with": "Assets whose names start with '{{prefix}}'",
+ "type-rule": "Rule",
+ "type-rules": "Rules",
+ "list-of-rules": "{ count, select, 1 {One rule} other {List of # rules} }",
+ "rule-name-starts-with": "Rules whose names start with '{{prefix}}'",
+ "type-plugin": "Plugin",
+ "type-plugins": "Plugins",
+ "list-of-plugins": "{ count, select, 1 {One plugin} other {List of # plugins} }",
+ "plugin-name-starts-with": "Plugins whose names start with '{{prefix}}'",
+ "type-tenant": "Tenant",
+ "type-tenants": "Tenants",
+ "list-of-tenants": "{ count, select, 1 {One tenant} other {List of # tenants} }",
+ "tenant-name-starts-with": "Tenants whose names start with '{{prefix}}'",
+ "type-customer": "Customer",
+ "type-customers": "Customers",
+ "list-of-customers": "{ count, select, 1 {One customer} other {List of # customers} }",
+ "customer-name-starts-with": "Customers whose names start with '{{prefix}}'",
+ "type-user": "User",
+ "type-users": "Users",
+ "list-of-users": "{ count, select, 1 {One user} other {List of # users} }",
+ "user-name-starts-with": "Users whose names start with '{{prefix}}'",
+ "type-dashboard": "Dashboard",
+ "type-dashboards": "Dashboards",
+ "list-of-dashboards": "{ count, select, 1 {One dashboard} other {List of # dashboards} }",
+ "dashboard-name-starts-with": "Dashboards whose names start with '{{prefix}}'",
+ "type-alarm": "Alarm",
+ "type-alarms": "Alarms",
+ "list-of-alarms": "{ count, select, 1 {One alarms} other {List of # alarms} }",
+ "alarm-name-starts-with": "Alarms whose names start with '{{prefix}}'",
+ "type-rulechain": "Rule chain",
+ "type-rulechains": "Rule chains",
+ "list-of-rulechains": "{ count, select, 1 {One rule chain} other {List of # rule chains} }",
+ "rulechain-name-starts-with": "Rule chains whose names start with '{{prefix}}'",
+ "type-current-customer": "Current Customer",
+ "search": "Search entities",
+ "selected-entities": "{ count, select, 1 {1 entity} other {# entities} } selected",
+ "entity-name": "Entity name",
+ "details": "Entity details",
+ "no-entities-prompt": "No entities found",
+ "no-data": "No data to display"
+ },
"event": {
"event-type": "이벤트 타입",
"type-error": "에러",
"type-lc-event": "주기적 이벤트",
"type-stats": "통계",
+ "type-debug-rule-node": "Debug", // TODO
+ "type-debug-rule-chain": "Debug", // TODO
"no-events-prompt": "이벤트 없음",
"error": "에러",
"alarm": "알람",
@@ -387,6 +692,14 @@ export default function addLocaleKorean(locales) {
"server": "서버",
"body": "Body",
"method": "Method",
+ "type": "Type", // TODO
+ "entity": "Entity", // TODO
+ "message-id": "Message Id", // TODO
+ "message-type": "Message Type", // TODO
+ "data-type": "Data Type", // TODO
+ "relation-type": "Relation Type", // TODO
+ "metadata": "Metadata", // TODO
+ "data": "Data", // TODO
"event": "이벤트",
"status": "상태",
"success": "성공",
@@ -394,6 +707,163 @@ export default function addLocaleKorean(locales) {
"messages-processed": "처리된 메시지",
"errors-occurred": "오류가 발생했습니다"
},
+ "extension": { // TODO
+ "extensions": "Extensions",
+ "selected-extensions": "{ count, select, 1 {1 extension} other {# extensions} } selected",
+ "type": "Type",
+ "key": "Key",
+ "value": "Value",
+ "id": "Id",
+ "extension-id": "Extension id",
+ "extension-type": "Extension type",
+ "transformer-json": "JSON *",
+ "unique-id-required": "Current extension id already exists.",
+ "delete": "Delete extension",
+ "add": "Add extension",
+ "edit": "Edit extension",
+ "delete-extension-title": "Are you sure you want to delete the extension '{{extensionId}}'?",
+ "delete-extension-text": "Be careful, after the confirmation the extension and all related data will become unrecoverable.",
+ "delete-extensions-title": "Are you sure you want to delete { count, select, 1 {1 extension} other {# extensions} }?",
+ "delete-extensions-text": "Be careful, after the confirmation all selected extensions will be removed.",
+ "converters": "Converters",
+ "converter-id": "Converter id",
+ "configuration": "Configuration",
+ "converter-configurations": "Converter configurations",
+ "token": "Security token",
+ "add-converter": "Add converter",
+ "add-config": "Add converter configuration",
+ "device-name-expression": "Device name expression",
+ "device-type-expression": "Device type expression",
+ "custom": "Custom",
+ "to-double": "To Double",
+ "transformer": "Transformer",
+ "json-required": "Transformer json is required.",
+ "json-parse": "Unable to parse transformer json.",
+ "attributes": "Attributes",
+ "add-attribute": "Add attribute",
+ "add-map": "Add mapping element",
+ "timeseries": "Timeseries",
+ "add-timeseries": "Add timeseries",
+ "field-required": "Field is required",
+ "brokers": "Brokers",
+ "add-broker": "Add broker",
+ "host": "Host",
+ "port": "Port",
+ "port-range": "Port should be in a range from 1 to 65535.",
+ "ssl": "Ssl",
+ "credentials": "Credentials",
+ "username": "Username",
+ "password": "Password",
+ "retry-interval": "Retry interval in milliseconds",
+ "anonymous": "Anonymous",
+ "basic": "Basic",
+ "pem": "PEM",
+ "ca-cert": "CA certificate file *",
+ "private-key": "Private key file *",
+ "cert": "Certificate file *",
+ "no-file": "No file selected.",
+ "drop-file": "Drop a file or click to select a file to upload.",
+ "mapping": "Mapping",
+ "topic-filter": "Topic filter",
+ "converter-type": "Converter type",
+ "converter-json": "Json",
+ "json-name-expression": "Device name json expression",
+ "topic-name-expression": "Device name topic expression",
+ "json-type-expression": "Device type json expression",
+ "topic-type-expression": "Device type topic expression",
+ "attribute-key-expression": "Attribute key expression",
+ "attr-json-key-expression": "Attribute key json expression",
+ "attr-topic-key-expression": "Attribute key topic expression",
+ "request-id-expression": "Request id expression",
+ "request-id-json-expression": "Request id json expression",
+ "request-id-topic-expression": "Request id topic expression",
+ "response-topic-expression": "Response topic expression",
+ "value-expression": "Value expression",
+ "topic": "Topic",
+ "timeout": "Timeout in milliseconds",
+ "converter-json-required": "Converter json is required.",
+ "converter-json-parse": "Unable to parse converter json.",
+ "filter-expression": "Filter expression",
+ "connect-requests": "Connect requests",
+ "add-connect-request": "Add connect request",
+ "disconnect-requests": "Disconnect requests",
+ "add-disconnect-request": "Add disconnect request",
+ "attribute-requests": "Attribute requests",
+ "add-attribute-request": "Add attribute request",
+ "attribute-updates": "Attribute updates",
+ "add-attribute-update": "Add attribute update",
+ "server-side-rpc": "Server side RPC",
+ "add-server-side-rpc-request": "Add server-side RPC request",
+ "device-name-filter": "Device name filter",
+ "attribute-filter": "Attribute filter",
+ "method-filter": "Method filter",
+ "request-topic-expression": "Request topic expression",
+ "response-timeout": "Response timeout in milliseconds",
+ "topic-expression": "Topic expression",
+ "client-scope": "Client scope",
+ "add-device": "Add device",
+ "opc-server": "Servers",
+ "opc-add-server": "Add server",
+ "opc-add-server-prompt": "Please add server",
+ "opc-application-name": "Application name",
+ "opc-application-uri": "Application uri",
+ "opc-scan-period-in-seconds": "Scan period in seconds",
+ "opc-security": "Security",
+ "opc-identity": "Identity",
+ "opc-keystore": "Keystore",
+ "opc-type": "Type",
+ "opc-keystore-type": "Type",
+ "opc-keystore-location": "Location *",
+ "opc-keystore-password": "Password",
+ "opc-keystore-alias": "Alias",
+ "opc-keystore-key-password": "Key password",
+ "opc-device-node-pattern": "Device node pattern",
+ "opc-device-name-pattern": "Device name pattern",
+ "modbus-server": "Servers/slaves",
+ "modbus-add-server": "Add server/slave",
+ "modbus-add-server-prompt": "Please add server/slave",
+ "modbus-transport": "Transport",
+ "modbus-port-name": "Serial port name",
+ "modbus-encoding": "Encoding",
+ "modbus-parity": "Parity",
+ "modbus-baudrate": "Baud rate",
+ "modbus-databits": "Data bits",
+ "modbus-stopbits": "Stop bits",
+ "modbus-databits-range": "Data bits should be in a range from 7 to 8.",
+ "modbus-stopbits-range": "Stop bits should be in a range from 1 to 2.",
+ "modbus-unit-id": "Unit ID",
+ "modbus-unit-id-range": "Unit ID should be in a range from 1 to 247.",
+ "modbus-device-name": "Device name",
+ "modbus-poll-period": "Poll period (ms)",
+ "modbus-attributes-poll-period": "Attributes poll period (ms)",
+ "modbus-timeseries-poll-period": "Timeseries poll period (ms)",
+ "modbus-poll-period-range": "Poll period should be positive value.",
+ "modbus-tag": "Tag",
+ "modbus-function": "Function",
+ "modbus-register-address": "Register address",
+ "modbus-register-address-range": "Register address should be in a range from 0 to 65535.",
+ "modbus-register-bit-index": "Bit index",
+ "modbus-register-bit-index-range": "Bit index should be in a range from 0 to 15.",
+ "modbus-register-count": "Register count",
+ "modbus-register-count-range": "Register count should be a positive value.",
+ "modbus-byte-order": "Byte order",
+
+ "sync": {
+ "status": "Status",
+ "sync": "Sync",
+ "not-sync": "Not sync",
+ "last-sync-time": "Last sync time",
+ "not-available": "Not available"
+ },
+
+ "export-extensions-configuration": "Export extensions configuration",
+ "import-extensions-configuration": "Import extensions configuration",
+ "import-extensions": "Import extensions",
+ "import-extension": "Import extension",
+ "export-extension": "Export extension",
+ "file": "Extensions file",
+ "invalid-file-error": "Invalid extension file"
+ },
"fullscreen": {
"expand": "전체화면으로 확장",
"exit": "전체화면 종료",
@@ -436,7 +906,24 @@ export default function addLocaleKorean(locales) {
},
"js-func": {
"no-return-error": "함수는 값을 반환해야 합니다!",
- "return-type-mismatch": "함수는 '{{type}}' 유형의 값을 반환해야 합니다!"
+ "return-type-mismatch": "함수는 '{{type}}' 유형의 값을 반환해야 합니다!",
+ "tidy": "Tidy" // TODO
+ },
+ "key-val": { // TODO
+ "key": "Key",
+ "value": "Value",
+ "remove-entry": "Remove entry",
+ "add-entry": "Add entry",
+ "no-data": "No entries"
+ },
+ "layout": { // TODO
+ "layout": "Layout",
+ "manage": "Manage layouts",
+ "settings": "Layout settings",
+ "color": "Color",
+ "main": "Main",
+ "right": "Right",
+ "select": "Select target layout"
},
"legend": {
"position": "범례 위치",
@@ -467,45 +954,6 @@ export default function addLocaleKorean(locales) {
"password-link-sent-message": "비밀번호 재설정 링크가 성공적으로 전송되었습니다!",
"email": "이메일"
},
- "plugin": {
- "plugins": "플러그인",
- "delete": "플러그인 삭제",
- "activate": "플러그인 활성화",
- "suspend": "플러그인 비활성화",
- "active": "활성화",
- "suspended": "비활성화",
- "name": "이름",
- "name-required": "이름을 입력하세요.",
- "description": "설명",
- "add": "플러그인 추가",
- "delete-plugin-title": "'{{pluginName}}' 플러그인을 삭제하시겠습니까?",
- "delete-plugin-text": "플러그인과 관련된 모든 데이터를 복구할 수 없으므로 주의하십시오.",
- "delete-plugins-title": "{ count, select, 1 {플러그인 1개} other {플러그인 #개} }를 삭제하시겠습니까?",
- "delete-plugins-action-title": "{ count, select, 1 {플러그인 1개} other {플러그인 #개} } 삭제",
- "delete-plugins-text": "선택된 플러그인이 삭제되고 관련된 모든 데이터가 없어지므로 주의하십시오.",
- "add-plugin-text": "새로운 플러그인 추가",
- "no-plugins-text": "플러그인이 없습니다.",
- "plugin-details": "플러그인 상세정보",
- "api-token": "API 토큰",
- "api-token-required": "API 토큰을 입력하세요.",
- "type": "플러그인 종류",
- "type-required": "플러그인 종류를 선택해주세요.",
- "configuration": "플러그인 구성",
- "system": "시스템",
- "select-plugin": "플러그인 선택",
- "plugin": "플러그인",
- "no-plugins-matching": "'{{entity}}'과 일치하는 플러그인을 찾을 수 없습니다.",
- "plugin-required": "플러그인을 입력하세요.",
- "plugin-require-match": "기존의 플러그인을 선택해주세요.",
- "events": "이벤트",
- "details": "상세",
- "import": "플러그인 가져오기",
- "export": "플러그인 내보내기",
- "export-failed-error": "플러그인을 내보내기 할 수 없습니다.: {{error}}",
- "create-new-plugin": "새로운 플러그인 생성",
- "plugin-file": "플러그인 파일",
- "invalid-plugin-file-error": "플러그인을 가져오기 할 수 없습니다.: 잘못된 플러그인 데이터 구조입니다."
- },
"position": {
"top": "상단",
"bottom": "하단",
@@ -517,60 +965,139 @@ export default function addLocaleKorean(locales) {
"change-password": "비밀번호 변경",
"current-password": "현재 비밀번호"
},
- "rule": {
- "rules": "규칙",
- "delete": "규칙 삭제",
- "activate": "규칙 활성화",
- "suspend": "규칙 비활성화",
- "active": "활성화",
- "suspended": "비활성화",
- "name": "이름",
- "name-required": "이름을 입력하세요.",
- "description": "설명",
- "add": "규칙 추가",
- "delete-rule-title": "'{{ruleName}}' 규칙을 삭제하시겠습니까?",
- "delete-rule-text": "규칙과 관련된 모든 데이터를 복구할 수 없으므로 주의하십시오.",
- "delete-rules-title": "{ count, select, 1 {규칙 1개} other {규칙 #개} }를 삭제하시겠습니까?",
- "delete-rules-action-title": "{ count, select, 1 {규칙 1개} other {규칙 #개} } 삭제",
- "delete-rules-text": "선택된 규칙이 삭제되고 관련된 모든 데이터를 복구할 수 없으므로 주의하십시오.",
- "add-rule-text": "규칙 추가",
- "no-rules-text": "규칙이 없습니다.",
- "rule-details": "규칙 상세정보",
- "filters": "필터",
- "filter": "필터",
- "add-filter-prompt": "필터를 추가해 주세요.",
- "remove-filter": "필터 삭제",
- "add-filter": "필터 추가",
- "filter-name": "필터 이름",
- "filter-type": "필터 종류",
- "edit-filter": "필터 수정",
- "view-filter": "필터 보기",
- "component-name": "이름",
- "component-name-required": "이름을 입력하세요.",
- "component-type": "종류",
- "component-type-required": "타입을 입력하세요.",
- "processor": "프로세서",
- "no-processor-configured": "프로세서가 구성되지 않았습니다.",
- "create-processor": "프로세서 생성",
- "processor-name": "프로세서 이름",
- "processor-type": "프로세서 종류",
- "plugin-action": "플러그인 액션",
- "action-name": "액션 이름",
- "action-type": "액션 종류",
- "create-action-prompt": "액션을 만들어 주세요",
- "create-action": "액션 생성",
- "details": "상세",
- "events": "이벤트",
- "system": "시스템",
- "import": "규칙 가져오기",
- "export": "규칙 내보내기",
- "export-failed-error": "규칙을 내보내기 할 수 없습니다.: {{error}}",
- "create-new-rule": "새로운 규칙 생성",
- "rule-file": "규칙 파일",
- "invalid-rule-file-error": "규칙을 가져오기 할 수 없습니다.: 잘못된 데이터 구조입니다."
+ "relation": { // TODO
+ "relations": "Relations",
+ "direction": "Direction",
+ "search-direction": {
+ "FROM": "From",
+ "TO": "To"
+ },
+ "direction-type": {
+ "FROM": "from",
+ "TO": "to"
+ },
+ "from-relations": "Outbound relations",
+ "to-relations": "Inbound relations",
+ "selected-relations": "{ count, select, 1 {1 relation} other {# relations} } selected",
+ "type": "Type",
+ "to-entity-type": "To entity type",
+ "to-entity-name": "To entity name",
+ "from-entity-type": "From entity type",
+ "from-entity-name": "From entity name",
+ "to-entity": "To entity",
+ "from-entity": "From entity",
+ "delete": "Delete relation",
+ "relation-type": "Relation type",
+ "relation-type-required": "Relation type is required.",
+ "any-relation-type": "Any type",
+ "add": "Add relation",
+ "edit": "Edit relation",
+ "delete-to-relation-title": "Are you sure you want to delete relation to the entity '{{entityName}}'?",
+ "delete-to-relation-text": "Be careful, after the confirmation the entity '{{entityName}}' will be unrelated from the current entity.",
+ "delete-to-relations-title": "Are you sure you want to delete { count, select, 1 {1 relation} other {# relations} }?",
+ "delete-to-relations-text": "Be careful, after the confirmation all selected relations will be removed and corresponding entities will be unrelated from the current entity.",
+ "delete-from-relation-title": "Are you sure you want to delete relation from the entity '{{entityName}}'?",
+ "delete-from-relation-text": "Be careful, after the confirmation current entity will be unrelated from the entity '{{entityName}}'.",
+ "delete-from-relations-title": "Are you sure you want to delete { count, select, 1 {1 relation} other {# relations} }?",
+ "delete-from-relations-text": "Be careful, after the confirmation all selected relations will be removed and current entity will be unrelated from the corresponding entities.",
+ "remove-relation-filter": "Remove relation filter",
+ "add-relation-filter": "Add relation filter",
+ "any-relation": "Any relation",
+ "relation-filters": "Relation filters",
+ "additional-info": "Additional info (JSON)",
+ "invalid-additional-info": "Unable to parse additional info json."
+ },
+ "rulechain": { // TODO
+ "rulechain": "Rule chain",
+ "rulechains": "Rule chains",
+ "root": "Root",
+ "delete": "Delete rule chain",
+ "name": "Name",
+ "name-required": "Name is required.",
+ "description": "Description",
+ "add": "Add Rule Chain",
+ "set-root": "Make rule chain root",
+ "set-root-rulechain-title": "Are you sure you want to make the rule chain '{{ruleChainName}}' root?",
+ "set-root-rulechain-text": "After the confirmation the rule chain will become root and will handle all incoming transport messages.",
+ "delete-rulechain-title": "Are you sure you want to delete the rule chain '{{ruleChainName}}'?",
+ "delete-rulechain-text": "Be careful, after the confirmation the rule chain and all related data will become unrecoverable.",
+ "delete-rulechains-title": "Are you sure you want to delete { count, select, 1 {1 rule chain} other {# rule chains} }?",
+ "delete-rulechains-action-title": "Delete { count, select, 1 {1 rule chain} other {# rule chains} }",
+ "delete-rulechains-text": "Be careful, after the confirmation all selected rule chains will be removed and all related data will become unrecoverable.",
+ "add-rulechain-text": "Add new rule chain",
+ "no-rulechains-text": "No rule chains found",
+ "rulechain-details": "Rule chain details",
+ "details": "Details",
+ "events": "Events",
+ "system": "System",
+ "import": "Import rule chain",
+ "export": "Export rule chain",
+ "export-failed-error": "Unable to export rule chain: {{error}}",
+ "create-new-rulechain": "Create new rule chain",
+ "rulechain-file": "Rule chain file",
+ "invalid-rulechain-file-error": "Unable to import rule chain: Invalid rule chain data structure.",
+ "copyId": "Copy rule chain Id",
+ "idCopiedMessage": "Rule chain Id has been copied to clipboard",
+ "select-rulechain": "Select rule chain",
+ "no-rulechains-matching": "No rule chains matching '{{entity}}' were found.",
+ "rulechain-required": "Rule chain is required",
+ "management": "Rules management",
+ "debug-mode": "Debug mode"
},
- "rule-plugin": {
- "management": "규칙 및 플러그인 관리"
+ "rulenode": { // TODO
+ "details": "Details",
+ "events": "Events",
+ "search": "Search nodes",
+ "open-node-library": "Open node library",
+ "add": "Add rule node",
+ "name": "Name",
+ "name-required": "Name is required.",
+ "type": "Type",
+ "description": "Description",
+ "delete": "Delete rule node",
+ "select-all-objects": "Select all nodes and connections",
+ "deselect-all-objects": "Deselect all nodes and connections",
+ "delete-selected-objects": "Delete selected nodes and connections",
+ "delete-selected": "Delete selected",
+ "select-all": "Select all",
+ "copy-selected": "Copy selected",
+ "deselect-all": "Deselect all",
+ "rulenode-details": "Rule node details",
+ "debug-mode": "Debug mode",
+ "configuration": "Configuration",
+ "link": "Link",
+ "link-details": "Rule node link details",
+ "add-link": "Add link",
+ "link-label": "Link label",
+ "link-label-required": "Link label is required.",
+ "custom-link-label": "Custom link label",
+ "custom-link-label-required": "Custom link label is required.",
+ "type-filter": "Filter",
+ "type-filter-details": "Filter incoming messages with configured conditions",
+ "type-enrichment": "Enrichment",
+ "type-enrichment-details": "Add additional information into Message Metadata",
+ "type-transformation": "Transformation",
+ "type-transformation-details": "Change Message payload and Metadata",
+ "type-action": "Action",
+ "type-action-details": "Perform special action",
+ "type-external": "External",
+ "type-external-details": "Interacts with external system",
+ "type-rule-chain": "Rule Chain",
+ "type-rule-chain-details": "Forwards incoming messages to specified Rule Chain",
+ "type-input": "Input",
+ "type-input-details": "Logical input of Rule Chain, forwards incoming messages to next related Rule Node",
+ "directive-is-not-loaded": "Defined configuration directive '{{directiveName}}' is not available.",
+ "ui-resources-load-error": "Failed to load configuration ui resources.",
+ "invalid-target-rulechain": "Unable to resolve target rule chain!",
+ "test-script-function": "Test script function",
+ "message": "Message",
+ "message-type": "Message type",
+ "message-type-required": "Message type is required",
+ "metadata": "Metadata",
+ "metadata-required": "Metadata entries can't be empty.",
+ "output": "Output",
+ "test": "Test",
+ "help": "Help"
},
"tenant": {
"tenants": "테넌트",
@@ -589,7 +1116,14 @@ export default function addLocaleKorean(locales) {
"delete-tenants-text": "선택된 테넌트가 삭제되고 관련된 모든 정보를 복구할 수 없으므로 주의하십시오.",
"title": "타이틀",
"title-required": "타이틀을 입력하세요.",
- "description": "설명"
+ "description": "설명",
+ "details": "Details", // TODO
+ "events": "Events", // TODO
+ "copyId": "Copy tenant Id", // TODO
+ "idCopiedMessage": "Tenant Id has been copied to clipboard", // TODO
+ "select-tenant": "Select tenant", // TODO
+ "no-tenants-matching": "No tenants matching '{{entity}}' were found.",
+ "tenant-required": "Tenant is required" // TODO
},
"timeinterval": {
"seconds-interval": "{ seconds, select, 1 {1 second} other {# seconds} }",
@@ -642,7 +1176,18 @@ export default function addLocaleKorean(locales) {
"last-name": "성",
"description": "설명",
"default-dashboard": "기본 대시보드",
- "always-fullscreen": "항상 전체화면"
+ "always-fullscreen": "항상 전체화면",
+ "select-user": "Select user", // TODO
+ "no-users-matching": "No users matching '{{entity}}' were found.", // TODO
+ "user-required": "User is required", // TODO
+ "activation-method": "Activation method", // TODO
+ "display-activation-link": "Display activation link", // TODO
+ "send-activation-mail": "Send activation mail", // TODO
+ "activation-link": "User activation link", // TODO
+ "activation-link-text": "In order to activate user use the following <a href='{{activationLink}}' target='_blank'>activation link</a> :", // TODO
+ "copy-activation-link": "Copy activation link", // TODO
+ "activation-link-copied-message": "User activation link has been copied to clipboard", // TODO
+ "details": "Details" // TODO
},
"value": {
"type": "Value type",
@@ -707,6 +1252,18 @@ export default function addLocaleKorean(locales) {
"undo": "위젯 변경사항 취소",
"export": "위젯 내보내기"
},
+ "widget-action": { // TODO
+ "header-button": "Widget header button",
+ "open-dashboard-state": "Navigate to new dashboard state",
+ "update-dashboard-state": "Update current dashboard state",
+ "open-dashboard": "Navigate to other dashboard",
+ "custom": "Custom action",
+ "target-dashboard-state": "Target dashboard state",
+ "target-dashboard-state-required": "Target dashboard state is required",
+ "set-entity-from-widget": "Set entity from widget",
+ "target-dashboard": "Target dashboard",
+ "open-right-layout": "Open right dashboard layout (mobile view)"
+ },
"widgets-bundle": {
"current": "현재 번들",
"widgets-bundles": "위젯 번들",
@@ -770,6 +1327,20 @@ export default function addLocaleKorean(locales) {
"widget-type-file": "위젯 타입 파일",
"invalid-widget-type-file-error": "위젯 타입을 가져오기 할 수 없습니다.: 잘못된 위젯 타입 데이터 구조입니다."
},
+ "icon": { // TODO
+ "icon": "Icon",
+ "select-icon": "Select icon",
+ "material-icons": "Material icons",
+ "show-all": "Show all icons"
+ },
+ "custom": {
+ "widget-action": {
+ "action-cell-button": "Action cell button",
+ "row-click": "On row click",
+ "marker-click": "On marker click",
+ "tooltip-tag-action": "Tooltip tag action"
+ }
+ },
"language": {
"language": "언어",
"en_US": "영어",
@@ -779,5 +1350,5 @@ export default function addLocaleKorean(locales) {
"es_ES": "스페인어"
}
};
- angular.extend(locales, {'ko_KR': ko_KR});
+ angular.extend(locales, { 'ko_KR': ko_KR });
}
\ No newline at end of file
ui/src/app/locale/locale.constant-ru.js 760(+662 -98)
diff --git a/ui/src/app/locale/locale.constant-ru.js b/ui/src/app/locale/locale.constant-ru.js
index db66834..cff9880 100644
--- a/ui/src/app/locale/locale.constant-ru.js
+++ b/ui/src/app/locale/locale.constant-ru.js
@@ -98,6 +98,159 @@ export default function addLocaleRussian(locales) {
"enable-tls": "Включить TLS",
"send-test-mail": "Отправить пробное письмо"
},
+ "alarm": { // TODO
+ "alarm": "Alarm",
+ "alarms": "Alarms",
+ "select-alarm": "Select alarm",
+ "no-alarms-matching": "No alarms matching '{{entity}}' were found.",
+ "alarm-required": "Alarm is required",
+ "alarm-status": "Alarm status",
+ "search-status": {
+ "ANY": "Any",
+ "ACTIVE": "Active",
+ "CLEARED": "Cleared",
+ "ACK": "Acknowledged",
+ "UNACK": "Unacknowledged"
+ },
+ "display-status": {
+ "ACTIVE_UNACK": "Active Unacknowledged",
+ "ACTIVE_ACK": "Active Acknowledged",
+ "CLEARED_UNACK": "Cleared Unacknowledged",
+ "CLEARED_ACK": "Cleared Acknowledged"
+ },
+ "no-alarms-prompt": "No alarms found",
+ "created-time": "Created time",
+ "type": "Type",
+ "severity": "Severity",
+ "originator": "Originator",
+ "originator-type": "Originator type",
+ "details": "Details",
+ "status": "Status",
+ "alarm-details": "Alarm details",
+ "start-time": "Start time",
+ "end-time": "End time",
+ "ack-time": "Acknowledged time",
+ "clear-time": "Cleared time",
+ "severity-critical": "Critical",
+ "severity-major": "Major",
+ "severity-minor": "Minor",
+ "severity-warning": "Warning",
+ "severity-indeterminate": "Indeterminate",
+ "acknowledge": "Acknowledge",
+ "clear": "Clear",
+ "search": "Search alarms",
+ "selected-alarms": "{ count, select, 1 {1 alarm} other {# alarms} } selected",
+ "no-data": "No data to display",
+ "polling-interval": "Alarms polling interval (sec)",
+ "polling-interval-required": "Alarms polling interval is required.",
+ "min-polling-interval-message": "At least 1 sec polling interval is allowed.",
+ "aknowledge-alarms-title": "Acknowledge { count, select, 1 {1 alarm} other {# alarms} }",
+ "aknowledge-alarms-text": "Are you sure you want to acknowledge { count, select, 1 {1 alarm} other {# alarms} }?",
+ "clear-alarms-title": "Clear { count, select, 1 {1 alarm} other {# alarms} }",
+ "clear-alarms-text": "Are you sure you want to clear { count, select, 1 {1 alarm} other {# alarms} }?"
+ },
+ "alias": { // TODO
+ "add": "Add alias",
+ "edit": "Edit alias",
+ "name": "Alias name",
+ "name-required": "Alias name is required",
+ "duplicate-alias": "Alias with same name is already exists.",
+ "filter-type-single-entity": "Single entity",
+ "filter-type-entity-list": "Entity list",
+ "filter-type-entity-name": "Entity name",
+ "filter-type-state-entity": "Entity from dashboard state",
+ "filter-type-state-entity-description": "Entity taken from dashboard state parameters",
+ "filter-type-asset-type": "Asset type",
+ "filter-type-asset-type-description": "Assets of type '{{assetType}}'",
+ "filter-type-asset-type-and-name-description": "Assets of type '{{assetType}}' and with name starting with '{{prefix}}'",
+ "filter-type-device-type": "Device type",
+ "filter-type-device-type-description": "Devices of type '{{deviceType}}'",
+ "filter-type-device-type-and-name-description": "Devices of type '{{deviceType}}' and with name starting with '{{prefix}}'",
+ "filter-type-relations-query": "Relations query",
+ "filter-type-relations-query-description": "{{entities}} that have {{relationType}} relation {{direction}} {{rootEntity}}",
+ "filter-type-asset-search-query": "Asset search query",
+ "filter-type-asset-search-query-description": "Assets with types {{assetTypes}} that have {{relationType}} relation {{direction}} {{rootEntity}}",
+ "filter-type-device-search-query": "Device search query",
+ "filter-type-device-search-query-description": "Devices with types {{deviceTypes}} that have {{relationType}} relation {{direction}} {{rootEntity}}",
+ "entity-filter": "Entity filter",
+ "resolve-multiple": "Resolve as multiple entities",
+ "filter-type": "Filter type",
+ "filter-type-required": "Filter type is required.",
+ "entity-filter-no-entity-matched": "No entities matching specified filter were found.",
+ "no-entity-filter-specified": "No entity filter specified",
+ "root-state-entity": "Use dashboard state entity as root",
+ "root-entity": "Root entity",
+ "state-entity-parameter-name": "State entity parameter name",
+ "default-state-entity": "Default state entity",
+ "default-entity-parameter-name": "By default",
+ "max-relation-level": "Max relation level",
+ "unlimited-level": "Unlimited level",
+ "state-entity": "Dashboard state entity",
+ "all-entities": "All entities",
+ "any-relation": "any"
+ },
+ "asset": { // TODO
+ "asset": "Asset",
+ "assets": "Assets",
+ "management": "Asset management",
+ "view-assets": "View Assets",
+ "add": "Add Asset",
+ "assign-to-customer": "Assign to customer",
+ "assign-asset-to-customer": "Assign Asset(s) To Customer",
+ "assign-asset-to-customer-text": "Please select the assets to assign to the customer",
+ "no-assets-text": "No assets found",
+ "assign-to-customer-text": "Please select the customer to assign the asset(s)",
+ "public": "Public",
+ "assignedToCustomer": "Assigned to customer",
+ "make-public": "Make asset public",
+ "make-private": "Make asset private",
+ "unassign-from-customer": "Unassign from customer",
+ "delete": "Delete asset",
+ "asset-public": "Asset is public",
+ "asset-type": "Asset type",
+ "asset-type-required": "Asset type is required.",
+ "select-asset-type": "Select asset type",
+ "enter-asset-type": "Enter asset type",
+ "any-asset": "Any asset",
+ "no-asset-types-matching": "No asset types matching '{{entitySubtype}}' were found.",
+ "asset-type-list-empty": "No asset types selected.",
+ "asset-types": "Asset types",
+ "name": "Name",
+ "name-required": "Name is required.",
+ "description": "Description",
+ "type": "Type",
+ "type-required": "Type is required.",
+ "details": "Details",
+ "events": "Events",
+ "add-asset-text": "Add new asset",
+ "asset-details": "Asset details",
+ "assign-assets": "Assign assets",
+ "assign-assets-text": "Assign { count, select, 1 {1 asset} other {# assets} } to customer",
+ "delete-assets": "Delete assets",
+ "unassign-assets": "Unassign assets",
+ "unassign-assets-action-title": "Unassign { count, select, 1 {1 asset} other {# assets} } from customer",
+ "assign-new-asset": "Assign new asset",
+ "delete-asset-title": "Are you sure you want to delete the asset '{{assetName}}'?",
+ "delete-asset-text": "Be careful, after the confirmation the asset and all related data will become unrecoverable.",
+ "delete-assets-title": "Are you sure you want to delete { count, select, 1 {1 asset} other {# assets} }?",
+ "delete-assets-action-title": "Delete { count, select, 1 {1 asset} other {# assets} }",
+ "delete-assets-text": "Be careful, after the confirmation all selected assets will be removed and all related data will become unrecoverable.",
+ "make-public-asset-title": "Are you sure you want to make the asset '{{assetName}}' public?",
+ "make-public-asset-text": "After the confirmation the asset and all its data will be made public and accessible by others.",
+ "make-private-asset-title": "Are you sure you want to make the asset '{{assetName}}' private?",
+ "make-private-asset-text": "After the confirmation the asset and all its data will be made private and won't be accessible by others.",
+ "unassign-asset-title": "Are you sure you want to unassign the asset '{{assetName}}'?",
+ "unassign-asset-text": "After the confirmation the asset will be unassigned and won't be accessible by the customer.",
+ "unassign-asset": "Unassign asset",
+ "unassign-assets-title": "Are you sure you want to unassign { count, select, 1 {1 asset} other {# assets} }?",
+ "unassign-assets-text": "After the confirmation all selected assets will be unassigned and won't be accessible by the customer.",
+ "copyId": "Copy asset Id",
+ "idCopiedMessage": "Asset Id has been copied to clipboard",
+ "select-asset": "Select asset",
+ "no-assets-matching": "No assets matching '{{entity}}' were found.",
+ "asset-required": "Asset is required",
+ "name-starts-with": "Asset name starts with"
+ },
"attribute": {
"attributes": "Атрибуты",
"latest-telemetry": "Последняя телеметрия",
@@ -124,6 +277,38 @@ export default function addLocaleRussian(locales) {
"selected-attributes": "{ count, plural, 1 {Выбран} other {Выбраны} } { count, plural, one {1 атрибут} few {# атрибута} other {# атрибутов} }",
"selected-telemetry": "{ count, plural, 1 {Выбран} other {Выбраны} } { count, plural, 1 {1 параметр} few {# параметра} other {# параметров} } телеметрии"
},
+ "audit-log": { // TODO
+ "audit": "Audit",
+ "audit-logs": "Audit Logs",
+ "timestamp": "Timestamp",
+ "entity-type": "Entity Type",
+ "entity-name": "Entity Name",
+ "user": "User",
+ "type": "Type",
+ "status": "Status",
+ "details": "Details",
+ "type-added": "Added",
+ "type-deleted": "Deleted",
+ "type-updated": "Updated",
+ "type-attributes-updated": "Attributes updated",
+ "type-attributes-deleted": "Attributes deleted",
+ "type-rpc-call": "RPC call",
+ "type-credentials-updated": "Credentials updated",
+ "type-assigned-to-customer": "Assigned to Customer",
+ "type-unassigned-from-customer": "Unassigned from Customer",
+ "type-activated": "Activated",
+ "type-suspended": "Suspended",
+ "type-credentials-read": "Credentials read",
+ "type-attributes-read": "Attributes read",
+ "status-success": "Success",
+ "status-failure": "Failure",
+ "audit-log-details": "Audit log details",
+ "no-audit-logs-prompt": "No logs found",
+ "action-data": "Action data",
+ "failure-details": "Failure details",
+ "search": "Search audit logs",
+ "clear-search": "Clear search"
+ },
"confirm-on-exit": {
"message": "У вас есть несохраненные изменения. Вы точно хотите покинуть эту страницу?",
"html-message": "У вас есть несохраненные изменения.<br/>Вы точно хотите покинуть эту страницу?",
@@ -148,6 +333,11 @@ export default function addLocaleRussian(locales) {
"enter-password": "Введите пароль",
"enter-search": "Введите условие поиска"
},
+ "content-type": { // TODO
+ "json": "Json",
+ "text": "Text",
+ "binary": "Binary (Base64)"
+ },
"customer": {
"customers": "Клиенты",
"management": "Управление клиентами",
@@ -172,11 +362,22 @@ export default function addLocaleRussian(locales) {
"delete-customers-action-title": "Удалить { count, plural, one {1 клиента} other {# клиентов} } }",
"delete-customers-text": "Внимание, после подтверждения клиенты и вся связанная с ними информация будут безвозвратно утеряны.",
"manage-users": "Управление пользователями",
+ "manage-assets": "Manage assets", // TODO
"manage-devices": "Управление устройствами",
"manage-dashboards": "Управление дашбордами",
"title": "Имя",
"title-required": "Название обязательно.",
- "description": "Описание"
+ "description": "Описание",
+ "details": "Details", // TODO
+ "events": "Events", // TODO
+ "copyId": "Copy customer Id", // TODO
+ "idCopiedMessage": "Customer Id has been copied to clipboard", // TODO
+ "select-customer": "Select customer", // TODO
+ "no-customers-matching": "No customers matching '{{entity}}' were found.", // TODO
+ "customer-required": "Customer is required", // TODO
+ "select-default-customer": "Select default customer", // TODO
+ "default-customer": "Default customer", // TODO
+ "default-customer-required": "Default customer is required in order to debug dashboard on Tenant level" // TODO
},
"datetime": {
"date-from": "Дата с",
@@ -282,19 +483,41 @@ export default function addLocaleRussian(locales) {
"configuration-error": "Ошибка конфигурирования",
"alias-resolution-error-title": "Ошибка конфигурирования псевдонимов дашборда",
"invalid-aliases-config": "Не удалось найти устройства, соответствующие фильтру псевдонимов.<br/>" +
- "Пожалуйста, свяжитесь с администратором для устранения этой проблемы.",
+ "Пожалуйста, свяжитесь с администратором для устранения этой проблемы.",
"select-devices": "Выберите устройства",
"assignedToCustomer": "Прикреплен к клиенту",
"public": "Общедоступный",
"public-link": "Общедоступная ссылка",
"copy-public-link": "Скопировать общедоступную ссылку",
- "public-link-copied-message": "Общедоступная ссылка на дашборд скопирована в буфер обмена"
+ "public-link-copied-message": "Общедоступная ссылка на дашборд скопирована в буфер обмена",
+ "manage-states": "Manage dashboard states", // TODO
+ "states": "Dashboard states", // TODO
+ "search-states": "Search dashboard states", // TODO
+ "selected-states": "{ count, select, 1 {1 dashboard state} other {# dashboard states} } selected", // TODO
+ "edit-state": "Edit dashboard state", // TODO
+ "delete-state": "Delete dashboard state", // TODO
+ "add-state": "Add dashboard state", // TODO
+ "state": "Dashboard state", // TODO
+ "state-name": "Name", // TODO
+ "state-name-required": "Dashboard state name is required.", // TODO
+ "state-id": "State Id", // TODO
+ "state-id-required": "Dashboard state id is required.", // TODO
+ "state-id-exists": "Dashboard state with the same id is already exists.", // TODO
+ "is-root-state": "Root state", // TODO
+ "delete-state-title": "Delete dashboard state", // TODO
+ "delete-state-text": "Are you sure you want delete dashboard state with name '{{stateName}}'?", // TODO
+ "show-details": "Show details", // TODO
+ "hide-details": "Hide details", // TODO
+ "select-state": "Select target state", // TODO
+ "state-controller": "State controller" // TODO
},
"datakey": {
"settings": "Настройки",
"advanced": "Дополнительно",
"label": "Метка",
"color": "Цвет",
+ "units": "Special symbol to show next to value", // TODO
+ "decimals": "Number of digits after floating point", // TODO
"data-generation-func": "Функция генерации данных",
"use-data-post-processing-func": "Использовать функцию пост-обработки данных",
"configuration": "Конфигурация ключа данных",
@@ -302,8 +525,11 @@ export default function addLocaleRussian(locales) {
"attributes": "Атрибуты",
"timeseries-required": "Выборка по времени обязательна.",
"timeseries-or-attributes-required": "Выборка по времени/атрибуты обязательны.",
+ "maximum-timeseries-or-attributes": "Maximum { count, select, 1 {1 timeseries/attribute is allowed.} other {# timeseries/attributes are allowed} }", // TODO
+ "alarm-fields-required": "Alarm fields are required.", // TODO
"function-types": "Тип функции",
- "function-types-required": "Тип функции обязателен."
+ "function-types-required": "Тип функции обязателен.",
+ "maximum-function-types": "Maximum { count, select, 1 {1 function type is allowed.} other {# function types are allowed} }" // TODO
},
"datasource": {
"type": "Тип источника данных",
@@ -408,11 +634,101 @@ export default function addLocaleRussian(locales) {
"unhandled-error-code": "Код необработанной ошибки: {{errorCode}}",
"unknown-error": "Неизвестная ошибка"
},
+ "entity": { // TODO
+ "entity": "Entity",
+ "entities": "Entities",
+ "aliases": "Entity aliases",
+ "entity-alias": "Entity alias",
+ "unable-delete-entity-alias-title": "Unable to delete entity alias",
+ "unable-delete-entity-alias-text": "Entity alias '{{entityAlias}}' can't be deleted as it used by the following widget(s):<br/>{{widgetsList}}",
+ "duplicate-alias-error": "Duplicate alias found '{{alias}}'.<br>Entity aliases must be unique whithin the dashboard.",
+ "missing-entity-filter-error": "Filter is missing for alias '{{alias}}'.",
+ "configure-alias": "Configure '{{alias}}' alias",
+ "alias": "Alias",
+ "alias-required": "Entity alias is required.",
+ "remove-alias": "Remove entity alias",
+ "add-alias": "Add entity alias",
+ "entity-list": "Entity list",
+ "entity-type": "Entity type",
+ "entity-types": "Entity types",
+ "entity-type-list": "Entity type list",
+ "any-entity": "Any entity",
+ "enter-entity-type": "Enter entity type",
+ "no-entities-matching": "No entities matching '{{entity}}' were found.",
+ "no-entity-types-matching": "No entity types matching '{{entityType}}' were found.",
+ "name-starts-with": "Name starts with",
+ "use-entity-name-filter": "Use filter",
+ "entity-list-empty": "No entities selected.",
+ "entity-type-list-empty": "No entity types selected.",
+ "entity-name-filter-required": "Entity name filter is required.",
+ "entity-name-filter-no-entity-matched": "No entities starting with '{{entity}}' were found.",
+ "all-subtypes": "All",
+ "select-entities": "Select entities",
+ "no-aliases-found": "No aliases found.",
+ "no-alias-matching": "'{{alias}}' not found.",
+ "create-new-alias": "Create a new one!",
+ "key": "Key",
+ "key-name": "Key name",
+ "no-keys-found": "No keys found.",
+ "no-key-matching": "'{{key}}' not found.",
+ "create-new-key": "Create a new one!",
+ "type": "Type",
+ "type-required": "Entity type is required.",
+ "type-device": "Device",
+ "type-devices": "Devices",
+ "list-of-devices": "{ count, select, 1 {One device} other {List of # devices} }",
+ "device-name-starts-with": "Devices whose names start with '{{prefix}}'",
+ "type-asset": "Asset",
+ "type-assets": "Assets",
+ "list-of-assets": "{ count, select, 1 {One asset} other {List of # assets} }",
+ "asset-name-starts-with": "Assets whose names start with '{{prefix}}'",
+ "type-rule": "Rule",
+ "type-rules": "Rules",
+ "list-of-rules": "{ count, select, 1 {One rule} other {List of # rules} }",
+ "rule-name-starts-with": "Rules whose names start with '{{prefix}}'",
+ "type-plugin": "Plugin",
+ "type-plugins": "Plugins",
+ "list-of-plugins": "{ count, select, 1 {One plugin} other {List of # plugins} }",
+ "plugin-name-starts-with": "Plugins whose names start with '{{prefix}}'",
+ "type-tenant": "Tenant",
+ "type-tenants": "Tenants",
+ "list-of-tenants": "{ count, select, 1 {One tenant} other {List of # tenants} }",
+ "tenant-name-starts-with": "Tenants whose names start with '{{prefix}}'",
+ "type-customer": "Customer",
+ "type-customers": "Customers",
+ "list-of-customers": "{ count, select, 1 {One customer} other {List of # customers} }",
+ "customer-name-starts-with": "Customers whose names start with '{{prefix}}'",
+ "type-user": "User",
+ "type-users": "Users",
+ "list-of-users": "{ count, select, 1 {One user} other {List of # users} }",
+ "user-name-starts-with": "Users whose names start with '{{prefix}}'",
+ "type-dashboard": "Dashboard",
+ "type-dashboards": "Dashboards",
+ "list-of-dashboards": "{ count, select, 1 {One dashboard} other {List of # dashboards} }",
+ "dashboard-name-starts-with": "Dashboards whose names start with '{{prefix}}'",
+ "type-alarm": "Alarm",
+ "type-alarms": "Alarms",
+ "list-of-alarms": "{ count, select, 1 {One alarms} other {List of # alarms} }",
+ "alarm-name-starts-with": "Alarms whose names start with '{{prefix}}'",
+ "type-rulechain": "Rule chain",
+ "type-rulechains": "Rule chains",
+ "list-of-rulechains": "{ count, select, 1 {One rule chain} other {List of # rule chains} }",
+ "rulechain-name-starts-with": "Rule chains whose names start with '{{prefix}}'",
+ "type-current-customer": "Current Customer",
+ "search": "Search entities",
+ "selected-entities": "{ count, select, 1 {1 entity} other {# entities} } selected",
+ "entity-name": "Entity name",
+ "details": "Entity details",
+ "no-entities-prompt": "No entities found",
+ "no-data": "No data to display"
+ },
"event": {
"event-type": "Тип события",
"type-error": "Ошибка",
"type-lc-event": "Событие жизненного цикла",
"type-stats": "Статистика",
+ "type-debug-rule-node": "Debug", // TODO
+ "type-debug-rule-chain": "Debug", // TODO
"no-events-prompt": "События не найдены",
"error": "Ошибка",
"alarm": "Аварийное оповещение",
@@ -420,6 +736,14 @@ export default function addLocaleRussian(locales) {
"server": "Сервер",
"body": "Тело",
"method": "Метод",
+ "type": "Type", // TODO
+ "entity": "Entity", // TODO
+ "message-id": "Message Id", // TODO
+ "message-type": "Message Type", // TODO
+ "data-type": "Data Type", // TODO
+ "relation-type": "Relation Type", // TODO
+ "metadata": "Metadata", // TODO
+ "data": "Data", // TODO
"event": "Событие",
"status": "Статус",
"success": "Успех",
@@ -427,6 +751,163 @@ export default function addLocaleRussian(locales) {
"messages-processed": "Сообщения обработаны",
"errors-occurred": "Возникли ошибки"
},
+ "extension": { // TODO
+ "extensions": "Extensions",
+ "selected-extensions": "{ count, select, 1 {1 extension} other {# extensions} } selected",
+ "type": "Type",
+ "key": "Key",
+ "value": "Value",
+ "id": "Id",
+ "extension-id": "Extension id",
+ "extension-type": "Extension type",
+ "transformer-json": "JSON *",
+ "unique-id-required": "Current extension id already exists.",
+ "delete": "Delete extension",
+ "add": "Add extension",
+ "edit": "Edit extension",
+ "delete-extension-title": "Are you sure you want to delete the extension '{{extensionId}}'?",
+ "delete-extension-text": "Be careful, after the confirmation the extension and all related data will become unrecoverable.",
+ "delete-extensions-title": "Are you sure you want to delete { count, select, 1 {1 extension} other {# extensions} }?",
+ "delete-extensions-text": "Be careful, after the confirmation all selected extensions will be removed.",
+ "converters": "Converters",
+ "converter-id": "Converter id",
+ "configuration": "Configuration",
+ "converter-configurations": "Converter configurations",
+ "token": "Security token",
+ "add-converter": "Add converter",
+ "add-config": "Add converter configuration",
+ "device-name-expression": "Device name expression",
+ "device-type-expression": "Device type expression",
+ "custom": "Custom",
+ "to-double": "To Double",
+ "transformer": "Transformer",
+ "json-required": "Transformer json is required.",
+ "json-parse": "Unable to parse transformer json.",
+ "attributes": "Attributes",
+ "add-attribute": "Add attribute",
+ "add-map": "Add mapping element",
+ "timeseries": "Timeseries",
+ "add-timeseries": "Add timeseries",
+ "field-required": "Field is required",
+ "brokers": "Brokers",
+ "add-broker": "Add broker",
+ "host": "Host",
+ "port": "Port",
+ "port-range": "Port should be in a range from 1 to 65535.",
+ "ssl": "Ssl",
+ "credentials": "Credentials",
+ "username": "Username",
+ "password": "Password",
+ "retry-interval": "Retry interval in milliseconds",
+ "anonymous": "Anonymous",
+ "basic": "Basic",
+ "pem": "PEM",
+ "ca-cert": "CA certificate file *",
+ "private-key": "Private key file *",
+ "cert": "Certificate file *",
+ "no-file": "No file selected.",
+ "drop-file": "Drop a file or click to select a file to upload.",
+ "mapping": "Mapping",
+ "topic-filter": "Topic filter",
+ "converter-type": "Converter type",
+ "converter-json": "Json",
+ "json-name-expression": "Device name json expression",
+ "topic-name-expression": "Device name topic expression",
+ "json-type-expression": "Device type json expression",
+ "topic-type-expression": "Device type topic expression",
+ "attribute-key-expression": "Attribute key expression",
+ "attr-json-key-expression": "Attribute key json expression",
+ "attr-topic-key-expression": "Attribute key topic expression",
+ "request-id-expression": "Request id expression",
+ "request-id-json-expression": "Request id json expression",
+ "request-id-topic-expression": "Request id topic expression",
+ "response-topic-expression": "Response topic expression",
+ "value-expression": "Value expression",
+ "topic": "Topic",
+ "timeout": "Timeout in milliseconds",
+ "converter-json-required": "Converter json is required.",
+ "converter-json-parse": "Unable to parse converter json.",
+ "filter-expression": "Filter expression",
+ "connect-requests": "Connect requests",
+ "add-connect-request": "Add connect request",
+ "disconnect-requests": "Disconnect requests",
+ "add-disconnect-request": "Add disconnect request",
+ "attribute-requests": "Attribute requests",
+ "add-attribute-request": "Add attribute request",
+ "attribute-updates": "Attribute updates",
+ "add-attribute-update": "Add attribute update",
+ "server-side-rpc": "Server side RPC",
+ "add-server-side-rpc-request": "Add server-side RPC request",
+ "device-name-filter": "Device name filter",
+ "attribute-filter": "Attribute filter",
+ "method-filter": "Method filter",
+ "request-topic-expression": "Request topic expression",
+ "response-timeout": "Response timeout in milliseconds",
+ "topic-expression": "Topic expression",
+ "client-scope": "Client scope",
+ "add-device": "Add device",
+ "opc-server": "Servers",
+ "opc-add-server": "Add server",
+ "opc-add-server-prompt": "Please add server",
+ "opc-application-name": "Application name",
+ "opc-application-uri": "Application uri",
+ "opc-scan-period-in-seconds": "Scan period in seconds",
+ "opc-security": "Security",
+ "opc-identity": "Identity",
+ "opc-keystore": "Keystore",
+ "opc-type": "Type",
+ "opc-keystore-type": "Type",
+ "opc-keystore-location": "Location *",
+ "opc-keystore-password": "Password",
+ "opc-keystore-alias": "Alias",
+ "opc-keystore-key-password": "Key password",
+ "opc-device-node-pattern": "Device node pattern",
+ "opc-device-name-pattern": "Device name pattern",
+ "modbus-server": "Servers/slaves",
+ "modbus-add-server": "Add server/slave",
+ "modbus-add-server-prompt": "Please add server/slave",
+ "modbus-transport": "Transport",
+ "modbus-port-name": "Serial port name",
+ "modbus-encoding": "Encoding",
+ "modbus-parity": "Parity",
+ "modbus-baudrate": "Baud rate",
+ "modbus-databits": "Data bits",
+ "modbus-stopbits": "Stop bits",
+ "modbus-databits-range": "Data bits should be in a range from 7 to 8.",
+ "modbus-stopbits-range": "Stop bits should be in a range from 1 to 2.",
+ "modbus-unit-id": "Unit ID",
+ "modbus-unit-id-range": "Unit ID should be in a range from 1 to 247.",
+ "modbus-device-name": "Device name",
+ "modbus-poll-period": "Poll period (ms)",
+ "modbus-attributes-poll-period": "Attributes poll period (ms)",
+ "modbus-timeseries-poll-period": "Timeseries poll period (ms)",
+ "modbus-poll-period-range": "Poll period should be positive value.",
+ "modbus-tag": "Tag",
+ "modbus-function": "Function",
+ "modbus-register-address": "Register address",
+ "modbus-register-address-range": "Register address should be in a range from 0 to 65535.",
+ "modbus-register-bit-index": "Bit index",
+ "modbus-register-bit-index-range": "Bit index should be in a range from 0 to 15.",
+ "modbus-register-count": "Register count",
+ "modbus-register-count-range": "Register count should be a positive value.",
+ "modbus-byte-order": "Byte order",
+
+ "sync": {
+ "status": "Status",
+ "sync": "Sync",
+ "not-sync": "Not sync",
+ "last-sync-time": "Last sync time",
+ "not-available": "Not available"
+ },
+
+ "export-extensions-configuration": "Export extensions configuration",
+ "import-extensions-configuration": "Import extensions configuration",
+ "import-extensions": "Import extensions",
+ "import-extension": "Import extension",
+ "export-extension": "Export extension",
+ "file": "Extensions file",
+ "invalid-file-error": "Invalid extension file"
+ },
"fullscreen": {
"expand": "Во весь экран",
"exit": "Выйти из полноэкранного режима",
@@ -469,7 +950,24 @@ export default function addLocaleRussian(locales) {
},
"js-func": {
"no-return-error": "Функция должна возвращать значение!",
- "return-type-mismatch": "Функция должна возвращать значение типа '{{type}}'!"
+ "return-type-mismatch": "Функция должна возвращать значение типа '{{type}}'!",
+ "tidy": "Tidy" // TODO
+ },
+ "key-val": { // TODO
+ "key": "Key",
+ "value": "Value",
+ "remove-entry": "Remove entry",
+ "add-entry": "Add entry",
+ "no-data": "No entries"
+ },
+ "layout": { // TODO
+ "layout": "Layout",
+ "manage": "Manage layouts",
+ "settings": "Layout settings",
+ "color": "Color",
+ "main": "Main",
+ "right": "Right",
+ "select": "Select target layout"
},
"legend": {
"position": "Расположение легенды",
@@ -500,45 +998,6 @@ export default function addLocaleRussian(locales) {
"password-link-sent-message": "Ссылка для сброса пароля была успешно отправлена!",
"email": "Эл. адрес"
},
- "plugin": {
- "plugins": "Плагины",
- "delete": "Удалить плагин",
- "activate": "Активировать плагин",
- "suspend": "Приостановить плагин",
- "active": "Активный",
- "suspended": "Приостановлен",
- "name": "Название",
- "name-required": "Название обязательно.",
- "description": "Описание",
- "add": "Добавить плагин",
- "delete-plugin-title": "Вы точно хотите удалить плагин '{{pluginName}}'?",
- "delete-plugin-text": "Внимание, после подтверждения плагин и все связанные с ним данные будут безвозвратно утеряны.",
- "delete-plugins-title": "Вы точно хотите удалить { count, plural, one {1 плагин} few {# плагина} other {# плагинов} }?",
- "delete-plugins-action-title": "Удалить { count, plural, one {1 плагин} few {# плагина} other {# плагинов} } }",
- "delete-plugins-text": "Внимание, после подтверждения выбранные плагины и все связанные с ними данные будут безвозвратно утеряны.",
- "add-plugin-text": "Добавить новый плагин",
- "no-plugins-text": "Плагины не найдены",
- "plugin-details": "Подробности о плагине",
- "api-token": "API токен",
- "api-token-required": "API токен обязателен.",
- "type": "Тип плагина",
- "type-required": "Тип плагина обязателен.",
- "configuration": "Настройки плагина",
- "system": "Системный",
- "select-plugin": "Выберите плагин",
- "plugin": "Плагин",
- "no-plugins-matching": "Плагин '{{entity}}' не найден.",
- "plugin-required": "Плагин обязателен.",
- "plugin-require-match": "Пожалуйста, выберите существующий плагин.",
- "events": "События",
- "details": "Подробности",
- "import": "Импортировать плагин",
- "export": "Экспортировать плагин",
- "export-failed-error": "Не удалось экспортировать плагин: {{error}}",
- "create-new-plugin": "Создать новый плагин",
- "plugin-file": "Файл плагина",
- "invalid-plugin-file-error": "Не удалось импортировать плагин: неизвестная схема данных плагина."
- },
"position": {
"top": "Верх",
"bottom": "Низ",
@@ -550,60 +1009,139 @@ export default function addLocaleRussian(locales) {
"change-password": "Изменить пароль",
"current-password": "Текущий пароль"
},
- "rule": {
- "rules": "Правила",
- "delete": "Удалить правило",
- "activate": "Активировать правило",
- "suspend": "Приостановить правило",
- "active": "Активное",
- "suspended": "Приостановлены",
- "name": "Название",
- "name-required": "Название обязательно.",
- "description": "Описание",
- "add": "Добавить правило",
- "delete-rule-title": "Вы точно хотите удалить правило '{{ruleName}}'?",
- "delete-rule-text": "Внимание, после подтверждения правило и все связанные с ним данные будут безвозвратно утеряны.",
- "delete-rules-title": "Вы точно хотите удалить { count, plural, one {1 правило} few {# правила} other {# правил} }?",
- "delete-rules-action-title": "Удалить { count, plural, one {1 правило} few {# правила} other {# правил} }",
- "delete-rules-text": "Внимание, после подтверждения выбранные правила и все связанные с ними данные будут безвозвратно утеряны.",
- "add-rule-text": "Добавить новое правило",
- "no-rules-text": "Правила не найдены",
- "rule-details": "Подробности о правиле",
- "filters": "Фильтры",
- "filter": "Фильтр",
- "add-filter-prompt": "Пожалуйста, добавьте фильтр",
- "remove-filter": "Удалить фильтр",
- "add-filter": "Добавить фильтр",
- "filter-name": "Название фильтра",
- "filter-type": "Тип фильтра",
- "edit-filter": "Редактировать фильтр",
- "view-filter": "Просмотреть фильтр",
- "component-name": "Название",
- "component-name-required": "Название обязательно.",
- "component-type": "Тип",
- "component-type-required": "Тип обязателен.",
- "processor": "Обработчик",
- "no-processor-configured": "Обработчики не сконфигурированы",
- "create-processor": "Создать обработчик",
- "processor-name": "Название обработчика",
- "processor-type": "Тип обработчика",
- "plugin-action": "Действие плагина",
- "action-name": "Название действия",
- "action-type": "Тип действия",
- "create-action-prompt": "Пожалуйста, создайте действие",
- "create-action": "Создать действие",
- "details": "Подробности",
- "events": "События",
- "system": "Системное",
- "import": "Импортировать правило",
- "export": "Экспортировать правило",
- "export-failed-error": "Не удалось экспортировать правило: {{error}}",
- "create-new-rule": "Создать новое правило",
- "rule-file": "Файл правила",
- "invalid-rule-file-error": "Не удалось импортировать правило: неизвестная схема данных правила."
+ "relation": { // TODO
+ "relations": "Relations",
+ "direction": "Direction",
+ "search-direction": {
+ "FROM": "From",
+ "TO": "To"
+ },
+ "direction-type": {
+ "FROM": "from",
+ "TO": "to"
+ },
+ "from-relations": "Outbound relations",
+ "to-relations": "Inbound relations",
+ "selected-relations": "{ count, select, 1 {1 relation} other {# relations} } selected",
+ "type": "Type",
+ "to-entity-type": "To entity type",
+ "to-entity-name": "To entity name",
+ "from-entity-type": "From entity type",
+ "from-entity-name": "From entity name",
+ "to-entity": "To entity",
+ "from-entity": "From entity",
+ "delete": "Delete relation",
+ "relation-type": "Relation type",
+ "relation-type-required": "Relation type is required.",
+ "any-relation-type": "Any type",
+ "add": "Add relation",
+ "edit": "Edit relation",
+ "delete-to-relation-title": "Are you sure you want to delete relation to the entity '{{entityName}}'?",
+ "delete-to-relation-text": "Be careful, after the confirmation the entity '{{entityName}}' will be unrelated from the current entity.",
+ "delete-to-relations-title": "Are you sure you want to delete { count, select, 1 {1 relation} other {# relations} }?",
+ "delete-to-relations-text": "Be careful, after the confirmation all selected relations will be removed and corresponding entities will be unrelated from the current entity.",
+ "delete-from-relation-title": "Are you sure you want to delete relation from the entity '{{entityName}}'?",
+ "delete-from-relation-text": "Be careful, after the confirmation current entity will be unrelated from the entity '{{entityName}}'.",
+ "delete-from-relations-title": "Are you sure you want to delete { count, select, 1 {1 relation} other {# relations} }?",
+ "delete-from-relations-text": "Be careful, after the confirmation all selected relations will be removed and current entity will be unrelated from the corresponding entities.",
+ "remove-relation-filter": "Remove relation filter",
+ "add-relation-filter": "Add relation filter",
+ "any-relation": "Any relation",
+ "relation-filters": "Relation filters",
+ "additional-info": "Additional info (JSON)",
+ "invalid-additional-info": "Unable to parse additional info json."
+ },
+ "rulechain": { // TODO
+ "rulechain": "Rule chain",
+ "rulechains": "Rule chains",
+ "root": "Root",
+ "delete": "Delete rule chain",
+ "name": "Name",
+ "name-required": "Name is required.",
+ "description": "Description",
+ "add": "Add Rule Chain",
+ "set-root": "Make rule chain root",
+ "set-root-rulechain-title": "Are you sure you want to make the rule chain '{{ruleChainName}}' root?",
+ "set-root-rulechain-text": "After the confirmation the rule chain will become root and will handle all incoming transport messages.",
+ "delete-rulechain-title": "Are you sure you want to delete the rule chain '{{ruleChainName}}'?",
+ "delete-rulechain-text": "Be careful, after the confirmation the rule chain and all related data will become unrecoverable.",
+ "delete-rulechains-title": "Are you sure you want to delete { count, select, 1 {1 rule chain} other {# rule chains} }?",
+ "delete-rulechains-action-title": "Delete { count, select, 1 {1 rule chain} other {# rule chains} }",
+ "delete-rulechains-text": "Be careful, after the confirmation all selected rule chains will be removed and all related data will become unrecoverable.",
+ "add-rulechain-text": "Add new rule chain",
+ "no-rulechains-text": "No rule chains found",
+ "rulechain-details": "Rule chain details",
+ "details": "Details",
+ "events": "Events",
+ "system": "System",
+ "import": "Import rule chain",
+ "export": "Export rule chain",
+ "export-failed-error": "Unable to export rule chain: {{error}}",
+ "create-new-rulechain": "Create new rule chain",
+ "rulechain-file": "Rule chain file",
+ "invalid-rulechain-file-error": "Unable to import rule chain: Invalid rule chain data structure.",
+ "copyId": "Copy rule chain Id",
+ "idCopiedMessage": "Rule chain Id has been copied to clipboard",
+ "select-rulechain": "Select rule chain",
+ "no-rulechains-matching": "No rule chains matching '{{entity}}' were found.",
+ "rulechain-required": "Rule chain is required",
+ "management": "Rules management",
+ "debug-mode": "Debug mode"
},
- "rule-plugin": {
- "management": "Управление плагинами и правилами"
+ "rulenode": { // TODO
+ "details": "Details",
+ "events": "Events",
+ "search": "Search nodes",
+ "open-node-library": "Open node library",
+ "add": "Add rule node",
+ "name": "Name",
+ "name-required": "Name is required.",
+ "type": "Type",
+ "description": "Description",
+ "delete": "Delete rule node",
+ "select-all-objects": "Select all nodes and connections",
+ "deselect-all-objects": "Deselect all nodes and connections",
+ "delete-selected-objects": "Delete selected nodes and connections",
+ "delete-selected": "Delete selected",
+ "select-all": "Select all",
+ "copy-selected": "Copy selected",
+ "deselect-all": "Deselect all",
+ "rulenode-details": "Rule node details",
+ "debug-mode": "Debug mode",
+ "configuration": "Configuration",
+ "link": "Link",
+ "link-details": "Rule node link details",
+ "add-link": "Add link",
+ "link-label": "Link label",
+ "link-label-required": "Link label is required.",
+ "custom-link-label": "Custom link label",
+ "custom-link-label-required": "Custom link label is required.",
+ "type-filter": "Filter",
+ "type-filter-details": "Filter incoming messages with configured conditions",
+ "type-enrichment": "Enrichment",
+ "type-enrichment-details": "Add additional information into Message Metadata",
+ "type-transformation": "Transformation",
+ "type-transformation-details": "Change Message payload and Metadata",
+ "type-action": "Action",
+ "type-action-details": "Perform special action",
+ "type-external": "External",
+ "type-external-details": "Interacts with external system",
+ "type-rule-chain": "Rule Chain",
+ "type-rule-chain-details": "Forwards incoming messages to specified Rule Chain",
+ "type-input": "Input",
+ "type-input-details": "Logical input of Rule Chain, forwards incoming messages to next related Rule Node",
+ "directive-is-not-loaded": "Defined configuration directive '{{directiveName}}' is not available.",
+ "ui-resources-load-error": "Failed to load configuration ui resources.",
+ "invalid-target-rulechain": "Unable to resolve target rule chain!",
+ "test-script-function": "Test script function",
+ "message": "Message",
+ "message-type": "Message type",
+ "message-type-required": "Message type is required",
+ "metadata": "Metadata",
+ "metadata-required": "Metadata entries can't be empty.",
+ "output": "Output",
+ "test": "Test",
+ "help": "Help"
},
"tenant": {
"tenants": "Владельцы",
@@ -740,6 +1278,18 @@ export default function addLocaleRussian(locales) {
"undo": "Откатить изменения в виджете",
"export": "Экспортировать виджет"
},
+ "widget-action": { // TODO
+ "header-button": "Widget header button",
+ "open-dashboard-state": "Navigate to new dashboard state",
+ "update-dashboard-state": "Update current dashboard state",
+ "open-dashboard": "Navigate to other dashboard",
+ "custom": "Custom action",
+ "target-dashboard-state": "Target dashboard state",
+ "target-dashboard-state-required": "Target dashboard state is required",
+ "set-entity-from-widget": "Set entity from widget",
+ "target-dashboard": "Target dashboard",
+ "open-right-layout": "Open right dashboard layout (mobile view)"
+ },
"widgets-bundle": {
"current": "Текущий набор",
"widgets-bundles": "Наборы виджетов",
@@ -803,6 +1353,20 @@ export default function addLocaleRussian(locales) {
"widget-type-file": "Файл типа виджета",
"invalid-widget-type-file-error": "Не удалось импортировать виджет: неизвестная схема данных типа виджета."
},
+ "icon": { // TODO
+ "icon": "Icon",
+ "select-icon": "Select icon",
+ "material-icons": "Material icons",
+ "show-all": "Show all icons"
+ },
+ "custom": { // TODO
+ "widget-action": {
+ "action-cell-button": "Action cell button",
+ "row-click": "On row click",
+ "marker-click": "On marker click",
+ "tooltip-tag-action": "Tooltip tag action"
+ }
+ },
"language": {
"language": "Язык",
"en_US": "Английский",
@@ -813,5 +1377,5 @@ export default function addLocaleRussian(locales) {
}
};
- angular.extend(locales, {'ru_RU': ru_RU});
+ angular.extend(locales, { 'ru_RU': ru_RU });
}
\ No newline at end of file
ui/src/app/locale/locale.constant-zh.js 370(+269 -101)
diff --git a/ui/src/app/locale/locale.constant-zh.js b/ui/src/app/locale/locale.constant-zh.js
index c25291a..9a08ca1 100644
--- a/ui/src/app/locale/locale.constant-zh.js
+++ b/ui/src/app/locale/locale.constant-zh.js
@@ -336,6 +336,11 @@ export default function addLocaleChinese(locales) {
"enter-password": "输入密码",
"enter-search": "输入检索条件"
},
+ "content-type": { // TODO
+ "json": "Json",
+ "text": "Text",
+ "binary": "Binary (Base64)"
+ },
"customer": {
"customer": "客户",
"customers": "客户",
@@ -754,6 +759,163 @@ export default function addLocaleChinese(locales) {
"messages-processed": "消息处理",
"errors-occurred": "错误发生"
},
+ "extension": { // TODO
+ "extensions": "Extensions",
+ "selected-extensions": "{ count, select, 1 {1 extension} other {# extensions} } selected",
+ "type": "Type",
+ "key": "Key",
+ "value": "Value",
+ "id": "Id",
+ "extension-id": "Extension id",
+ "extension-type": "Extension type",
+ "transformer-json": "JSON *",
+ "unique-id-required": "Current extension id already exists.",
+ "delete": "Delete extension",
+ "add": "Add extension",
+ "edit": "Edit extension",
+ "delete-extension-title": "Are you sure you want to delete the extension '{{extensionId}}'?",
+ "delete-extension-text": "Be careful, after the confirmation the extension and all related data will become unrecoverable.",
+ "delete-extensions-title": "Are you sure you want to delete { count, select, 1 {1 extension} other {# extensions} }?",
+ "delete-extensions-text": "Be careful, after the confirmation all selected extensions will be removed.",
+ "converters": "Converters",
+ "converter-id": "Converter id",
+ "configuration": "Configuration",
+ "converter-configurations": "Converter configurations",
+ "token": "Security token",
+ "add-converter": "Add converter",
+ "add-config": "Add converter configuration",
+ "device-name-expression": "Device name expression",
+ "device-type-expression": "Device type expression",
+ "custom": "Custom",
+ "to-double": "To Double",
+ "transformer": "Transformer",
+ "json-required": "Transformer json is required.",
+ "json-parse": "Unable to parse transformer json.",
+ "attributes": "Attributes",
+ "add-attribute": "Add attribute",
+ "add-map": "Add mapping element",
+ "timeseries": "Timeseries",
+ "add-timeseries": "Add timeseries",
+ "field-required": "Field is required",
+ "brokers": "Brokers",
+ "add-broker": "Add broker",
+ "host": "Host",
+ "port": "Port",
+ "port-range": "Port should be in a range from 1 to 65535.",
+ "ssl": "Ssl",
+ "credentials": "Credentials",
+ "username": "Username",
+ "password": "Password",
+ "retry-interval": "Retry interval in milliseconds",
+ "anonymous": "Anonymous",
+ "basic": "Basic",
+ "pem": "PEM",
+ "ca-cert": "CA certificate file *",
+ "private-key": "Private key file *",
+ "cert": "Certificate file *",
+ "no-file": "No file selected.",
+ "drop-file": "Drop a file or click to select a file to upload.",
+ "mapping": "Mapping",
+ "topic-filter": "Topic filter",
+ "converter-type": "Converter type",
+ "converter-json": "Json",
+ "json-name-expression": "Device name json expression",
+ "topic-name-expression": "Device name topic expression",
+ "json-type-expression": "Device type json expression",
+ "topic-type-expression": "Device type topic expression",
+ "attribute-key-expression": "Attribute key expression",
+ "attr-json-key-expression": "Attribute key json expression",
+ "attr-topic-key-expression": "Attribute key topic expression",
+ "request-id-expression": "Request id expression",
+ "request-id-json-expression": "Request id json expression",
+ "request-id-topic-expression": "Request id topic expression",
+ "response-topic-expression": "Response topic expression",
+ "value-expression": "Value expression",
+ "topic": "Topic",
+ "timeout": "Timeout in milliseconds",
+ "converter-json-required": "Converter json is required.",
+ "converter-json-parse": "Unable to parse converter json.",
+ "filter-expression": "Filter expression",
+ "connect-requests": "Connect requests",
+ "add-connect-request": "Add connect request",
+ "disconnect-requests": "Disconnect requests",
+ "add-disconnect-request": "Add disconnect request",
+ "attribute-requests": "Attribute requests",
+ "add-attribute-request": "Add attribute request",
+ "attribute-updates": "Attribute updates",
+ "add-attribute-update": "Add attribute update",
+ "server-side-rpc": "Server side RPC",
+ "add-server-side-rpc-request": "Add server-side RPC request",
+ "device-name-filter": "Device name filter",
+ "attribute-filter": "Attribute filter",
+ "method-filter": "Method filter",
+ "request-topic-expression": "Request topic expression",
+ "response-timeout": "Response timeout in milliseconds",
+ "topic-expression": "Topic expression",
+ "client-scope": "Client scope",
+ "add-device": "Add device",
+ "opc-server": "Servers",
+ "opc-add-server": "Add server",
+ "opc-add-server-prompt": "Please add server",
+ "opc-application-name": "Application name",
+ "opc-application-uri": "Application uri",
+ "opc-scan-period-in-seconds": "Scan period in seconds",
+ "opc-security": "Security",
+ "opc-identity": "Identity",
+ "opc-keystore": "Keystore",
+ "opc-type": "Type",
+ "opc-keystore-type": "Type",
+ "opc-keystore-location": "Location *",
+ "opc-keystore-password": "Password",
+ "opc-keystore-alias": "Alias",
+ "opc-keystore-key-password": "Key password",
+ "opc-device-node-pattern": "Device node pattern",
+ "opc-device-name-pattern": "Device name pattern",
+ "modbus-server": "Servers/slaves",
+ "modbus-add-server": "Add server/slave",
+ "modbus-add-server-prompt": "Please add server/slave",
+ "modbus-transport": "Transport",
+ "modbus-port-name": "Serial port name",
+ "modbus-encoding": "Encoding",
+ "modbus-parity": "Parity",
+ "modbus-baudrate": "Baud rate",
+ "modbus-databits": "Data bits",
+ "modbus-stopbits": "Stop bits",
+ "modbus-databits-range": "Data bits should be in a range from 7 to 8.",
+ "modbus-stopbits-range": "Stop bits should be in a range from 1 to 2.",
+ "modbus-unit-id": "Unit ID",
+ "modbus-unit-id-range": "Unit ID should be in a range from 1 to 247.",
+ "modbus-device-name": "Device name",
+ "modbus-poll-period": "Poll period (ms)",
+ "modbus-attributes-poll-period": "Attributes poll period (ms)",
+ "modbus-timeseries-poll-period": "Timeseries poll period (ms)",
+ "modbus-poll-period-range": "Poll period should be positive value.",
+ "modbus-tag": "Tag",
+ "modbus-function": "Function",
+ "modbus-register-address": "Register address",
+ "modbus-register-address-range": "Register address should be in a range from 0 to 65535.",
+ "modbus-register-bit-index": "Bit index",
+ "modbus-register-bit-index-range": "Bit index should be in a range from 0 to 15.",
+ "modbus-register-count": "Register count",
+ "modbus-register-count-range": "Register count should be a positive value.",
+ "modbus-byte-order": "Byte order",
+
+ "sync": {
+ "status": "Status",
+ "sync": "Sync",
+ "not-sync": "Not sync",
+ "last-sync-time": "Last sync time",
+ "not-available": "Not available"
+ },
+
+ "export-extensions-configuration": "Export extensions configuration",
+ "import-extensions-configuration": "Import extensions configuration",
+ "import-extensions": "Import extensions",
+ "import-extension": "Import extension",
+ "export-extension": "Export extension",
+ "file": "Extensions file",
+ "invalid-file-error": "Invalid extension file"
+ },
"fullscreen": {
"expand": "展开到全屏",
"exit": "退出全屏",
@@ -798,6 +960,13 @@ export default function addLocaleChinese(locales) {
"no-return-error": "函数必须返回值!",
"return-type-mismatch": "函数必须返回 '{{type}}' 类型的值!"
},
+ "key-val": { // TODO
+ "key": "Key",
+ "value": "Value",
+ "remove-entry": "Remove entry",
+ "add-entry": "Add entry",
+ "no-data": "No entries"
+ },
"layout": {
"layout": "布局",
"manage": "布局管理",
@@ -836,47 +1005,6 @@ export default function addLocaleChinese(locales) {
"password-link-sent-message": "密码重置链接已成功发送!",
"email": "电子邮件"
},
- "plugin": {
- "plugins": "插件",
- "delete": "删除插件",
- "activate": "激活插件",
- "suspend": "暂停插件",
- "active": "激活",
- "suspended": "暂停",
- "name": "名称",
- "name-required": "名称必填。",
- "description": "描述",
- "add": "添加插件",
- "delete-plugin-title": "你确定要删除插件 '{{pluginName}}' 吗?",
- "delete-plugin-text": "小心!确认后,插件和所有相关数据将不可恢复。",
- "delete-plugins-title": "你确定你要删除 { count, select, 1 {1 插件} other {# 插件} } 吗?",
- "delete-plugins-action-title": "删除 { count, select, 1 {1 插件} other {# 插件} }",
- "delete-plugins-text": "小心!确认后,所有选定的插件将被删除,所有相关数据将不可恢复。",
- "add-plugin-text": "添加新的插件",
- "no-plugins-text": "没有找到插件",
- "plugin-details": "插件详细信息",
- "api-token": "API令牌",
- "api-token-required": "API令牌必填。",
- "type": "插件类型",
- "type-required": "插件类型必填。",
- "configuration": "插件配置",
- "system": "系统",
- "select-plugin": "选择插件",
- "plugin": "插件",
- "no-plugins-matching": "没有找到匹配'{{entity}}'的插件。",
- "plugin-required": "插件必填。",
- "plugin-require-match": "请选择一个现有的插件。",
- "events": "事件",
- "details": "详情",
- "import": "导入插件",
- "export": "导出插件",
- "export-failed-error": "无法导出插件:{{error}}",
- "create-new-plugin": "创建新的插件",
- "plugin-file": "插件文件",
- "invalid-plugin-file-error": "无法导入插件:插件数据结构无效。",
- "copyId": "复制插件ID",
- "idCopiedMessage": "插件ID已经复制到粘贴板"
- },
"position": {
"top": "顶部",
"bottom": "底部",
@@ -930,66 +1058,97 @@ export default function addLocaleChinese(locales) {
"additional-info": "附加信息 (JSON)",
"invalid-additional-info": "无法解析附加信息json。"
},
- "rule": {
- "rule": "规则",
- "rules": "规则",
- "delete": "删除规则",
- "activate": "激活规则",
- "suspend": "暂停规则",
- "active": "激活",
- "suspended": "暂停",
- "name": "名称",
- "name-required": "名称必填。",
- "description": "描述",
- "add": "添加规则",
- "delete-rule-title": "您确定要删除规则'{{ruleName}}'吗?",
- "delete-rule-text": "小心!确认后,规则和所有相关数据将不可恢复。",
- "delete-rules-title": "你确定要删除 {count, select, 1 {1 规则} other {# 规则}} 吗?",
- "delete-rules-action-title": "删除 { count, select, 1 {1 规则} other {# 规则} }",
- "delete-rules-text": "小心!确认后,所有选定的规则将被删除,所有相关数据将不可恢复。",
- "add-rule-text": "添加新规则",
- "no-rules-text": "没有找到规则",
- "rule-details": "规则详情",
- "filters": "过滤器",
- "filter": "过滤器",
- "add-filter-prompt": "请添加过滤器",
- "remove-filter": "删除过滤器",
- "add-filter": "添加过滤器",
- "filter-name": "过滤器名称",
- "filter-type": "过滤器类型",
- "edit-filter": "编辑过滤器",
- "view-filter": "查看过滤器",
- "component-name": "名称",
- "component-name-required": "名称必填。",
- "component-type": "类型",
- "component-type-required": "类型必填。",
- "processor": "处理器",
- "no-processor-configured": "未配置处理器",
- "create-processor": "创建处理器",
- "processor-name": "处理器名称",
- "processor-type": "处理器类型",
- "plugin-action": "插件动作",
- "action-name": "动作名称",
- "action-type": "动作类型",
- "create-action-prompt": "请创建动作",
- "create-action": "创建动作",
- "details": "详情",
- "events": "事件",
- "system": "系统",
- "import": "导入规则",
- "export": "导出规则",
- "export-failed-error": "无法导出规则:{{error}}",
- "create-new-rule": "创建新规则",
- "rule-file": "规则文件",
- "invalid-rule-file-error": "无法导入规则:规则数据结构无效。",
- "copyId": "Copy rule Id",
- "idCopiedMessage": "规则ID已经复制到粘贴板",
- "select-rule": "选择规则",
- "no-rules-matching": "没有找到符合 '{{entity}}' 的规则。",
- "rule-required": "规则必填"
+ "rulechain": { // TODO
+ "rulechain": "Rule chain",
+ "rulechains": "Rule chains",
+ "root": "Root",
+ "delete": "Delete rule chain",
+ "name": "Name",
+ "name-required": "Name is required.",
+ "description": "Description",
+ "add": "Add Rule Chain",
+ "set-root": "Make rule chain root",
+ "set-root-rulechain-title": "Are you sure you want to make the rule chain '{{ruleChainName}}' root?",
+ "set-root-rulechain-text": "After the confirmation the rule chain will become root and will handle all incoming transport messages.",
+ "delete-rulechain-title": "Are you sure you want to delete the rule chain '{{ruleChainName}}'?",
+ "delete-rulechain-text": "Be careful, after the confirmation the rule chain and all related data will become unrecoverable.",
+ "delete-rulechains-title": "Are you sure you want to delete { count, select, 1 {1 rule chain} other {# rule chains} }?",
+ "delete-rulechains-action-title": "Delete { count, select, 1 {1 rule chain} other {# rule chains} }",
+ "delete-rulechains-text": "Be careful, after the confirmation all selected rule chains will be removed and all related data will become unrecoverable.",
+ "add-rulechain-text": "Add new rule chain",
+ "no-rulechains-text": "No rule chains found",
+ "rulechain-details": "Rule chain details",
+ "details": "Details",
+ "events": "Events",
+ "system": "System",
+ "import": "Import rule chain",
+ "export": "Export rule chain",
+ "export-failed-error": "Unable to export rule chain: {{error}}",
+ "create-new-rulechain": "Create new rule chain",
+ "rulechain-file": "Rule chain file",
+ "invalid-rulechain-file-error": "Unable to import rule chain: Invalid rule chain data structure.",
+ "copyId": "Copy rule chain Id",
+ "idCopiedMessage": "Rule chain Id has been copied to clipboard",
+ "select-rulechain": "Select rule chain",
+ "no-rulechains-matching": "No rule chains matching '{{entity}}' were found.",
+ "rulechain-required": "Rule chain is required",
+ "management": "Rules management",
+ "debug-mode": "Debug mode"
},
- "rule-plugin": {
- "management": "规则和插件管理"
+ "rulenode": { // TODO
+ "details": "Details",
+ "events": "Events",
+ "search": "Search nodes",
+ "open-node-library": "Open node library",
+ "add": "Add rule node",
+ "name": "Name",
+ "name-required": "Name is required.",
+ "type": "Type",
+ "description": "Description",
+ "delete": "Delete rule node",
+ "select-all-objects": "Select all nodes and connections",
+ "deselect-all-objects": "Deselect all nodes and connections",
+ "delete-selected-objects": "Delete selected nodes and connections",
+ "delete-selected": "Delete selected",
+ "select-all": "Select all",
+ "copy-selected": "Copy selected",
+ "deselect-all": "Deselect all",
+ "rulenode-details": "Rule node details",
+ "debug-mode": "Debug mode",
+ "configuration": "Configuration",
+ "link": "Link",
+ "link-details": "Rule node link details",
+ "add-link": "Add link",
+ "link-label": "Link label",
+ "link-label-required": "Link label is required.",
+ "custom-link-label": "Custom link label",
+ "custom-link-label-required": "Custom link label is required.",
+ "type-filter": "Filter",
+ "type-filter-details": "Filter incoming messages with configured conditions",
+ "type-enrichment": "Enrichment",
+ "type-enrichment-details": "Add additional information into Message Metadata",
+ "type-transformation": "Transformation",
+ "type-transformation-details": "Change Message payload and Metadata",
+ "type-action": "Action",
+ "type-action-details": "Perform special action",
+ "type-external": "External",
+ "type-external-details": "Interacts with external system",
+ "type-rule-chain": "Rule Chain",
+ "type-rule-chain-details": "Forwards incoming messages to specified Rule Chain",
+ "type-input": "Input",
+ "type-input-details": "Logical input of Rule Chain, forwards incoming messages to next related Rule Node",
+ "directive-is-not-loaded": "Defined configuration directive '{{directiveName}}' is not available.",
+ "ui-resources-load-error": "Failed to load configuration ui resources.",
+ "invalid-target-rulechain": "Unable to resolve target rule chain!",
+ "test-script-function": "Test script function",
+ "message": "Message",
+ "message-type": "Message type",
+ "message-type-required": "Message type is required",
+ "metadata": "Metadata",
+ "metadata-required": "Metadata entries can't be empty.",
+ "output": "Output",
+ "test": "Test",
+ "help": "Help"
},
"tenant": {
"tenant": "租户",
@@ -1081,7 +1240,8 @@ export default function addLocaleChinese(locales) {
"activation-link": "用户激活链接",
"activation-link-text": "使用该链接 <a href='{{activationLink}}' target='_blank'>激活</a> 激活用户:",
"copy-activation-link": "复制用户激活链接",
- "activation-link-copied-message": "用户激活链接已经复制到粘贴板"
+ "activation-link-copied-message": "用户激活链接已经复制到粘贴板",
+ "details": "Details" // TODO
},
"value": {
"type": "值类型",
@@ -1248,6 +1408,14 @@ export default function addLocaleChinese(locales) {
"material-icons": "素材图标",
"show-all": "显示所有图标"
},
+ "custom": { // TODO
+ "widget-action": {
+ "action-cell-button": "Action cell button",
+ "row-click": "On row click",
+ "marker-click": "On marker click",
+ "tooltip-tag-action": "Tooltip tag action"
+ }
+ },
"language": {
"language": "语言",
"en_US": "英语",