diff --git a/application/pom.xml b/application/pom.xml
index 3093dcfb57..f76a0709c7 100644
--- a/application/pom.xml
+++ b/application/pom.xml
@@ -354,6 +354,10 @@
com.slack.api
slack-api-client
+
+ com.google.firebase
+ firebase-admin
+
diff --git a/application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java b/application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java
index e586f079ca..86131b41af 100644
--- a/application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java
@@ -149,7 +149,7 @@ public class SystemInfoController extends BaseController {
infoObject.put("artifact", buildProperties.getArtifact());
infoObject.put("name", buildProperties.getName());
} else {
- infoObject.put("version", "unknown");
+ infoObject.put("version", "3.5.5");
}
return infoObject;
}
diff --git a/application/src/main/java/org/thingsboard/server/service/notification/channels/MobileNotificationChannel.java b/application/src/main/java/org/thingsboard/server/service/notification/channels/MobileNotificationChannel.java
new file mode 100644
index 0000000000..75ff7866ab
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/notification/channels/MobileNotificationChannel.java
@@ -0,0 +1,109 @@
+/**
+ * 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.service.notification.channels;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.google.auth.oauth2.GoogleCredentials;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.firebase.FirebaseApp;
+import com.google.firebase.FirebaseOptions;
+import com.google.firebase.messaging.FirebaseMessaging;
+import com.google.firebase.messaging.Message;
+import com.google.firebase.messaging.Notification;
+import lombok.RequiredArgsConstructor;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+import org.thingsboard.server.common.data.User;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
+import org.thingsboard.server.common.data.notification.settings.MobileNotificationDeliveryMethodConfig;
+import org.thingsboard.server.common.data.notification.settings.NotificationSettings;
+import org.thingsboard.server.common.data.notification.template.MobileDeliveryMethodNotificationTemplate;
+import org.thingsboard.server.dao.notification.NotificationSettingsService;
+import org.thingsboard.server.service.executors.ExternalCallExecutorService;
+import org.thingsboard.server.service.notification.NotificationProcessingContext;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Optional;
+
+@Component
+@RequiredArgsConstructor
+public class MobileNotificationChannel implements NotificationChannel {
+
+ private final ExternalCallExecutorService executor;
+ private final NotificationSettingsService notificationSettingsService;
+
+ @Override
+ public ListenableFuture sendNotification(User recipient, MobileDeliveryMethodNotificationTemplate processedTemplate, NotificationProcessingContext ctx) {
+ String fcmToken = Optional.ofNullable(recipient.getAdditionalInfo())
+ .map(info -> info.get("fcmToken")).filter(JsonNode::isTextual).map(JsonNode::asText)
+ .orElse(null);
+ if (StringUtils.isEmpty(fcmToken)) {
+ return Futures.immediateFailedFuture(new RuntimeException("User doesn't have the mobile app installed"));
+ }
+
+ MobileNotificationDeliveryMethodConfig config = ctx.getDeliveryMethodConfig(NotificationDeliveryMethod.MOBILE);
+ return executor.submit(() -> {
+ FirebaseOptions firebaseOptions = FirebaseOptions.builder()
+ .setCredentials(GoogleCredentials.fromStream(IOUtils.toInputStream(config.getFirebaseServiceAccountCredentials(), StandardCharsets.UTF_8)))
+ .build();
+ String appName = ctx.getTenantId().toString();
+
+ FirebaseApp firebaseApp = FirebaseApp.getApps().stream()
+ .filter(app -> app.getName().equals(appName))
+ .findFirst().orElseGet(() -> {
+ try {
+ return FirebaseApp.initializeApp(firebaseOptions, appName);
+ } catch (IllegalStateException e) {
+ return FirebaseApp.getInstance(appName);
+ }
+ });
+ FirebaseMessaging firebaseMessaging;
+ try {
+ firebaseMessaging = FirebaseMessaging.getInstance(firebaseApp);
+ } catch (IllegalArgumentException e) {
+ // because of concurrency issues: FirebaseMessaging.getInstance lazily loads FirebaseMessagingService
+ firebaseMessaging = FirebaseMessaging.getInstance(firebaseApp);
+ }
+
+ Message message = Message.builder()
+ .setNotification(Notification.builder()
+ .setTitle(processedTemplate.getSubject())
+ .setBody(processedTemplate.getBody())
+ .build())
+ .setToken(fcmToken)
+ .build();
+ firebaseMessaging.send(message);
+ return null;
+ });
+ }
+
+ @Override
+ public void check(TenantId tenantId) throws Exception {
+ NotificationSettings settings = notificationSettingsService.findNotificationSettings(tenantId);
+ if (!settings.getDeliveryMethodsConfigs().containsKey(NotificationDeliveryMethod.MOBILE)) {
+ throw new RuntimeException("Push-notifications to mobile are not configured");
+ }
+ }
+
+ @Override
+ public NotificationDeliveryMethod getDeliveryMethod() {
+ return NotificationDeliveryMethod.MOBILE;
+ }
+
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationDeliveryMethod.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationDeliveryMethod.java
index 4a2c4657d5..5b9e84d37d 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationDeliveryMethod.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationDeliveryMethod.java
@@ -24,7 +24,8 @@ public enum NotificationDeliveryMethod {
WEB("web"),
EMAIL("email"),
SMS("SMS"),
- SLACK("Slack");
+ SLACK("Slack"),
+ MOBILE("mobile");
@Getter
private final String name;
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/MobileNotificationDeliveryMethodConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/MobileNotificationDeliveryMethodConfig.java
new file mode 100644
index 0000000000..4e443227ee
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/MobileNotificationDeliveryMethodConfig.java
@@ -0,0 +1,35 @@
+/**
+ * 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.common.data.notification.settings;
+
+import lombok.Data;
+import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
+
+import javax.validation.constraints.NotEmpty;
+
+@Data
+public class MobileNotificationDeliveryMethodConfig implements NotificationDeliveryMethodConfig {
+
+ private String firebaseServiceAccountCredentialsFileName;
+ @NotEmpty
+ private String firebaseServiceAccountCredentials;
+
+ @Override
+ public NotificationDeliveryMethod getMethod() {
+ return NotificationDeliveryMethod.MOBILE;
+ }
+
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/NotificationDeliveryMethodConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/NotificationDeliveryMethodConfig.java
index 962eba6d06..28020d6c4e 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/NotificationDeliveryMethodConfig.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/NotificationDeliveryMethodConfig.java
@@ -27,7 +27,8 @@ import java.io.Serializable;
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "method")
@JsonSubTypes({
- @Type(name = "SLACK", value = SlackNotificationDeliveryMethodConfig.class)
+ @Type(name = "SLACK", value = SlackNotificationDeliveryMethodConfig.class),
+ @Type(name = "MOBILE", value = MobileNotificationDeliveryMethodConfig.class)
})
public interface NotificationDeliveryMethodConfig extends Serializable {
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetType.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetType.java
index 1254654ecf..2180679cb2 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetType.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetType.java
@@ -25,7 +25,7 @@ import java.util.Set;
@RequiredArgsConstructor
public enum NotificationTargetType {
- PLATFORM_USERS(Set.of(NotificationDeliveryMethod.WEB, NotificationDeliveryMethod.EMAIL, NotificationDeliveryMethod.SMS)),
+ PLATFORM_USERS(Set.of(NotificationDeliveryMethod.WEB, NotificationDeliveryMethod.EMAIL, NotificationDeliveryMethod.SMS, NotificationDeliveryMethod.MOBILE)),
SLACK(Set.of(NotificationDeliveryMethod.SLACK));
@Getter
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.java
index ecb7f2d2b6..f033fe5092 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.java
@@ -33,7 +33,8 @@ import javax.validation.constraints.NotEmpty;
@Type(name = "WEB", value = WebDeliveryMethodNotificationTemplate.class),
@Type(name = "EMAIL", value = EmailDeliveryMethodNotificationTemplate.class),
@Type(name = "SMS", value = SmsDeliveryMethodNotificationTemplate.class),
- @Type(name = "SLACK", value = SlackDeliveryMethodNotificationTemplate.class)
+ @Type(name = "SLACK", value = SlackDeliveryMethodNotificationTemplate.class),
+ @Type(name = "MOBILE", value = MobileDeliveryMethodNotificationTemplate.class)
})
@Data
@NoArgsConstructor
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/MobileDeliveryMethodNotificationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/MobileDeliveryMethodNotificationTemplate.java
new file mode 100644
index 0000000000..91786db48a
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/MobileDeliveryMethodNotificationTemplate.java
@@ -0,0 +1,56 @@
+/**
+ * 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.common.data.notification.template;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import lombok.ToString;
+import org.apache.commons.lang3.StringUtils;
+import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
+
+import javax.validation.constraints.NotEmpty;
+
+@Data
+@NoArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class MobileDeliveryMethodNotificationTemplate extends DeliveryMethodNotificationTemplate implements HasSubject {
+
+ @NotEmpty
+ private String subject;
+
+ public MobileDeliveryMethodNotificationTemplate(MobileDeliveryMethodNotificationTemplate other) {
+ super(other);
+ this.subject = other.subject;
+ }
+
+ @Override
+ public NotificationDeliveryMethod getMethod() {
+ return NotificationDeliveryMethod.MOBILE;
+ }
+
+ @Override
+ public MobileDeliveryMethodNotificationTemplate copy() {
+ return new MobileDeliveryMethodNotificationTemplate(this);
+ }
+
+ @Override
+ public boolean containsAny(String... params) {
+ return super.containsAny(params) || StringUtils.containsAny(subject, params);
+ }
+
+}
diff --git a/pom.xml b/pom.xml
index f78a4826bc..f58e80480a 100755
--- a/pom.xml
+++ b/pom.xml
@@ -150,6 +150,7 @@
2.21.0
2.12.0
1.12.1
+ 8.0.1
3.4.0
@@ -1987,6 +1988,11 @@
slack-api-client
${slack-api.version}
+
+ com.google.firebase
+ firebase-admin
+ ${firebase-admin.version}
+
org.eclipse.jgit
org.eclipse.jgit