Browse Source

Merge branch 'publishTelemetryCommands' of github.com:dashevchenko/thingsboard into feature/check-connectivity-device

pull/8938/head
Vladyslav_Prykhodko 3 years ago
parent
commit
576455cdce
  1. 36
      application/src/main/java/org/thingsboard/server/controller/DeviceController.java
  2. 28
      application/src/main/resources/thingsboard.yml
  3. 193
      application/src/test/java/org/thingsboard/server/controller/DeviceControllerTest.java
  4. 5
      common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java
  5. 29
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityConfiguration.java
  6. 25
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityInfo.java
  7. 96
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
  8. 92
      dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java

36
application/src/main/java/org/thingsboard/server/controller/DeviceController.java

@ -21,8 +21,11 @@ import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
@ -73,11 +76,15 @@ import org.thingsboard.server.service.entitiy.device.TbDeviceService;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
import org.thingsboard.server.service.security.system.SystemSecurityService;
import javax.annotation.Nullable;
import javax.servlet.http.HttpServletRequest;
import java.net.URISyntaxException;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
@ -134,6 +141,8 @@ public class DeviceController extends BaseController {
private final TbDeviceService tbDeviceService;
private final SystemSecurityService systemSecurityService;
@ApiOperation(value = "Get Device (getDeviceById)",
notes = "Fetch the Device object based on the provided Device Id. " +
"If the user has the authority of 'TENANT_ADMIN', the server checks that the device is owned by the same tenant. " +
@ -164,6 +173,33 @@ public class DeviceController extends BaseController {
return checkDeviceInfoId(deviceId, Operation.READ);
}
@ApiOperation(value = "Get commands to publish device telemetry (getDevicePublishTelemetryCommands)",
notes = "Fetch the list of commands to publish device telemetry based on device profile " +
"If the user has the authority of 'Tenant Administrator', the server checks that the device is owned by the same tenant. " +
"If the user has the authority of 'Customer User', the server checks that the device is assigned to the same customer. " +
TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@ApiResponses(value = {
@ApiResponse(code = 200, message = "OK",
examples = @io.swagger.annotations.Example(
value = {
@io.swagger.annotations.ExampleProperty(
mediaType="application/json",
value="{\"http\":\"curl -v -X POST http://localhost:8080/api/v1/0ySs4FTOn5WU15XLmal8/telemetry --header Content-Type:application/json --data {temperature:25}\"," +
"\"mqtt\":\"mosquitto_pub -d -q 1 -h localhost -t v1/devices/me/telemetry -i myClient1 -u myUsername1 -P myPassword -m {temperature:25}\"," +
"\"coap\":\"coap-client -m POST coap://localhost:5683/api/v1/0ySs4FTOn5WU15XLmal8/telemetry -t json -e {temperature:25}\"}")}))})
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/device/{deviceId}/commands", method = RequestMethod.GET)
@ResponseBody
public Map<String, String> getDevicePublishTelemetryCommands(@ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION)
@PathVariable(DEVICE_ID) String strDeviceId, HttpServletRequest request) throws ThingsboardException, URISyntaxException {
checkParameter(DEVICE_ID, strDeviceId);
DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
Device device = checkDeviceId(deviceId, Operation.READ_CREDENTIALS);
String baseUrl = systemSecurityService.getBaseUrl(getTenantId(), getCurrentUser().getCustomerId(), request);
return deviceService.findDevicePublishTelemetryCommands(baseUrl, device);
}
@ApiOperation(value = "Create Or Update Device (saveDevice)",
notes = "Create or update the Device. When creating device, platform generates Device Id as " + UUID_WIKI_LINK +
"Device credentials are also generated if not provided in the 'accessToken' request parameter. " +

28
application/src/main/resources/thingsboard.yml

@ -985,6 +985,34 @@ transport:
enabled: "${TB_TRANSPORT_STATS_ENABLED:true}"
print-interval-ms: "${TB_TRANSPORT_STATS_PRINT_INTERVAL_MS:60000}"
# Device connectivity properties to publish telemetry
device:
connectivity:
http:
enabled: "${DEVICE_CONNECTIVITY_HTTP_ENABLED:true}"
host: "${DEVICE_CONNECTIVITY_HTTP_HOST:localhost}"
port: "${DEVICE_CONNECTIVITY_HTTP_PORT:8080}"
https:
enabled: "${DEVICE_CONNECTIVITY_HTTPS_ENABLED:false}"
host: "${DEVICE_CONNECTIVITY_HTTPS_HOST:localhost}"
port: "${DEVICE_CONNECTIVITY_HTTPS_PORT:443}"
mqtt:
enabled: "${DEVICE_CONNECTIVITY_MQTT_ENABLED:true}"
host: "${DEVICE_CONNECTIVITY_MQTT_HOST:localhost}"
port: "${DEVICE_CONNECTIVITY_MQTT_PORT:1883}"
mqtts:
enabled: "${DEVICE_CONNECTIVITY_MQTTS_ENABLED:false}"
host: "${DEVICE_CONNECTIVITY_MQTTS_HOST:localhost}"
port: "${DEVICE_CONNECTIVITY_MQTTS_PORT:8883}"
coap:
enabled: "${DEVICE_CONNECTIVITY_COAP_ENABLED:true}"
host: "${DEVICE_CONNECTIVITY_COAP_HOST:localhost}"
port: "${DEVICE_CONNECTIVITY_COAP_PORT:5683}"
coaps:
enabled: "${DEVICE_CONNECTIVITY_COAPS_ENABLED:false}"
host: "${DEVICE_CONNECTIVITY_COAPS_HOST:localhost}"
port: "${DEVICE_CONNECTIVITY_COAPS_PORT:5684}"
# Edges parameters
edges:
enabled: "${EDGES_ENABLED:true}"

193
application/src/test/java/org/thingsboard/server/controller/DeviceControllerTest.java

@ -34,12 +34,15 @@ import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.common.util.ThingsBoardExecutors;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceInfo;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceProfileType;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.OtaPackageInfo;
@ -49,10 +52,16 @@ import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials;
import org.thingsboard.server.common.data.device.profile.CoapDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration;
import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceCredentialsId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
@ -84,13 +93,27 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE;
import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE;
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.COAP;
import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.COAPS;
import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.HTTP;
import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.HTTPS;
import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.MQTT;
import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.MQTTS;
@TestPropertySource(properties = {
"device.connectivity.https.enabled=true",
"device.connectivity.mqtts.enabled=true",
"device.connectivity.coaps.enabled=true",
})
@ContextConfiguration(classes = {DeviceControllerTest.Config.class})
@DaoSqlTest
public class DeviceControllerTest extends AbstractControllerTest {
static final TypeReference<PageData<Device>> PAGE_DATA_DEVICE_TYPE_REF = new TypeReference<>() {
};
private static final String DEVICE_TELEMETRY_TOPIC = "v1/devices/customTopic";
private static final String CHECK_DOCUMENTATION = "Check documentation";
ListeningExecutorService executor;
List<ListenableFuture<Device>> futures;
@ -98,6 +121,8 @@ public class DeviceControllerTest extends AbstractControllerTest {
private Tenant savedTenant;
private User tenantAdmin;
private DeviceProfileId mqttDeviceProfileId;
private DeviceProfileId coapDeviceProfileId;
@SpyBean
private GatewayNotificationsService gatewayNotificationsService;
@ -132,6 +157,34 @@ public class DeviceControllerTest extends AbstractControllerTest {
tenantAdmin.setLastName("Downs");
tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
DeviceProfile mqttProfile = new DeviceProfile();
mqttProfile.setName("Mqtt device profile");
mqttProfile.setType(DeviceProfileType.DEFAULT);
mqttProfile.setTransportType(DeviceTransportType.MQTT);
DeviceProfileData deviceProfileData = new DeviceProfileData();
deviceProfileData.setConfiguration(new DefaultDeviceProfileConfiguration());
MqttDeviceProfileTransportConfiguration transportConfiguration = new MqttDeviceProfileTransportConfiguration();
transportConfiguration.setDeviceTelemetryTopic(DEVICE_TELEMETRY_TOPIC);
deviceProfileData.setTransportConfiguration(transportConfiguration);
mqttProfile.setProfileData(deviceProfileData);
mqttProfile.setDefault(false);
mqttProfile.setDefaultRuleChainId(null);
mqttDeviceProfileId = doPost("/api/deviceProfile", mqttProfile, DeviceProfile.class).getId();
DeviceProfile coapProfile = new DeviceProfile();
coapProfile.setName("Coap device profile");
coapProfile.setType(DeviceProfileType.DEFAULT);
coapProfile.setTransportType(DeviceTransportType.COAP);
DeviceProfileData deviceProfileData2 = new DeviceProfileData();
deviceProfileData2.setConfiguration(new DefaultDeviceProfileConfiguration());
deviceProfileData2.setTransportConfiguration(new CoapDeviceProfileTransportConfiguration());
coapProfile.setProfileData(deviceProfileData);
coapProfile.setDefault(false);
coapProfile.setDefaultRuleChainId(null);
coapDeviceProfileId = doPost("/api/deviceProfile", coapProfile, DeviceProfile.class).getId();
}
@After
@ -690,6 +743,144 @@ public class DeviceControllerTest extends AbstractControllerTest {
Assert.assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId());
}
@Test
public void testFetchPublishTelemetryCommandsForDefaultDevice() throws Exception {
Device device = new Device();
device.setName("My device");
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
Map<String, String> commands =
doGetTyped("/api/device/" + savedDevice.getId().getId() + "/commands", new TypeReference<>() {});
DeviceCredentials credentials =
doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class);
assertThat(commands).hasSize(6);
assertThat(commands.get(HTTP)).isEqualTo(String.format("curl -v -X POST http://localhost:8080/api/v1/%s/telemetry --header Content-Type:application/json --data \"{temperature:25}\"",
credentials.getCredentialsId()));
assertThat(commands.get(HTTPS)).isEqualTo(String.format("curl -v -X POST https://localhost:443/api/v1/%s/telemetry --header Content-Type:application/json --data \"{temperature:25}\"",
credentials.getCredentialsId()));
assertThat(commands.get(MQTT)).isEqualTo(String.format("mosquitto_pub -d -q 1 -h localhost -p 1883 -t v1/devices/me/telemetry -u %s -m \"{temperature:25}\"",
credentials.getCredentialsId()));
assertThat(commands.get(MQTTS)).isEqualTo(String.format("mosquitto_pub -d -q 1 --cafile tb-server-chain.pem -h localhost -p 8883 -t v1/devices/me/telemetry -u %s -m \"{temperature:25}\"",
credentials.getCredentialsId()));
assertThat(commands.get(COAP)).isEqualTo(String.format("coap-client -m POST coap://localhost:5683/api/v1/%s/telemetry -t json -e \"{temperature:25}\"",
credentials.getCredentialsId()));
assertThat(commands.get(COAPS)).isEqualTo(String.format("coap-client-openssl -v 9 -m POST coaps://localhost:5684/api/v1/%s/telemetry -t json -e \"{temperature:25}\"",
credentials.getCredentialsId()));
}
@Test
public void testFetchPublishTelemetryCommandsForMqttDeviceWithAccessToken() throws Exception {
Device device = new Device();
device.setName("My device");
device.setDeviceProfileId(mqttDeviceProfileId);
Device savedDevice = doPost("/api/device", device, Device.class);
DeviceCredentials credentials =
doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class);
Map<String, String> commands =
doGetTyped("/api/device/" + savedDevice.getId().getId() + "/commands", new TypeReference<>() {});
assertThat(commands).hasSize(2);
assertThat(commands.get(MQTT)).isEqualTo(String.format("mosquitto_pub -d -q 1 -h localhost -p 1883 -t %s -u %s -m \"{temperature:25}\"",
DEVICE_TELEMETRY_TOPIC, credentials.getCredentialsId()));
assertThat(commands.get(MQTTS)).isEqualTo(String.format("mosquitto_pub -d -q 1 --cafile tb-server-chain.pem -h localhost -p 8883 -t %s -u %s -m \"{temperature:25}\"",
DEVICE_TELEMETRY_TOPIC, credentials.getCredentialsId()));
}
@Test
public void testFetchPublishTelemetryCommandsForDeviceWithMqttBasicCreds() throws Exception {
Device device = new Device();
device.setName("My device");
device.setDeviceProfileId(mqttDeviceProfileId);
Device savedDevice = doPost("/api/device", device, Device.class);
DeviceCredentials credentials =
doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class);
credentials.setCredentialsId(null);
credentials.setCredentialsType(DeviceCredentialsType.MQTT_BASIC);
BasicMqttCredentials basicMqttCredentials = new BasicMqttCredentials();
String clientId = "testClientId";
String userName = "testUsername";
String password = "testPassword";
basicMqttCredentials.setClientId(clientId);
basicMqttCredentials.setUserName(userName);
basicMqttCredentials.setPassword(password);
credentials.setCredentialsValue(JacksonUtil.toString(basicMqttCredentials));
doPost("/api/device/credentials", credentials)
.andExpect(status().isOk());
Map<String, String> commands =
doGetTyped("/api/device/" + savedDevice.getId().getId() + "/commands", new TypeReference<>() {});
assertThat(commands).hasSize(2);
assertThat(commands.get(MQTT)).isEqualTo(String.format("mosquitto_pub -d -q 1 -h localhost -p 1883 -t %s -i %s -u %s -P %s -m \"{temperature:25}\"",
DEVICE_TELEMETRY_TOPIC, clientId, userName, password));
assertThat(commands.get(MQTTS)).isEqualTo(String.format("mosquitto_pub -d -q 1 --cafile tb-server-chain.pem -h localhost -p 8883 -t %s -i %s -u %s -P %s -m \"{temperature:25}\"",
DEVICE_TELEMETRY_TOPIC, clientId, userName, password));
}
@Test
public void testFetchPublishTelemetryCommandsForDeviceWithX509Creds() throws Exception {
Device device = new Device();
device.setName("My device");
device.setDeviceProfileId(mqttDeviceProfileId);
Device savedDevice = doPost("/api/device", device, Device.class);
DeviceCredentials credentials =
doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class);
credentials.setCredentialsId(null);
credentials.setCredentialsType(DeviceCredentialsType.X509_CERTIFICATE);
credentials.setCredentialsValue("testValue");
doPost("/api/device/credentials", credentials)
.andExpect(status().isOk());
Map<String, String> commands =
doGetTyped("/api/device/" + savedDevice.getId().getId() + "/commands", new TypeReference<>() {});
assertThat(commands).hasSize(1);
assertThat(commands.get(MQTTS)).isEqualTo(CHECK_DOCUMENTATION);
}
@Test
public void testFetchPublishTelemetryCommandsForСoapDevice() throws Exception {
Device device = new Device();
device.setName("My device");
device.setDeviceProfileId(coapDeviceProfileId);
Device savedDevice = doPost("/api/device", device, Device.class);
DeviceCredentials credentials =
doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class);
Map<String, String> commands =
doGetTyped("/api/device/" + savedDevice.getId().getId() + "/commands", new TypeReference<>() {});
assertThat(commands).hasSize(2);
assertThat(commands.get(COAP)).isEqualTo(String.format("coap-client -m POST coap://localhost:5683/api/v1/%s/telemetry -t json -e \"{temperature:25}\"",
credentials.getCredentialsId()));
assertThat(commands.get(COAPS)).isEqualTo(String.format("coap-client-openssl -v 9 -m POST coaps://localhost:5684/api/v1/%s/telemetry -t json -e \"{temperature:25}\"",
credentials.getCredentialsId()));
}
@Test
public void testFetchPublishTelemetryCommandsForСoapDeviceWithX509Creds() throws Exception {
Device device = new Device();
device.setName("My device");
device.setDeviceProfileId(coapDeviceProfileId);
Device savedDevice = doPost("/api/device", device, Device.class);
DeviceCredentials credentials =
doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class);
credentials.setCredentialsId(null);
credentials.setCredentialsType(DeviceCredentialsType.X509_CERTIFICATE);
credentials.setCredentialsValue("testValue");
doPost("/api/device/credentials", credentials)
.andExpect(status().isOk());
Map<String, String> commands =
doGetTyped("/api/device/" + savedDevice.getId().getId() + "/commands", new TypeReference<>() {});
assertThat(commands).hasSize(1);
assertThat(commands.get(COAPS)).isEqualTo(CHECK_DOCUMENTATION);
}
@Test
public void testSaveDeviceCredentials() throws Exception {
Device device = new Device();

5
common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.dao.device;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceIdInfo;
@ -36,13 +37,17 @@ import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.dao.device.provision.ProvisionRequest;
import org.thingsboard.server.dao.entity.EntityDaoService;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public interface DeviceService extends EntityDaoService {
DeviceInfo findDeviceInfoById(TenantId tenantId, DeviceId deviceId);
Map<String, String> findDevicePublishTelemetryCommands(String baseUrl, Device device) throws URISyntaxException;
Device findDeviceById(TenantId tenantId, DeviceId deviceId);
ListenableFuture<Device> findDeviceByIdAsync(TenantId tenantId, DeviceId deviceId);

29
dao/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityConfiguration.java

@ -0,0 +1,29 @@
/**
* Copyright © 2016-2023 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.device;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import java.util.Map;
@Configuration
@ConfigurationProperties(prefix = "device")
@Data
public class DeviceConnectivityConfiguration {
private Map<String, DeviceConnectivityInfo> connectivity;
}

25
dao/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityInfo.java

@ -0,0 +1,25 @@
/**
* Copyright © 2016-2023 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.device;
import lombok.Data;
@Data
public class DeviceConnectivityInfo {
private Boolean enabled;
private String host;
private String port;
}

96
dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java

@ -38,6 +38,7 @@ import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.TransportPayloadType;
import org.thingsboard.server.common.data.device.DeviceSearchQuery;
import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials;
import org.thingsboard.server.common.data.device.data.CoapDeviceTransportConfiguration;
@ -47,6 +48,7 @@ import org.thingsboard.server.common.data.device.data.DeviceData;
import org.thingsboard.server.common.data.device.data.Lwm2mDeviceTransportConfiguration;
import org.thingsboard.server.common.data.device.data.MqttDeviceTransportConfiguration;
import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
@ -74,9 +76,13 @@ import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.PaginatedRemover;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
@ -85,6 +91,17 @@ import static org.thingsboard.server.dao.service.Validator.validateId;
import static org.thingsboard.server.dao.service.Validator.validateIds;
import static org.thingsboard.server.dao.service.Validator.validatePageLink;
import static org.thingsboard.server.dao.service.Validator.validateString;
import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.COAP;
import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.COAPS;
import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.HTTP;
import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.HTTPS;
import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.JSON_EXAMPLE_PAYLOAD;
import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.MQTT;
import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.MQTTS;
import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.CHECK_DOCUMENTATION;
import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.getCoapClientCommand;
import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.getCurlCommand;
import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.getMosquittoPublishCommand;
@Service("DeviceDaoService")
@Slf4j
@ -96,6 +113,7 @@ public class DeviceServiceImpl extends AbstractCachedEntityService<DeviceCacheKe
public static final String INCORRECT_CUSTOMER_ID = "Incorrect customerId ";
public static final String INCORRECT_DEVICE_ID = "Incorrect deviceId ";
public static final String INCORRECT_EDGE_ID = "Incorrect edgeId ";
public static final String DEFAULT_DEVICE_TELEMETRY_TOPIC = "v1/devices/me/telemetry";
@Autowired
private DeviceDao deviceDao;
@ -115,6 +133,9 @@ public class DeviceServiceImpl extends AbstractCachedEntityService<DeviceCacheKe
@Autowired
private EntityCountService countService;
@Autowired
private DeviceConnectivityConfiguration deviceConnectivityConfiguration;
@Override
public DeviceInfo findDeviceInfoById(TenantId tenantId, DeviceId deviceId) {
log.trace("Executing findDeviceInfoById [{}]", deviceId);
@ -122,6 +143,47 @@ public class DeviceServiceImpl extends AbstractCachedEntityService<DeviceCacheKe
return deviceDao.findDeviceInfoById(tenantId, deviceId.getId());
}
@Override
public Map<String, String> findDevicePublishTelemetryCommands(String baseUrl, Device device) throws URISyntaxException {
DeviceId deviceId = device.getId();
log.trace("Executing findDevicePublishTelemetryCommands [{}]", deviceId);
validateId(deviceId, INCORRECT_DEVICE_ID + deviceId);
String defaultHostname = new URI(baseUrl).getHost();
DeviceCredentials creds = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getTenantId(), deviceId);
DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(device.getTenantId(), device.getDeviceProfileId());
DeviceTransportType transportType = deviceProfile.getTransportType();
Map<String, String> commands = new HashMap<>();
switch (transportType) {
case DEFAULT:
Optional.ofNullable(getHttpPublishCommand(HTTP, defaultHostname, creds)).ifPresent(v -> commands.put(HTTP, v));
Optional.ofNullable(getHttpPublishCommand(HTTPS, defaultHostname, creds)).ifPresent(v -> commands.put(HTTPS, v));
Optional.ofNullable(getMqttPublishCommand(MQTT, defaultHostname, creds)).ifPresent(v -> commands.put(MQTT, v));
Optional.ofNullable(getMqttPublishCommand(MQTTS, defaultHostname, creds)).ifPresent(v -> commands.put(MQTTS, v));
Optional.ofNullable(getCoapPublishCommand(COAP, defaultHostname, creds)).ifPresent(v -> commands.put(COAP, v));
Optional.ofNullable(getCoapPublishCommand(COAPS, defaultHostname, creds)).ifPresent(v -> commands.put(COAPS, v));
break;
case MQTT:
MqttDeviceProfileTransportConfiguration transportConfiguration =
(MqttDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
String topicName = transportConfiguration.getDeviceTelemetryTopic();
TransportPayloadType payloadType = transportConfiguration.getTransportPayloadTypeConfiguration().getTransportPayloadType();
String payload = (payloadType == TransportPayloadType.PROTOBUF) ? " -f protobufFileName" : " -m " + JSON_EXAMPLE_PAYLOAD;
Optional.ofNullable(getMqttPublishCommand(MQTT, defaultHostname, topicName, creds, payload)).ifPresent(v -> commands.put(MQTT, v));
Optional.ofNullable(getMqttPublishCommand(MQTTS, defaultHostname, topicName, creds, payload)).ifPresent(v -> commands.put(MQTTS, v));
break;
case COAP:
Optional.ofNullable(getCoapPublishCommand(COAP, defaultHostname, creds)).ifPresent(v -> commands.put(COAP, v));
Optional.ofNullable(getCoapPublishCommand(COAPS, defaultHostname, creds)).ifPresent(v -> commands.put(COAPS, v));
break;
default:
commands.put(transportType.name(), CHECK_DOCUMENTATION);
}
return commands;
}
@Override
public Device findDeviceById(TenantId tenantId, DeviceId deviceId) {
log.trace("Executing findDeviceById [{}]", deviceId);
@ -677,4 +739,38 @@ public class DeviceServiceImpl extends AbstractCachedEntityService<DeviceCacheKe
return EntityType.DEVICE;
}
private String getHttpPublishCommand(String protocol, String defaultHostname, DeviceCredentials deviceCredentials) {
DeviceConnectivityInfo httpProps = deviceConnectivityConfiguration.getConnectivity().get(protocol);
if (httpProps == null || !httpProps.getEnabled() ||
deviceCredentials.getCredentialsType() != DeviceCredentialsType.ACCESS_TOKEN) {
return null;
}
String hostName = httpProps.getHost().isEmpty() ? defaultHostname : httpProps.getHost();
String port = httpProps.getPort().isEmpty() ? "" : ":" + httpProps.getPort();
return getCurlCommand(protocol, hostName, port, deviceCredentials);
}
private String getMqttPublishCommand(String protocol, String defaultHostname, DeviceCredentials deviceCredentials) {
return getMqttPublishCommand(protocol, defaultHostname, DEFAULT_DEVICE_TELEMETRY_TOPIC, deviceCredentials, " -m " + JSON_EXAMPLE_PAYLOAD);
}
private String getMqttPublishCommand(String protocol, String defaultHostname, String deviceTelemetryTopic, DeviceCredentials deviceCredentials, String payload) {
DeviceConnectivityInfo properties = deviceConnectivityConfiguration.getConnectivity().get(protocol);
if (properties == null || !properties.getEnabled()) {
return null;
}
String mqttHost = properties.getHost().isEmpty() ? defaultHostname : properties.getHost();
String mqttPort = properties.getPort().isEmpty() ? null : properties.getPort();
return getMosquittoPublishCommand(protocol, mqttHost, mqttPort, deviceTelemetryTopic, deviceCredentials, payload);
}
private String getCoapPublishCommand(String protocol, String defaultHostname, DeviceCredentials deviceCredentials) {
DeviceConnectivityInfo properties = deviceConnectivityConfiguration.getConnectivity().get(protocol);
if (properties == null || !properties.getEnabled()) {
return null;
}
String hostName = properties.getHost().isEmpty() ? defaultHostname : properties.getHost();
String port = properties.getPort().isEmpty() ? "" : ":" + properties.getPort();
return getCoapClientCommand(protocol, hostName, port, deviceCredentials);
}
}

92
dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java

@ -0,0 +1,92 @@
/**
* Copyright © 2016-2023 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.util;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentials;
public class DeviceConnectivityUtil {
public static final String HTTP = "http";
public static final String HTTPS = "https";
public static final String MQTT = "mqtt";
public static final String MQTTS = "mqtts";
public static final String COAP = "coap";
public static final String COAPS = "coaps";
public static final String CHECK_DOCUMENTATION = "Check documentation";
public static final String JSON_EXAMPLE_PAYLOAD = "\"{temperature:25}\"";
public static String getCurlCommand(String protocol, String host, String port, DeviceCredentials deviceCredentials) {
return String.format("curl -v -X POST %s://%s%s/api/v1/%s/telemetry --header Content-Type:application/json --data " + JSON_EXAMPLE_PAYLOAD,
protocol, host, port, deviceCredentials.getCredentialsId());
}
public static String getMosquittoPublishCommand(String protocol, String host, String port, String deviceTelemetryTopic, DeviceCredentials deviceCredentials, String payload) {
StringBuilder command = new StringBuilder("mosquitto_pub -d -q 1");
if (MQTTS.equals(protocol)) {
command.append(" --cafile tb-server-chain.pem");
}
command.append(" -h ").append(host).append(port == null ? "" : " -p " + port);
command.append(" -t ").append(deviceTelemetryTopic);
switch (deviceCredentials.getCredentialsType()) {
case ACCESS_TOKEN:
command.append(" -u ").append(deviceCredentials.getCredentialsId());
break;
case MQTT_BASIC:
BasicMqttCredentials credentials = JacksonUtil.fromString(deviceCredentials.getCredentialsValue(),
BasicMqttCredentials.class);
if (credentials != null) {
if (credentials.getClientId() != null) {
command.append(" -i ").append(credentials.getClientId());
}
if (credentials.getUserName() != null) {
command.append(" -u ").append(credentials.getUserName());
}
if (credentials.getPassword() != null) {
command.append(" -P ").append(credentials.getPassword());
}
} else {
return null;
}
break;
case X509_CERTIFICATE:
if (MQTTS.equals(protocol)) {
return CHECK_DOCUMENTATION;
}
default:
return null;
}
command.append(payload);
return command.toString();
}
public static String getCoapClientCommand(String protocol, String host, String port, DeviceCredentials deviceCredentials) {
switch (deviceCredentials.getCredentialsType()) {
case ACCESS_TOKEN:
String client = COAPS.equals(protocol) ? "coap-client-openssl -v 9" : "coap-client";
return String.format("%s -m POST %s://%s%s/api/v1/%s/telemetry -t json -e %s",
client, protocol, host, port, deviceCredentials.getCredentialsId(), JSON_EXAMPLE_PAYLOAD);
case X509_CERTIFICATE:
if (COAPS.equals(protocol)) {
return CHECK_DOCUMENTATION;
}
default:
return null;
}
}
}
Loading…
Cancel
Save