Merge pull request #6986 from adovh/feature/work-1385-custom-mqtt-subscribe-topic
[3.5] added ability to create&subscribe custom mqtt attributes topics
This commit is contained in:
		
						commit
						14ed9df205
					
				@ -536,7 +536,19 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
 | 
			
		||||
    protected MqttDeviceProfileTransportConfiguration createMqttDeviceProfileTransportConfiguration(TransportPayloadTypeConfiguration transportPayloadTypeConfiguration, boolean sendAckOnValidationException) {
 | 
			
		||||
        MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = new MqttDeviceProfileTransportConfiguration();
 | 
			
		||||
        mqttDeviceProfileTransportConfiguration.setDeviceTelemetryTopic(MqttTopics.DEVICE_TELEMETRY_TOPIC);
 | 
			
		||||
        mqttDeviceProfileTransportConfiguration.setDeviceTelemetryTopic(MqttTopics.DEVICE_ATTRIBUTES_TOPIC);
 | 
			
		||||
        mqttDeviceProfileTransportConfiguration.setDeviceAttributesTopic(MqttTopics.DEVICE_ATTRIBUTES_TOPIC);
 | 
			
		||||
        mqttDeviceProfileTransportConfiguration.setDeviceAttributesSubscribeTopic(MqttTopics.DEVICE_ATTRIBUTES_TOPIC);
 | 
			
		||||
        mqttDeviceProfileTransportConfiguration.setSendAckOnValidationException(sendAckOnValidationException);
 | 
			
		||||
        mqttDeviceProfileTransportConfiguration.setTransportPayloadTypeConfiguration(transportPayloadTypeConfiguration);
 | 
			
		||||
        return mqttDeviceProfileTransportConfiguration;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected MqttDeviceProfileTransportConfiguration createMqttDeviceProfileTransportConfiguration(TransportPayloadTypeConfiguration transportPayloadTypeConfiguration, boolean sendAckOnValidationException,
 | 
			
		||||
                                                                                                    String telemetryTopic, String attributesPublishTopic, String attributesSubscribeTopic) {
 | 
			
		||||
        MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = new MqttDeviceProfileTransportConfiguration();
 | 
			
		||||
        mqttDeviceProfileTransportConfiguration.setDeviceTelemetryTopic(telemetryTopic);
 | 
			
		||||
        mqttDeviceProfileTransportConfiguration.setDeviceAttributesTopic(attributesPublishTopic);
 | 
			
		||||
        mqttDeviceProfileTransportConfiguration.setDeviceAttributesSubscribeTopic(attributesSubscribeTopic);
 | 
			
		||||
        mqttDeviceProfileTransportConfiguration.setSendAckOnValidationException(sendAckOnValidationException);
 | 
			
		||||
        mqttDeviceProfileTransportConfiguration.setTransportPayloadTypeConfiguration(transportPayloadTypeConfiguration);
 | 
			
		||||
        return mqttDeviceProfileTransportConfiguration;
 | 
			
		||||
 | 
			
		||||
@ -945,6 +945,28 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
 | 
			
		||||
        Assert.assertEquals(savedDeviceProfile, foundDeviceProfile);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testSaveDeviceProfileWorks() throws Exception {
 | 
			
		||||
        JsonTransportPayloadConfiguration jsonTransportPayloadConfiguration = new JsonTransportPayloadConfiguration();
 | 
			
		||||
        MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration =
 | 
			
		||||
                this.createMqttDeviceProfileTransportConfiguration(jsonTransportPayloadConfiguration, true,
 | 
			
		||||
                        "v1/devices/me/telemetry", "v1/devices/me/attributes", "v1/devices/me/subscribeattributes");
 | 
			
		||||
        DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile",
 | 
			
		||||
                mqttDeviceProfileTransportConfiguration);
 | 
			
		||||
        DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
 | 
			
		||||
        Assert.assertNotNull(savedDeviceProfile);
 | 
			
		||||
        Assert.assertEquals(savedDeviceProfile.getTransportType(), DeviceTransportType.MQTT);
 | 
			
		||||
        Assert.assertTrue(savedDeviceProfile.getProfileData().getTransportConfiguration() instanceof MqttDeviceProfileTransportConfiguration);
 | 
			
		||||
        MqttDeviceProfileTransportConfiguration transportConfiguration =
 | 
			
		||||
                (MqttDeviceProfileTransportConfiguration) savedDeviceProfile.getProfileData().getTransportConfiguration();
 | 
			
		||||
        Assert.assertTrue(transportConfiguration.isSendAckOnValidationException());
 | 
			
		||||
        DeviceProfile foundDeviceProfile =
 | 
			
		||||
                doGet("/api/deviceProfile/" + savedDeviceProfile.getId().getId().toString(), DeviceProfile.class);
 | 
			
		||||
        Assert.assertEquals(savedDeviceProfile.getProfileData().getTransportConfiguration(),
 | 
			
		||||
                foundDeviceProfile.getProfileData().getTransportConfiguration());
 | 
			
		||||
        Assert.assertEquals(savedDeviceProfile, foundDeviceProfile);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private DeviceProfile testSaveDeviceProfileWithProtoPayloadType(String schema) throws Exception {
 | 
			
		||||
        ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = this.createProtoTransportPayloadConfiguration(schema, schema, null, null);
 | 
			
		||||
        MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = this.createMqttDeviceProfileTransportConfiguration(protoTransportPayloadConfiguration, false);
 | 
			
		||||
 | 
			
		||||
@ -18,7 +18,11 @@ package org.thingsboard.server.transport.mqtt.mqttv3.attributes.updates;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.junit.Before;
 | 
			
		||||
import org.junit.Test;
 | 
			
		||||
import org.thingsboard.server.common.data.Device;
 | 
			
		||||
import org.thingsboard.server.common.data.DeviceProfile;
 | 
			
		||||
import org.thingsboard.server.common.data.TransportPayloadType;
 | 
			
		||||
import org.thingsboard.server.common.data.device.profile.JsonTransportPayloadConfiguration;
 | 
			
		||||
import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
 | 
			
		||||
import org.thingsboard.server.dao.service.DaoSqlTest;
 | 
			
		||||
import org.thingsboard.server.transport.mqtt.MqttTestConfigProperties;
 | 
			
		||||
import org.thingsboard.server.transport.mqtt.mqttv3.attributes.AbstractMqttAttributesIntegrationTest;
 | 
			
		||||
@ -46,6 +50,23 @@ public class MqttAttributesUpdatesIntegrationTest extends AbstractMqttAttributes
 | 
			
		||||
        processJsonTestSubscribeToAttributesUpdates(DEVICE_ATTRIBUTES_TOPIC);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testJsonSubscribeToAttributesUpdatesFromTheServerOnCustomTopic() throws Exception {
 | 
			
		||||
        Device tmp = savedDevice;
 | 
			
		||||
        String customTopic = "v1/devices/me/subscribeattributes";
 | 
			
		||||
        JsonTransportPayloadConfiguration jsonTransportPayloadConfiguration = new JsonTransportPayloadConfiguration();
 | 
			
		||||
        MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration =
 | 
			
		||||
                this.createMqttDeviceProfileTransportConfiguration(jsonTransportPayloadConfiguration, true,
 | 
			
		||||
                        "v1/devices/me/telemetry", "v1/devices/me/attributes", customTopic);
 | 
			
		||||
        DeviceProfile deviceProfile = this.createDeviceProfile("New device Profile",
 | 
			
		||||
                mqttDeviceProfileTransportConfiguration);
 | 
			
		||||
        DeviceProfile savedProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
 | 
			
		||||
        savedDevice.setDeviceProfileId(savedProfile.getId());
 | 
			
		||||
        doPost("/api/device", savedDevice);
 | 
			
		||||
        processJsonTestSubscribeToAttributesUpdates(customTopic);
 | 
			
		||||
        savedDevice = tmp;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testJsonSubscribeToAttributesUpdatesFromTheServerOnShortTopic() throws Exception {
 | 
			
		||||
        processJsonTestSubscribeToAttributesUpdates(DEVICE_ATTRIBUTES_SHORT_TOPIC);
 | 
			
		||||
 | 
			
		||||
@ -28,6 +28,9 @@ public class MqttDeviceProfileTransportConfiguration implements DeviceProfileTra
 | 
			
		||||
    private String deviceTelemetryTopic = MqttTopics.DEVICE_TELEMETRY_TOPIC;
 | 
			
		||||
    @NoXss
 | 
			
		||||
    private String deviceAttributesTopic = MqttTopics.DEVICE_ATTRIBUTES_TOPIC;
 | 
			
		||||
    @NoXss
 | 
			
		||||
    private String deviceAttributesSubscribeTopic = MqttTopics.DEVICE_ATTRIBUTES_TOPIC;
 | 
			
		||||
 | 
			
		||||
    private TransportPayloadTypeConfiguration transportPayloadTypeConfiguration;
 | 
			
		||||
    private boolean sparkplug;
 | 
			
		||||
    private Set<String> sparkplugAttributesMetricNames;
 | 
			
		||||
 | 
			
		||||
@ -687,6 +687,11 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
 | 
			
		||||
        for (MqttTopicSubscription subscription : mqttMsg.payload().topicSubscriptions()) {
 | 
			
		||||
            String topic = subscription.topicName();
 | 
			
		||||
            MqttQoS reqQoS = subscription.qualityOfService();
 | 
			
		||||
            if (deviceSessionCtx.isDeviceSubscriptionAttributesTopic(topic)){
 | 
			
		||||
                processAttributesSubscribe(grantedQoSList, topic, reqQoS, TopicType.V1);
 | 
			
		||||
                activityReported = true;
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            try {
 | 
			
		||||
                if (sparkplugSessionHandler != null) {
 | 
			
		||||
                    sparkplugSessionHandler.handleSparkplugSubscribeMsg(grantedQoSList, subscription, reqQoS);
 | 
			
		||||
 | 
			
		||||
@ -80,7 +80,8 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext {
 | 
			
		||||
    private MqttVersion mqttVersion;
 | 
			
		||||
 | 
			
		||||
    private volatile MqttTopicFilter telemetryTopicFilter = MqttTopicFilterFactory.getDefaultTelemetryFilter();
 | 
			
		||||
    private volatile MqttTopicFilter attributesTopicFilter = MqttTopicFilterFactory.getDefaultAttributesFilter();
 | 
			
		||||
    private volatile MqttTopicFilter attributesPublishTopicFilter = MqttTopicFilterFactory.getDefaultAttributesFilter();
 | 
			
		||||
    private volatile MqttTopicFilter attributesSubscribeTopicFilter = MqttTopicFilterFactory.getDefaultAttributesFilter();
 | 
			
		||||
    private volatile TransportPayloadType payloadType = TransportPayloadType.JSON;
 | 
			
		||||
    private volatile Descriptors.Descriptor attributesDynamicMessageDescriptor;
 | 
			
		||||
    private volatile Descriptors.Descriptor telemetryDynamicMessageDescriptor;
 | 
			
		||||
@ -110,7 +111,11 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean isDeviceAttributesTopic(String topicName) {
 | 
			
		||||
        return attributesTopicFilter.filter(topicName);
 | 
			
		||||
        return attributesPublishTopicFilter.filter(topicName);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean isDeviceSubscriptionAttributesTopic(String topicName) {
 | 
			
		||||
        return attributesSubscribeTopicFilter.filter(topicName);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public MqttTransportAdaptor getPayloadAdaptor() {
 | 
			
		||||
@ -161,7 +166,8 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext {
 | 
			
		||||
            TransportPayloadTypeConfiguration transportPayloadTypeConfiguration = mqttConfig.getTransportPayloadTypeConfiguration();
 | 
			
		||||
            payloadType = transportPayloadTypeConfiguration.getTransportPayloadType();
 | 
			
		||||
            telemetryTopicFilter = MqttTopicFilterFactory.toFilter(mqttConfig.getDeviceTelemetryTopic());
 | 
			
		||||
            attributesTopicFilter = MqttTopicFilterFactory.toFilter(mqttConfig.getDeviceAttributesTopic());
 | 
			
		||||
            attributesPublishTopicFilter = MqttTopicFilterFactory.toFilter(mqttConfig.getDeviceAttributesTopic());
 | 
			
		||||
            attributesSubscribeTopicFilter = MqttTopicFilterFactory.toFilter(mqttConfig.getDeviceAttributesSubscribeTopic());
 | 
			
		||||
            sendAckOnValidationException = mqttConfig.isSendAckOnValidationException();
 | 
			
		||||
            if (TransportPayloadType.PROTOBUF.equals(payloadType)) {
 | 
			
		||||
                ProtoTransportPayloadConfiguration protoTransportPayloadConfig = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration;
 | 
			
		||||
@ -171,7 +177,7 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext {
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            telemetryTopicFilter = MqttTopicFilterFactory.getDefaultTelemetryFilter();
 | 
			
		||||
            attributesTopicFilter = MqttTopicFilterFactory.getDefaultAttributesFilter();
 | 
			
		||||
            attributesPublishTopicFilter = MqttTopicFilterFactory.getDefaultAttributesFilter();
 | 
			
		||||
            payloadType = TransportPayloadType.JSON;
 | 
			
		||||
            sendAckOnValidationException = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -66,6 +66,21 @@
 | 
			
		||||
            {{ 'device-profile.not-valid-multi-character' | translate}}
 | 
			
		||||
          </mat-error>
 | 
			
		||||
        </mat-form-field>
 | 
			
		||||
        <mat-form-field fxFlex>
 | 
			
		||||
          <mat-label translate>device-profile.attributes-subscribe-topic-filter</mat-label>
 | 
			
		||||
          <input matInput required
 | 
			
		||||
                 formControlName="deviceAttributesSubscribeTopic"
 | 
			
		||||
                 type="text">
 | 
			
		||||
          <mat-error *ngIf="mqttDeviceProfileTransportConfigurationFormGroup.get('deviceAttributesSubscribeTopic').hasError('required')">
 | 
			
		||||
            {{ 'device-profile.attributes-subscribe-topic-filter-required' | translate}}
 | 
			
		||||
          </mat-error>
 | 
			
		||||
          <mat-error *ngIf="mqttDeviceProfileTransportConfigurationFormGroup.get('deviceAttributesSubscribeTopic').hasError('invalidSingleTopicCharacter')">
 | 
			
		||||
            {{ 'device-profile.not-valid-single-character' | translate}}
 | 
			
		||||
          </mat-error>
 | 
			
		||||
          <mat-error *ngIf="mqttDeviceProfileTransportConfigurationFormGroup.get('deviceAttributesSubscribeTopic').hasError('invalidMultiTopicCharacter')">
 | 
			
		||||
            {{ 'device-profile.not-valid-multi-character' | translate}}
 | 
			
		||||
          </mat-error>
 | 
			
		||||
        </mat-form-field>
 | 
			
		||||
      </div>
 | 
			
		||||
      <mat-error *ngIf="mqttDeviceProfileTransportConfigurationFormGroup.hasError('unique')">
 | 
			
		||||
        {{ 'device-profile.mqtt-device-topic-filters-unique' | translate }}
 | 
			
		||||
 | 
			
		||||
@ -91,6 +91,7 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control
 | 
			
		||||
  ngOnInit() {
 | 
			
		||||
    this.mqttDeviceProfileTransportConfigurationFormGroup = this.fb.group({
 | 
			
		||||
        deviceAttributesTopic: [null, [Validators.required, this.validationMQTTTopic()]],
 | 
			
		||||
        deviceAttributesSubscribeTopic: [null, [Validators.required, this.validationMQTTTopic()]],
 | 
			
		||||
        deviceTelemetryTopic: [null, [Validators.required, this.validationMQTTTopic()]],
 | 
			
		||||
        sparkplug: [false],
 | 
			
		||||
        sparkplugAttributesMetricNames: [null],
 | 
			
		||||
 | 
			
		||||
@ -1104,8 +1104,10 @@
 | 
			
		||||
        "support-level-wildcards": "Jsou podporovány jednoúrovňové <code>[+]</code> a víceúrovňové <code>[#]</code> zástupné znaky.",
 | 
			
		||||
        "telemetry-topic-filter": "Filtr fronty telemetrie",
 | 
			
		||||
        "telemetry-topic-filter-required": "Filtr fronty telemetrie je povinný.",
 | 
			
		||||
        "attributes-topic-filter": "Filtr atributů fronty",
 | 
			
		||||
        "attributes-topic-filter-required": "Filtr atributů fronty je povinný.",
 | 
			
		||||
        "attributes-topic-filter": "Attributes publish topic filter",
 | 
			
		||||
        "attributes-subscribe-topic-filter": "Attributes subscribe topic filter",
 | 
			
		||||
        "attributes-topic-filter-required": "Attributes publish topic filter is required.",
 | 
			
		||||
        "attributes-subscribe-topic-filter-required": "Attributes subscribe topic is required",
 | 
			
		||||
        "telemetry-proto-schema": "Proto schéma telemetrie",
 | 
			
		||||
        "telemetry-proto-schema-required": "Proto schéma telemetrie je povinné.",
 | 
			
		||||
        "attributes-proto-schema": "Atributy proto schémata",
 | 
			
		||||
 | 
			
		||||
@ -1440,8 +1440,10 @@
 | 
			
		||||
        "support-level-wildcards": "Single <code>[+]</code> and multi-level <code>[#]</code> wildcards supported.",
 | 
			
		||||
        "telemetry-topic-filter": "Telemetry topic filter",
 | 
			
		||||
        "telemetry-topic-filter-required": "Telemetry topic filter is required.",
 | 
			
		||||
        "attributes-topic-filter": "Attributes topic filter",
 | 
			
		||||
        "attributes-topic-filter-required": "Attributes topic filter is required.",
 | 
			
		||||
        "attributes-topic-filter": "Attributes publish topic filter",
 | 
			
		||||
        "attributes-subscribe-topic-filter": "Attributes subscribe topic filter",
 | 
			
		||||
        "attributes-topic-filter-required": "Attributes publish topic filter is required.",
 | 
			
		||||
        "attributes-subscribe-topic-filter-required": "Attributes subscribe topic is required",
 | 
			
		||||
        "telemetry-proto-schema": "Telemetry proto schema",
 | 
			
		||||
        "telemetry-proto-schema-required": "Telemetry proto schema is required.",
 | 
			
		||||
        "attributes-proto-schema": "Attributes proto schema",
 | 
			
		||||
 | 
			
		||||
@ -1298,8 +1298,10 @@
 | 
			
		||||
        "support-level-wildcards": "Se soportan los wilcards únicos <code>[+]</code> y multi-nivel <code>[#]</code>.",
 | 
			
		||||
        "telemetry-topic-filter": "Filtro de topic en telemetría",
 | 
			
		||||
        "telemetry-topic-filter-required": "Se requiere filtro de topic (telemetría).",
 | 
			
		||||
        "attributes-topic-filter": "Filtro de topic en atributos",
 | 
			
		||||
        "attributes-topic-filter-required": "Se requiere filtro de topic (atributos).",
 | 
			
		||||
        "attributes-topic-filter": "Attributes publish topic filter",
 | 
			
		||||
        "attributes-subscribe-topic-filter": "Attributes subscribe topic filter",
 | 
			
		||||
        "attributes-topic-filter-required": "Attributes publish topic filter is required.",
 | 
			
		||||
        "attributes-subscribe-topic-filter-required": "Attributes subscribe topic is required",
 | 
			
		||||
        "telemetry-proto-schema": "Proto esquema de telemetría",
 | 
			
		||||
        "telemetry-proto-schema-required": "Se requiere proto esquema de telemetría.",
 | 
			
		||||
        "attributes-proto-schema": "Proto esquema de atributos",
 | 
			
		||||
 | 
			
		||||
@ -1129,8 +1129,10 @@
 | 
			
		||||
        "support-level-wildcards": "<code>[+]</code> unique et wildcards de <code>[#]</code> multi-niveaux supportés.",
 | 
			
		||||
        "telemetry-topic-filter": "Filtre de sujets de télémétrie",
 | 
			
		||||
        "telemetry-topic-filter-required": "Filtre de sujets de télémétrie est requis.",
 | 
			
		||||
        "attributes-topic-filter": "Filtre de sujets d'attributs",
 | 
			
		||||
        "attributes-topic-filter-required": "Filtre de sujets d'attributs est requis.",
 | 
			
		||||
        "attributes-topic-filter": "Attributes publish topic filter",
 | 
			
		||||
        "attributes-subscribe-topic-filter": "Attributes subscribe topic filter",
 | 
			
		||||
        "attributes-topic-filter-required": "Attributes publish topic filter is required.",
 | 
			
		||||
        "attributes-subscribe-topic-filter-required": "Attributes subscribe topic is required",
 | 
			
		||||
        "telemetry-proto-schema": "Schéma proto de télémétrie",
 | 
			
		||||
        "telemetry-proto-schema-required": "Schéma proto de télémétrie est requis.",
 | 
			
		||||
        "attributes-proto-schema": "Schéma proto d'attributs",
 | 
			
		||||
 | 
			
		||||
@ -976,8 +976,10 @@
 | 
			
		||||
        "support-level-wildcards": "Single <code>[+]</code> and multi-level <code>[#]</code> wildcards supported.",
 | 
			
		||||
        "telemetry-topic-filter": "Telemetry topic filter",
 | 
			
		||||
        "telemetry-topic-filter-required": "Telemetry topic filter is required.",
 | 
			
		||||
        "attributes-topic-filter": "Attributes topic filter",
 | 
			
		||||
        "attributes-topic-filter-required": "Attributes topic filter is required.",
 | 
			
		||||
        "attributes-topic-filter": "Attributes publish topic filter",
 | 
			
		||||
        "attributes-subscribe-topic-filter": "Attributes subscribe topic filter",
 | 
			
		||||
        "attributes-topic-filter-required": "Attributes publish topic filter is required.",
 | 
			
		||||
        "attributes-subscribe-topic-filter-required": "Attributes subscribe topic is required",
 | 
			
		||||
        "telemetry-proto-schema": "Telemetry proto schema",
 | 
			
		||||
        "telemetry-proto-schema-required": "Telemetry proto schema is required.",
 | 
			
		||||
        "attributes-proto-schema": "Attributes proto schema",
 | 
			
		||||
 | 
			
		||||
@ -976,8 +976,10 @@
 | 
			
		||||
    "support-level-wildcards": "Single <code>[+]</code> and multi-level <code>[#]</code> wildcards supported.",
 | 
			
		||||
    "telemetry-topic-filter": "Telemetry topic filter",
 | 
			
		||||
    "telemetry-topic-filter-required": "Telemetry topic filter is required.",
 | 
			
		||||
    "attributes-topic-filter": "Attributes topic filter",
 | 
			
		||||
    "attributes-topic-filter-required": "Attributes topic filter is required.",
 | 
			
		||||
    "attributes-topic-filter": "Attributes publish topic filter",
 | 
			
		||||
    "attributes-subscribe-topic-filter": "Attributes subscribe topic filter",
 | 
			
		||||
    "attributes-topic-filter-required": "Attributes publish topic filter is required.",
 | 
			
		||||
    "attributes-subscribe-topic-filter-required": "Attributes subscribe topic is required",
 | 
			
		||||
    "telemetry-proto-schema": "Telemetry proto schema",
 | 
			
		||||
    "telemetry-proto-schema-required": "Telemetry proto schema is required.",
 | 
			
		||||
    "attributes-proto-schema": "Attributes proto schema",
 | 
			
		||||
 | 
			
		||||
@ -1107,8 +1107,10 @@
 | 
			
		||||
    "support-level-wildcards": "Tekli <code>[+]</code> ve çoklu <code>[#]</code> joker karakter destekler.",
 | 
			
		||||
    "telemetry-topic-filter": "Telemetri konu filtresi",
 | 
			
		||||
    "telemetry-topic-filter-required": "Telemetri konu filtresi gerekli.",
 | 
			
		||||
    "attributes-topic-filter": "Öznitelikler konu filtresi",
 | 
			
		||||
    "attributes-topic-filter-required": "Öznitelikler konu filtresi gerekli.",
 | 
			
		||||
    "attributes-topic-filter": "Attributes publish topic filter",
 | 
			
		||||
    "attributes-subscribe-topic-filter": "Attributes subscribe topic filter",
 | 
			
		||||
    "attributes-topic-filter-required": "Attributes publish topic filter is required.",
 | 
			
		||||
    "attributes-subscribe-topic-filter-required": "Attributes subscribe topic is required",
 | 
			
		||||
    "telemetry-proto-schema": "Telemetri proto şeması",
 | 
			
		||||
    "telemetry-proto-schema-required": "Telemetri proto şeması gerekli.",
 | 
			
		||||
    "attributes-proto-schema": "Öznitelikler proto şeması",
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user