diff --git a/application/pom.xml b/application/pom.xml index 3f2ffa353c..45b35cf172 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.0.1 + 2.0.2 thingsboard application 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 85b32857f1..9527171624 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> clientAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.CLIENT_SCOPE, request.getClientAttributeNames()); - ListenableFuture> sharedAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.SHARED_SCOPE, request.getClientAttributeNames()); + ListenableFuture> sharedAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.SHARED_SCOPE, request.getSharedAttributeNames()); Futures.addCallback(Futures.allAsList(Arrays.asList(clientAttributesFuture, sharedAttributesFuture)), new FutureCallback>>() { @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 3507f244e9..fac5d97175 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 731d8d8e6c..1e607516e1 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 6002b0e67e..c3ffbabbea 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 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 4d2adeaad1..f4ff92cf23 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 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 b3723682e2..5daebcc3c5 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() { @Override - protected List findEntities(String region, TextPageLink pageLink) { - return tenantService.findTenants(pageLink).getData(); + protected TextPageData findEntities(String region, TextPageLink pageLink) { + return tenantService.findTenants(pageLink); } @Override @@ -76,7 +75,7 @@ public class DefaultDataUpdateService implements DataUpdateService { } }; - public abstract class PaginatedUpdater> { + public abstract class PaginatedUpdater> { 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 entities = findEntities(id, pageLink); - for (D entity : entities) { + TextPageData 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 findEntities(I id, TextPageLink pageLink); + protected abstract TextPageData 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 29d5c65be9..3a4a837217 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 45b969669c..a41fdee250 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 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 tenants = tenantService.findTenants(new TextPageLink(Integer.MAX_VALUE)).getData(); + for (Tenant tenant : tenants) { + List> fetchFutures = new ArrayList<>(); + List 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 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> fetchFutures = new ArrayList<>(); List 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() { - @Override - public void onSuccess(@Nullable DeviceStateData state) { - addDeviceUsingState(state); - } + Optional address = routingService.resolveById(device.getId()); + if (!address.isPresent()) { + Futures.addCallback(fetchDeviceState(device), new FutureCallback() { + @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 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 deviceIds = tenantDevices.get(tenantId); - if (deviceIds != null) { - deviceIds.remove(deviceId); - if (deviceIds.isEmpty()) { - tenantDevices.remove(tenantId); + Optional address = routingService.resolveById(deviceId); + if (!address.isPresent()) { + deviceStates.remove(deviceId); + Set 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 37f785cf44..dab9e69f6e 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 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 0942b4010a..dbd58d540c 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)); } }, diff --git a/application/src/main/proto/cluster.proto b/application/src/main/proto/cluster.proto index ac96010efe..21c963b15e 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 4fa61621bc..5e6896b2b7 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()); } diff --git a/common/data/pom.xml b/common/data/pom.xml index dac9ece1a8..d2a44e2666 100644 --- a/common/data/pom.xml +++ b/common/data/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.0.1 + 2.0.2 common org.thingsboard.common 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 ff908fc0ae..7b74e6283f 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 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 { diff --git a/common/message/pom.xml b/common/message/pom.xml index 9944fe06a8..3863443f7a 100644 --- a/common/message/pom.xml +++ b/common/message/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.0.1 + 2.0.2 common org.thingsboard.common diff --git a/common/pom.xml b/common/pom.xml index e1defb9fa0..00711eda08 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.0.1 + 2.0.2 thingsboard org.thingsboard diff --git a/common/transport/pom.xml b/common/transport/pom.xml index e87bb60f98..4cc9e8dc10 100644 --- a/common/transport/pom.xml +++ b/common/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.0.1 + 2.0.2 common org.thingsboard.common 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 ea77366f17..4cff643a5a 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); + } diff --git a/dao/pom.xml b/dao/pom.xml index da8acc9337..0a062ac449 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.0.1 + 2.0.2 thingsboard dao 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 fd619765d2..f4626fbfa2 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 diff --git a/docker/cassandra-setup/Makefile b/docker/cassandra-setup/Makefile index b90a1f1e44..9f0e211f95 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 diff --git a/docker/cassandra/Makefile b/docker/cassandra/Makefile index 1ac51fcccf..0447716ca5 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 diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index b7a715e73a..5219dcc996 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" diff --git a/docker/k8s/cassandra-setup.yaml b/docker/k8s/cassandra-setup.yaml index eefc148427..f0337f804e 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" diff --git a/docker/k8s/cassandra.yaml b/docker/k8s/cassandra.yaml index 04cafcb74a..ca5dfc0bee 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 diff --git a/docker/k8s/tb.yaml b/docker/k8s/tb.yaml index 947afb46e0..81b4ff17af 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 diff --git a/docker/k8s/zookeeper.yaml b/docker/k8s/zookeeper.yaml index 744c071797..e4648dd994 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 diff --git a/docker/tb/Makefile b/docker/tb/Makefile index 5e0fcf1c58..63d740101f 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 diff --git a/docker/zookeeper/Makefile b/docker/zookeeper/Makefile index 1d74b0dc37..6503c90a85 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 diff --git a/netty-mqtt/pom.xml b/netty-mqtt/pom.xml index 4fc662514a..67603ff6d5 100644 --- a/netty-mqtt/pom.xml +++ b/netty-mqtt/pom.xml @@ -19,12 +19,12 @@ 4.0.0 org.thingsboard - 2.0.1 + 2.0.2 thingsboard org.thingsboard netty-mqtt - 2.0.1 + 2.0.2 jar Netty MQTT Client 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 ef5e7a531d..c927c428e6 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 } 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 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 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 } }); 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 } 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 } 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 6563525783..37efca3278 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 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 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 d7f0a08409..9f86b8e6cd 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 391410525f..a5df846899 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 serverSubscribtions = new HashSet<>(); - private final IntObjectHashMap pendingServerUnsubscribes = new IntObjectHashMap<>(); + private final Set serverSubscriptions = new HashSet<>(); + private final IntObjectHashMap pendingServerUnsubscribes = new IntObjectHashMap<>(); private final IntObjectHashMap qos2PendingIncomingPublishes = new IntObjectHashMap<>(); private final IntObjectHashMap pendingPublishes = new IntObjectHashMap<>(); - private final HashMultimap subscriptions = HashMultimap.create(); - private final IntObjectHashMap pendingSubscribtions = new IntObjectHashMap<>(); + private final HashMultimap subscriptions = HashMultimap.create(); + private final IntObjectHashMap pendingSubscriptions = new IntObjectHashMap<>(); private final Set pendingSubscribeTopics = new HashSet<>(); - private final HashMultimap handlerToSubscribtion = HashMultimap.create(); + private final HashMultimap 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 connect(String host, int port) { + return connect(host, port, false); + } + + private Future connect(String host, int port, boolean reconnect) { if (this.eventLoop == null) { this.eventLoop = new NioEventLoopGroup(); } this.host = host; this.port = port; - Promise 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 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 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 off(String topic, MqttHandler handler) { Promise 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 off(String topic) { Promise future = new DefaultPromise<>(this.eventLoop.next()); - ImmutableSet subscribtions = ImmutableSet.copyOf(this.subscriptions.get(topic)); - for (MqttSubscribtion subscribtion : subscribtions) { - for (MqttSubscribtion handSub : this.handlerToSubscribtion.get(subscribtion.getHandler())) { + ImmutableSet 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 createSubscribtion(String topic, MqttHandler handler, boolean once, MqttQoS qos) { + private Future createSubscription(String topic, MqttHandler handler, boolean once, MqttQoS qos) { if (this.pendingSubscribeTopics.contains(topic)) { - Optional> 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> 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 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 getPendingSubscribtions() { - return pendingSubscribtions; + IntObjectHashMap getPendingSubscriptions() { + return pendingSubscriptions; } - HashMultimap getSubscriptions() { + HashMultimap getSubscriptions() { return subscriptions; } @@ -433,15 +471,15 @@ final class MqttClientImpl implements MqttClient { return pendingSubscribeTopics; } - HashMultimap getHandlerToSubscribtion() { + HashMultimap getHandlerToSubscribtion() { return handlerToSubscribtion; } - Set getServerSubscribtions() { - return serverSubscribtions; + Set getServerSubscriptions() { + return serverSubscriptions; } - IntObjectHashMap getPendingServerUnsubscribes() { + IntObjectHashMap 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; + } + } diff --git a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttPendingSubscribtion.java b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttPendingSubscription.java similarity index 96% rename from netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttPendingSubscribtion.java rename to netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttPendingSubscription.java index 782aef1e12..b603460c98 100644 --- a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttPendingSubscribtion.java +++ b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttPendingSubscription.java @@ -23,7 +23,7 @@ import java.util.HashSet; import java.util.Set; import java.util.function.Consumer; -final class MqttPendingSubscribtion { +final class MqttPendingSubscription { private final Promise future; private final String topic; @@ -34,7 +34,7 @@ final class MqttPendingSubscribtion { private boolean sent = false; - MqttPendingSubscribtion(Promise future, String topic, MqttSubscribeMessage message) { + MqttPendingSubscription(Promise future, String topic, MqttSubscribeMessage message) { this.future = future; this.topic = topic; this.subscribeMessage = message; diff --git a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttPendingUnsubscribtion.java b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttPendingUnsubscription.java similarity index 94% rename from netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttPendingUnsubscribtion.java rename to netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttPendingUnsubscription.java index a626a815e4..6901e2efed 100644 --- a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttPendingUnsubscribtion.java +++ b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttPendingUnsubscription.java @@ -21,14 +21,14 @@ import io.netty.util.concurrent.Promise; import java.util.function.Consumer; -final class MqttPendingUnsubscribtion { +final class MqttPendingUnsubscription { private final Promise future; private final String topic; private final RetransmissionHandler retransmissionHandler = new RetransmissionHandler<>(); - MqttPendingUnsubscribtion(Promise future, String topic, MqttUnsubscribeMessage unsubscribeMessage) { + MqttPendingUnsubscription(Promise future, String topic, MqttUnsubscribeMessage unsubscribeMessage) { this.future = future; this.topic = topic; diff --git a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttSubscribtion.java b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttSubscription.java similarity index 93% rename from netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttSubscribtion.java rename to netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttSubscription.java index 27f4cb969e..88761054b2 100644 --- a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttSubscribtion.java +++ b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttSubscription.java @@ -17,7 +17,7 @@ package org.thingsboard.mqtt; import java.util.regex.Pattern; -final class MqttSubscribtion { +final class MqttSubscription { private final String topic; private final Pattern topicRegex; @@ -27,7 +27,7 @@ final class MqttSubscribtion { private boolean called; - MqttSubscribtion(String topic, MqttHandler handler, boolean once) { + MqttSubscription(String topic, MqttHandler handler, boolean once) { if(topic == null){ throw new NullPointerException("topic"); } @@ -65,7 +65,7 @@ final class MqttSubscribtion { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - MqttSubscribtion that = (MqttSubscribtion) o; + MqttSubscription that = (MqttSubscription) o; return once == that.once && topic.equals(that.topic) && handler.equals(that.handler); } diff --git a/pom.xml b/pom.xml index b46faf6f24..3252d89d43 100755 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard thingsboard - 2.0.1 + 2.0.2 pom Thingsboard diff --git a/rule-engine/pom.xml b/rule-engine/pom.xml index 53b2d1ad24..232517bac4 100644 --- a/rule-engine/pom.xml +++ b/rule-engine/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.0.1 + 2.0.2 thingsboard rule-engine diff --git a/rule-engine/rule-engine-api/pom.xml b/rule-engine/rule-engine-api/pom.xml index a1c33faede..367a5e2571 100644 --- a/rule-engine/rule-engine-api/pom.xml +++ b/rule-engine/rule-engine-api/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 2.0.1 + 2.0.2 rule-engine org.thingsboard.rule-engine diff --git a/rule-engine/rule-engine-components/pom.xml b/rule-engine/rule-engine-components/pom.xml index 39832688e1..a561f0e9fa 100644 --- a/rule-engine/rule-engine-components/pom.xml +++ b/rule-engine/rule-engine-components/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 2.0.1 + 2.0.2 rule-engine org.thingsboard.rule-engine 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 dc16a1e774..6b05b7c526 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 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 f63e20143e..d1e000f55f 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 {{scope.name | translate}} '},function(e,t){e.exports="
{{ 'tb.rulenode.test-details-function' | translate }}
tb.rulenode.alarm-type-required
"},function(e,t){e.exports="
{{ 'tb.rulenode.test-details-function' | translate }}
tb.rulenode.alarm-type-required
{{ severity.name | translate}}
tb.rulenode.alarm-severity-required
{{ 'tb.rulenode.propagate' | translate }}
"},function(e,t){e.exports="
tb.rulenode.message-count-required
tb.rulenode.min-message-count-message
tb.rulenode.period-seconds-required
tb.rulenode.min-period-seconds-message
{{ 'tb.rulenode.test-generator-function' | translate }}
"},function(e,t){e.exports='
tb.rulenode.topic-pattern-required
tb.rulenode.bootstrap-servers-required
tb.rulenode.min-retries-message
tb.rulenode.min-batch-size-bytes-message
tb.rulenode.min-linger-ms-message
tb.rulenode.min-buffer-memory-bytes-message
{{ ackValue }}
tb.rulenode.key-serializer-required
tb.rulenode.value-serializer-required
'},function(e,t){e.exports="
{{ 'tb.rulenode.test-to-string-function' | translate }}
"},function(e,t){e.exports='
tb.rulenode.topic-pattern-required
tb.rulenode.mqtt-topic-pattern-hint
tb.rulenode.host-required
tb.rulenode.port-required
tb.rulenode.port-range
tb.rulenode.port-range
tb.rulenode.connect-timeout-required
tb.rulenode.connect-timeout-range
tb.rulenode.connect-timeout-range
{{ \'tb.rulenode.enable-ssl\' | translate }}
{{ \'tb.rulenode.credentials\' | translate }}
{{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}
{{ \'tb.rulenode.credentials\' | translate }}
{{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}
{{credentialsValue.name | translate}}
tb.rulenode.credentials-type-required
tb.rulenode.username-required
tb.rulenode.password-required
'},function(e,t){e.exports='
{{ property }}
tb.rulenode.host-required
tb.rulenode.port-required
tb.rulenode.port-range
tb.rulenode.port-range
{{ \'tb.rulenode.automatic-recovery\' | translate }}
tb.rulenode.min-connection-timeout-ms-message
tb.rulenode.min-handshake-timeout-ms-message
'},function(e,t){e.exports='
tb.rulenode.endpoint-url-pattern-required
tb.rulenode.endpoint-url-pattern-hint
{{ type }}
tb.rulenode.headers-hint
'},function(e,t){e.exports="
"},function(e,t){e.exports="
tb.rulenode.timeout-required
tb.rulenode.min-timeout-message
"},function(e,t){e.exports='
{{ \'tb.rulenode.use-system-smtp-settings\' | translate }}
{{smtpProtocol.toUpperCase()}}
tb.rulenode.smtp-host-required
tb.rulenode.smtp-port-required
tb.rulenode.smtp-port-range
tb.rulenode.smtp-port-range
tb.rulenode.timeout-required
tb.rulenode.min-timeout-msec-message
{{ \'tb.rulenode.enable-tls\' | translate }}
'},function(e,t){e.exports="
tb.rulenode.topic-arn-pattern-required
tb.rulenode.topic-arn-pattern-hint
tb.rulenode.aws-access-key-id-required
tb.rulenode.aws-secret-access-key-required
tb.rulenode.aws-region-required
"},function(e,t){e.exports='
{{ type.name | translate }}
tb.rulenode.queue-url-pattern-required
tb.rulenode.queue-url-pattern-hint
tb.rulenode.min-delay-seconds-message
tb.rulenode.max-delay-seconds-message
tb.rulenode.message-attributes-hint
tb.rulenode.aws-access-key-id-required
tb.rulenode.aws-secret-access-key-required
tb.rulenode.aws-region-required
'},function(e,t){e.exports="
tb.rulenode.default-ttl-required
tb.rulenode.min-default-ttl-message
"},function(e,t){e.exports='
{{ (\'relation.search-direction.\' + direction) | translate}}
relation.relation-type
device.device-types
'},function(e,t){e.exports="
{{ 'tb.rulenode.latest-telemetry' | translate }}
"; -},function(e,t){e.exports='
'},function(e,t){e.exports='
'},function(e,t){e.exports='
'},function(e,t){e.exports="
{{ 'tb.rulenode.latest-telemetry' | translate }}
"},21,function(e,t){e.exports="
{{ ('relation.search-direction.' + direction) | translate}}
"},function(e,t){e.exports='
{{item}}
tb.rulenode.no-message-types-found
tb.rulenode.no-message-type-matching tb.rulenode.create-new-message-type
{{$chip.name}}
'},function(e,t){e.exports="
{{ 'tb.rulenode.test-filter-function' | translate }}
"},function(e,t){e.exports="
{{ 'tb.rulenode.test-switch-function' | translate }}
"},function(e,t){e.exports='
{{ keyText }} {{ valText }}  
{{keyRequiredText}}
{{valRequiredText}}
{{ \'tb.key-val.remove-entry\' | translate }} close
{{ \'tb.key-val.add-entry\' | translate }} add {{ \'action.add\' | translate }}
'},function(e,t){e.exports="
{{ ('relation.search-direction.' + direction) | translate}}
relation.relation-filters
"},function(e,t){e.exports='
{{ source.name | translate}}
'},function(e,t){e.exports="
{{ 'tb.rulenode.test-transformer-function' | translate }}
"},function(e,t){e.exports="
tb.rulenode.from-template-required
tb.rulenode.from-template-hint
tb.rulenode.to-template-required
tb.rulenode.mail-address-list-template-hint
tb.rulenode.mail-address-list-template-hint
tb.rulenode.mail-address-list-template-hint
tb.rulenode.subject-template-required
tb.rulenode.subject-template-hint
tb.rulenode.body-template-required
tb.rulenode.body-template-hint
"},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("
"),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-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 ${metaKeyName} 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 ${metaKeyName} 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 ${metaKeyName} to substitute variables from metadata","body-template":"Body Template","body-template-required":"Body Template is required","body-template-hint":"Mail body template, use ${metaKeyName} 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 ${metaKeyName} to substitute variables from metadata","request-method":"Request method",headers:"Headers","headers-hint":"Use ${metaKeyName} 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 ${metaKeyName} 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 ${metaKeyName} 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 ${metaKeyName} 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 ${metaKeyName} 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='
{{scope.name | translate}}
'},function(e,t){e.exports="
{{ 'tb.rulenode.test-details-function' | translate }}
tb.rulenode.alarm-type-required
"},function(e,t){e.exports="
{{ 'tb.rulenode.test-details-function' | translate }}
tb.rulenode.alarm-type-required
{{ severity.name | translate}}
tb.rulenode.alarm-severity-required
{{ 'tb.rulenode.propagate' | translate }}
"},function(e,t){e.exports="
tb.rulenode.message-count-required
tb.rulenode.min-message-count-message
tb.rulenode.period-seconds-required
tb.rulenode.min-period-seconds-message
{{ 'tb.rulenode.test-generator-function' | translate }}
"},function(e,t){e.exports='
tb.rulenode.topic-pattern-required
tb.rulenode.bootstrap-servers-required
tb.rulenode.min-retries-message
tb.rulenode.min-batch-size-bytes-message
tb.rulenode.min-linger-ms-message
tb.rulenode.min-buffer-memory-bytes-message
{{ ackValue }}
tb.rulenode.key-serializer-required
tb.rulenode.value-serializer-required
'},function(e,t){e.exports="
{{ 'tb.rulenode.test-to-string-function' | translate }}
"},function(e,t){e.exports='
tb.rulenode.topic-pattern-required
tb.rulenode.mqtt-topic-pattern-hint
tb.rulenode.host-required
tb.rulenode.port-required
tb.rulenode.port-range
tb.rulenode.port-range
tb.rulenode.connect-timeout-required
tb.rulenode.connect-timeout-range
tb.rulenode.connect-timeout-range
{{ \'tb.rulenode.clean-session\' | translate }} {{ \'tb.rulenode.enable-ssl\' | translate }}
{{ \'tb.rulenode.credentials\' | translate }}
{{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}
{{ \'tb.rulenode.credentials\' | translate }}
{{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}
{{credentialsValue.name | translate}}
tb.rulenode.credentials-type-required
tb.rulenode.username-required
tb.rulenode.password-required
'},function(e,t){e.exports='
{{ property }}
tb.rulenode.host-required
tb.rulenode.port-required
tb.rulenode.port-range
tb.rulenode.port-range
{{ \'tb.rulenode.automatic-recovery\' | translate }}
tb.rulenode.min-connection-timeout-ms-message
tb.rulenode.min-handshake-timeout-ms-message
'},function(e,t){e.exports='
tb.rulenode.endpoint-url-pattern-required
tb.rulenode.endpoint-url-pattern-hint
{{ type }}
tb.rulenode.headers-hint
'},function(e,t){e.exports="
"},function(e,t){e.exports="
tb.rulenode.timeout-required
tb.rulenode.min-timeout-message
"},function(e,t){e.exports='
{{ \'tb.rulenode.use-system-smtp-settings\' | translate }}
{{smtpProtocol.toUpperCase()}}
tb.rulenode.smtp-host-required
tb.rulenode.smtp-port-required
tb.rulenode.smtp-port-range
tb.rulenode.smtp-port-range
tb.rulenode.timeout-required
tb.rulenode.min-timeout-msec-message
{{ \'tb.rulenode.enable-tls\' | translate }}
'},function(e,t){e.exports="
tb.rulenode.topic-arn-pattern-required
tb.rulenode.topic-arn-pattern-hint
tb.rulenode.aws-access-key-id-required
tb.rulenode.aws-secret-access-key-required
tb.rulenode.aws-region-required
"},function(e,t){e.exports='
{{ type.name | translate }}
tb.rulenode.queue-url-pattern-required
tb.rulenode.queue-url-pattern-hint
tb.rulenode.min-delay-seconds-message
tb.rulenode.max-delay-seconds-message
tb.rulenode.message-attributes-hint
tb.rulenode.aws-access-key-id-required
tb.rulenode.aws-secret-access-key-required
tb.rulenode.aws-region-required
'},function(e,t){e.exports="
tb.rulenode.default-ttl-required
tb.rulenode.min-default-ttl-message
"},function(e,t){e.exports='
{{ (\'relation.search-direction.\' + direction) | translate}}
relation.relation-type
device.device-types
'},function(e,t){e.exports="
{{ 'tb.rulenode.latest-telemetry' | translate }}
"; +},function(e,t){e.exports='
'},function(e,t){e.exports='
'},function(e,t){e.exports='
'},function(e,t){e.exports="
{{ 'tb.rulenode.latest-telemetry' | translate }}
"},21,function(e,t){e.exports="
{{ ('relation.search-direction.' + direction) | translate}}
"},function(e,t){e.exports='
{{item}}
tb.rulenode.no-message-types-found
tb.rulenode.no-message-type-matching tb.rulenode.create-new-message-type
{{$chip.name}}
'},function(e,t){e.exports="
{{ 'tb.rulenode.test-filter-function' | translate }}
"},function(e,t){e.exports="
{{ 'tb.rulenode.test-switch-function' | translate }}
"},function(e,t){e.exports='
{{ keyText }} {{ valText }}  
{{keyRequiredText}}
{{valRequiredText}}
{{ \'tb.key-val.remove-entry\' | translate }} close
{{ \'tb.key-val.add-entry\' | translate }} add {{ \'action.add\' | translate }}
'},function(e,t){e.exports="
{{ ('relation.search-direction.' + direction) | translate}}
relation.relation-filters
"},function(e,t){e.exports='
{{ source.name | translate}}
'},function(e,t){e.exports="
{{ 'tb.rulenode.test-transformer-function' | translate }}
"},function(e,t){e.exports="
tb.rulenode.from-template-required
tb.rulenode.from-template-hint
tb.rulenode.to-template-required
tb.rulenode.mail-address-list-template-hint
tb.rulenode.mail-address-list-template-hint
tb.rulenode.mail-address-list-template-hint
tb.rulenode.subject-template-required
tb.rulenode.subject-template-hint
tb.rulenode.body-template-required
tb.rulenode.body-template-hint
"},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("
"),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-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 ${metaKeyName} 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 ${metaKeyName} 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 ${metaKeyName} to substitute variables from metadata","body-template":"Body Template","body-template-required":"Body Template is required","body-template-hint":"Mail body template, use ${metaKeyName} 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 ${metaKeyName} to substitute variables from metadata","request-method":"Request method",headers:"Headers","headers-hint":"Use ${metaKeyName} 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 ${metaKeyName} 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 ${metaKeyName} 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 ${metaKeyName} 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 ${metaKeyName} 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 diff --git a/tools/pom.xml b/tools/pom.xml index 916baa1474..89263d38f4 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.0.1 + 2.0.2 thingsboard org.thingsboard diff --git a/transport/coap/pom.xml b/transport/coap/pom.xml index b9c0d957e5..41e6fc95e9 100644 --- a/transport/coap/pom.xml +++ b/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.0.1 + 2.0.2 transport org.thingsboard.transport 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 48150567a0..e2fd7cd6c4 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) { + + } + + }; } diff --git a/transport/http/pom.xml b/transport/http/pom.xml index ba944ae6ea..367b707a46 100644 --- a/transport/http/pom.xml +++ b/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.0.1 + 2.0.2 transport org.thingsboard.transport diff --git a/transport/mqtt/pom.xml b/transport/mqtt/pom.xml index fbdf2eae38..a2d13713c4 100644 --- a/transport/mqtt/pom.xml +++ b/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.0.1 + 2.0.2 transport org.thingsboard.transport 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 f666bb8a93..cc825b92b8 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); } diff --git a/transport/pom.xml b/transport/pom.xml index 8f07bd4f95..72aee87ce8 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.0.1 + 2.0.2 thingsboard org.thingsboard diff --git a/ui/package.json b/ui/package.json index 85ee4b80a3..83bca57b60 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": [ { diff --git a/ui/pom.xml b/ui/pom.xml index fa5f90f156..e836febb08 100644 --- a/ui/pom.xml +++ b/ui/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.0.1 + 2.0.2 thingsboard org.thingsboard diff --git a/ui/src/app/locale/locale.constant-es.js b/ui/src/app/locale/locale.constant-es.js index af8b5b1625..c6d2f96cbc 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!
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!
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.
¿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.
¿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 {{dashboardTitle}} es ahora público y podrá ser accedido desde: aquí:", - "public-dashboard-notice": "Nota: 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.
" + - "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 {{dashboardTitle}} es ahora público y podrá ser accedido desde: aquí:", + "public-dashboard-notice": "Nota: 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.
" + + "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}}'.
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):
{{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}}'.
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):
{{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):
{{widgetsList}}", + "duplicate-alias-error": "Duplicate alias found '{{alias}}'.
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.
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.
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 diff --git a/ui/src/app/locale/locale.constant-ko.js b/ui/src/app/locale/locale.constant-ko.js index 9273037b2f..91121577e1 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": "변경 사항을 저장하지 않았습니다.
이 페이지를 나가시겠습니까?", @@ -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):
{{widgetsList}}", + "duplicate-alias-error": "Duplicate alias found '{{alias}}'.
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 activation link :", // 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 diff --git a/ui/src/app/locale/locale.constant-ru.js b/ui/src/app/locale/locale.constant-ru.js index db66834c32..cff98809c0 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": "У вас есть несохраненные изменения.
Вы точно хотите покинуть эту страницу?", @@ -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": "Не удалось найти устройства, соответствующие фильтру псевдонимов.
" + - "Пожалуйста, свяжитесь с администратором для устранения этой проблемы.", + "Пожалуйста, свяжитесь с администратором для устранения этой проблемы.", "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):
{{widgetsList}}", + "duplicate-alias-error": "Duplicate alias found '{{alias}}'.
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 diff --git a/ui/src/app/locale/locale.constant-zh.js b/ui/src/app/locale/locale.constant-zh.js index c25291adc0..9a08ca146c 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": "使用该链接 激活 激活用户:", "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": "英语", diff --git a/ui/src/app/locale/locale.constant.js b/ui/src/app/locale/locale.constant.js index 5bb599cbc3..29f6f8e8f8 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.
" + - "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" } } }