diff --git a/application/pom.xml b/application/pom.xml index 405f3b4d7d..321a1fc638 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -249,6 +249,10 @@ io.grpc grpc-stub + + org.opensmpp + opensmpp-core + org.thingsboard springfox-boot-starter diff --git a/application/src/main/java/org/thingsboard/server/service/sms/DefaultSmsSenderFactory.java b/application/src/main/java/org/thingsboard/server/service/sms/DefaultSmsSenderFactory.java index c4b56dbb03..d86899219e 100644 --- a/application/src/main/java/org/thingsboard/server/service/sms/DefaultSmsSenderFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/sms/DefaultSmsSenderFactory.java @@ -19,9 +19,11 @@ import org.springframework.stereotype.Component; import org.thingsboard.rule.engine.api.sms.SmsSender; import org.thingsboard.rule.engine.api.sms.SmsSenderFactory; import org.thingsboard.server.common.data.sms.config.AwsSnsSmsProviderConfiguration; +import org.thingsboard.server.common.data.sms.config.SmppSmsProviderConfiguration; import org.thingsboard.server.common.data.sms.config.SmsProviderConfiguration; import org.thingsboard.server.common.data.sms.config.TwilioSmsProviderConfiguration; import org.thingsboard.server.service.sms.aws.AwsSmsSender; +import org.thingsboard.server.service.sms.smpp.SmppSmsSender; import org.thingsboard.server.service.sms.twilio.TwilioSmsSender; @Component @@ -34,6 +36,8 @@ public class DefaultSmsSenderFactory implements SmsSenderFactory { return new AwsSmsSender((AwsSnsSmsProviderConfiguration)config); case TWILIO: return new TwilioSmsSender((TwilioSmsProviderConfiguration)config); + case SMPP: + return new SmppSmsSender((SmppSmsProviderConfiguration) config); default: throw new RuntimeException("Unknown SMS provider type " + config.getType()); } diff --git a/application/src/main/java/org/thingsboard/server/service/sms/smpp/SmppSmsSender.java b/application/src/main/java/org/thingsboard/server/service/sms/smpp/SmppSmsSender.java new file mode 100644 index 0000000000..e89a346773 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sms/smpp/SmppSmsSender.java @@ -0,0 +1,183 @@ +/** + * Copyright © 2016-2021 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.sms.smpp; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.smpp.Connection; +import org.smpp.Data; +import org.smpp.Session; +import org.smpp.TCPIPConnection; +import org.smpp.TimeoutException; +import org.smpp.WrongSessionStateException; +import org.smpp.pdu.Address; +import org.smpp.pdu.BindReceiver; +import org.smpp.pdu.BindRequest; +import org.smpp.pdu.BindResponse; +import org.smpp.pdu.BindTransciever; +import org.smpp.pdu.BindTransmitter; +import org.smpp.pdu.PDUException; +import org.smpp.pdu.SubmitSM; +import org.smpp.pdu.SubmitSMResp; +import org.thingsboard.rule.engine.api.sms.exception.SmsException; +import org.thingsboard.server.common.data.sms.config.SmppSmsProviderConfiguration; +import org.thingsboard.server.service.sms.AbstractSmsSender; + +import java.io.IOException; +import java.util.Optional; + +@Slf4j +public class SmppSmsSender extends AbstractSmsSender { + private final SmppSmsProviderConfiguration config; + + private Session smppSession; + + public SmppSmsSender(SmppSmsProviderConfiguration config) { + if (config.getBindType() == null) { + config.setBindType(SmppSmsProviderConfiguration.SmppBindType.TX); + } + if (StringUtils.isNotEmpty(config.getSourceAddress())) { + if (config.getSourceTon() == null) { + config.setSourceTon((byte) 5); + } + if (config.getSourceNpi() == null) { + config.setSourceNpi((byte) 0); + } + } + if (config.getDestinationTon() == null) { + config.setDestinationTon((byte) 5); + } + if (config.getDestinationNpi() == null) { + config.setDestinationNpi((byte) 0); + } + + this.config = config; + initSmppSession(); + } + + @Override + public int sendSms(String numberTo, String message) throws SmsException { + try { + checkSmppSession(); + + SubmitSM request = new SubmitSM(); + if (StringUtils.isNotEmpty(config.getServiceType())) { + request.setServiceType(config.getServiceType()); + } + if (StringUtils.isNotEmpty(config.getSourceAddress())) { + request.setSourceAddr(new Address(config.getSourceTon(), config.getSourceNpi(), config.getSourceAddress())); + } + request.setDestAddr(new Address(config.getDestinationTon(), config.getDestinationNpi(), prepareNumber(numberTo))); + request.setShortMessage(message); + request.setDataCoding(Optional.ofNullable(config.getCodingScheme()).orElse((byte) 0)); + request.setReplaceIfPresentFlag((byte) 0); + request.setEsmClass((byte) 0); + request.setProtocolId((byte) 0); + request.setPriorityFlag((byte) 0); + request.setRegisteredDelivery((byte) 0); + request.setSmDefaultMsgId((byte) 0); + + SubmitSMResp response = smppSession.submit(request); + + log.info("SMPP submit command status: {}", response.getCommandStatus()); + } catch (Exception e) { + throw new RuntimeException(e); + } + + return countMessageSegments(message); + } + + public synchronized void checkSmppSession() { + if (!smppSession.isOpened()) { + smppSession = initSmppSession(); + } + } + + private Session initSmppSession() { + try { + Connection connection = new TCPIPConnection(config.getHost(), config.getPort()); + Session session = new Session(connection); + + BindRequest bindRequest; + switch (config.getBindType()) { + case TX: + bindRequest = new BindTransmitter(); + break; + case RX: + bindRequest = new BindReceiver(); + break; + case TRX: + bindRequest = new BindTransciever(); + break; + default: + throw new UnsupportedOperationException("Unsupported bind type " + config.getBindType()); + } + + bindRequest.setSystemId(config.getSystemId()); + bindRequest.setPassword(config.getPassword()); + + byte interfaceVersion; + switch (config.getProtocolVersion()) { + case "3.3": + interfaceVersion = Data.SMPP_V33; + break; + case "3.4": + interfaceVersion = Data.SMPP_V34; + break; + default: + throw new UnsupportedOperationException("Unsupported SMPP version: " + config.getProtocolVersion()); + } + bindRequest.setInterfaceVersion(interfaceVersion); + + if (StringUtils.isNotEmpty(config.getSystemType())) { + bindRequest.setSystemType(config.getSystemType()); + } + if (StringUtils.isNotEmpty(config.getAddressRange())) { + bindRequest.setAddressRange(config.getDestinationTon(), config.getDestinationNpi(), config.getAddressRange()); + } + + BindResponse bindResponse = session.bind(bindRequest); + log.debug("SMPP bind response: {}", bindResponse.debugString()); + + if (bindResponse.getCommandStatus() != 0) { + throw new IllegalStateException("Error status when binding: " + bindResponse.getCommandStatus()); + } + + return session; + } catch (Exception e) { + throw new IllegalArgumentException("Failed to establish SMPP session: " + ExceptionUtils.getRootCauseMessage(e)); + } + } + + private String prepareNumber(String number) { + if (config.getDestinationTon() == Data.GSM_TON_INTERNATIONAL) { + return StringUtils.removeStart(number, "+"); + } + return number; + } + + @Override + public void destroy() { + try { + smppSession.unbind(); + smppSession.close(); + } catch (TimeoutException | PDUException | IOException | WrongSessionStateException e) { + throw new RuntimeException(e); + } + + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmppSmsProviderConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmppSmsProviderConfiguration.java new file mode 100644 index 0000000000..4d9b53e10c --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmppSmsProviderConfiguration.java @@ -0,0 +1,117 @@ +/** + * Copyright © 2016-2021 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.sms.config; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +public class SmppSmsProviderConfiguration implements SmsProviderConfiguration { + @ApiModelProperty(value = "SMPP version", allowableValues = "3.3, 3.4", required = true) + private String protocolVersion; + + @ApiModelProperty(value = "SMPP host", required = true) + private String host; + @ApiModelProperty(value = "SMPP port", required = true) + private Integer port; + + @ApiModelProperty(value = "System ID", required = true) + private String systemId; + @ApiModelProperty(value = "Password", required = true) + private String password; + + @ApiModelProperty(value = "System type", required = false) + private String systemType; + @ApiModelProperty(value = "TX - Transmitter, RX - Receiver, TRX - Transciever. By default TX is used", required = false) + private SmppBindType bindType; + @ApiModelProperty(value = "Service type", required = false) + private String serviceType; + + @ApiModelProperty(value = "Source address", required = false) + private String sourceAddress; + @ApiModelProperty(value = "Source TON (Type of Number). Needed is source address is set. 5 by default.\n" + + "0 - Unknown\n" + + "1 - International\n" + + "2 - National\n" + + "3 - Network Specific\n" + + "4 - Subscriber Number\n" + + "5 - Alphanumeric\n" + + "6 - Abbreviated", required = false) + private Byte sourceTon; + @ApiModelProperty(value = "Source NPI (Numbering Plan Identification). Needed is source address is set. 0 by default.\n" + + "0 - Unknown\n" + + "1 - ISDN/telephone numbering plan (E163/E164)\n" + + "3 - Data numbering plan (X.121)\n" + + "4 - Telex numbering plan (F.69)\n" + + "6 - Land Mobile (E.212) =6\n" + + "8 - National numbering plan\n" + + "9 - Private numbering plan\n" + + "10 - ERMES numbering plan (ETSI DE/PS 3 01-3)\n" + + "13 - Internet (IP)\n" + + "18 - WAP Client Id (to be defined by WAP Forum)", required = false) + private Byte sourceNpi; + + @ApiModelProperty(value = "Destination TON (Type of Number). 5 by default.\n" + + "0 - Unknown\n" + + "1 - International\n" + + "2 - National\n" + + "3 - Network Specific\n" + + "4 - Subscriber Number\n" + + "5 - Alphanumeric\n" + + "6 - Abbreviated", required = false) + private Byte destinationTon; + @ApiModelProperty(value = "Destination NPI (Numbering Plan Identification). 0 by default.\n" + + "0 - Unknown\n" + + "1 - ISDN/telephone numbering plan (E163/E164)\n" + + "3 - Data numbering plan (X.121)\n" + + "4 - Telex numbering plan (F.69)\n" + + "6 - Land Mobile (E.212) =6\n" + + "8 - National numbering plan\n" + + "9 - Private numbering plan\n" + + "10 - ERMES numbering plan (ETSI DE/PS 3 01-3)\n" + + "13 - Internet (IP)\n" + + "18 - WAP Client Id (to be defined by WAP Forum)", required = false) + private Byte destinationNpi; + + @ApiModelProperty(value = "Address range", required = false) + private String addressRange; + + @ApiModelProperty(allowableValues = "0-10,13-14", + value = "0 - SMSC Default Alphabet (ASCII for short and long code and to GSM for toll-free, used as default)\n" + + "1 - IA5 (ASCII for short and long code, Latin 9 for toll-free (ISO-8859-9))\n" + + "2 - Octet Unspecified (8-bit binary)\n" + + "3 - Latin 1 (ISO-8859-1)\n" + + "4 - Octet Unspecified (8-bit binary)\n" + + "5 - JIS (X 0208-1990)\n" + + "6 - Cyrillic (ISO-8859-5)\n" + + "7 - Latin/Hebrew (ISO-8859-8)\n" + + "8 - UCS2/UTF-16 (ISO/IEC-10646)\n" + + "9 - Pictogram Encoding\n" + + "10 - Music Codes (ISO-2022-JP)\n" + + "13 - Extended Kanji JIS (X 0212-1990)\n" + + "14 - Korean Graphic Character Set (KS C 5601/KS X 1001)", required = false) + private Byte codingScheme; + + @Override + public SmsProviderType getType() { + return SmsProviderType.SMPP; + } + + public enum SmppBindType { + TX, RX, TRX + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmsProviderConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmsProviderConfiguration.java index 34f187b005..08968534b7 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmsProviderConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmsProviderConfiguration.java @@ -27,7 +27,9 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; property = "type") @JsonSubTypes({ @JsonSubTypes.Type(value = AwsSnsSmsProviderConfiguration.class, name = "AWS_SNS"), - @JsonSubTypes.Type(value = TwilioSmsProviderConfiguration.class, name = "TWILIO")}) + @JsonSubTypes.Type(value = TwilioSmsProviderConfiguration.class, name = "TWILIO"), + @JsonSubTypes.Type(value = SmppSmsProviderConfiguration.class, name = "SMPP") +}) public interface SmsProviderConfiguration { @JsonIgnore diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmsProviderType.java b/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmsProviderType.java index 33ccf440ab..f6390433cb 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmsProviderType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmsProviderType.java @@ -17,5 +17,6 @@ package org.thingsboard.server.common.data.sms.config; public enum SmsProviderType { AWS_SNS, - TWILIO + TWILIO, + SMPP } diff --git a/pom.xml b/pom.xml index ea6b4bf837..0a427aa081 100755 --- a/pom.xml +++ b/pom.xml @@ -132,6 +132,7 @@ 1.16.0 1.12 + 3.0.0 @@ -1871,6 +1872,11 @@ ${zeroturnaround.version} test + + org.opensmpp + opensmpp-core + ${opensmpp.version} + diff --git a/ui-ngx/src/app/modules/home/components/home-components.module.ts b/ui-ngx/src/app/modules/home/components/home-components.module.ts index 0960d98620..ad8c334973 100644 --- a/ui-ngx/src/app/modules/home/components/home-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/home-components.module.ts @@ -117,6 +117,7 @@ import { DefaultTenantProfileConfigurationComponent } from '@home/components/pro import { TenantProfileConfigurationComponent } from '@home/components/profile/tenant/tenant-profile-configuration.component'; import { SmsProviderConfigurationComponent } from '@home/components/sms/sms-provider-configuration.component'; import { AwsSnsProviderConfigurationComponent } from '@home/components/sms/aws-sns-provider-configuration.component'; +import { SmppSmsProviderConfigurationComponent } from '@home/components/sms/smpp-sms-provider-configuration.component'; import { TwilioSmsProviderConfigurationComponent } from '@home/components/sms/twilio-sms-provider-configuration.component'; import { Lwm2mProfileComponentsModule } from '@home/components/profile/device/lwm2m/lwm2m-profile-components.module'; import { DashboardPageComponent } from '@home/components/dashboard-page/dashboard-page.component'; @@ -253,6 +254,7 @@ import { EntityDetailsPageComponent } from '@home/components/entity/entity-detai EditAlarmDetailsDialogComponent, SmsProviderConfigurationComponent, AwsSnsProviderConfigurationComponent, + SmppSmsProviderConfigurationComponent, TwilioSmsProviderConfigurationComponent, DashboardToolbarComponent, DashboardPageComponent, @@ -366,6 +368,7 @@ import { EntityDetailsPageComponent } from '@home/components/entity/entity-detai AlarmScheduleComponent, SmsProviderConfigurationComponent, AwsSnsProviderConfigurationComponent, + SmppSmsProviderConfigurationComponent, TwilioSmsProviderConfigurationComponent, DashboardToolbarComponent, DashboardPageComponent, diff --git a/ui-ngx/src/app/modules/home/components/sms/smpp-sms-provider-configuration.component.html b/ui-ngx/src/app/modules/home/components/sms/smpp-sms-provider-configuration.component.html new file mode 100644 index 0000000000..e8c3de5943 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/sms/smpp-sms-provider-configuration.component.html @@ -0,0 +1,101 @@ +
+ + SMPP version + + + SMPP version is required + + + + SMPP host + + + SMPP host is required + + + + SMPP port + + + SMPP port is required + + + + System ID + + + System ID is required + + + + Password + + + Password is required + + + + System type + + + + Bind type + + + {{bindType.name}} + + + + + Service type + + + + Source address + + + + Source TON + + + {{sourceTon.name}} + + + + + Source NPI + + + {{sourceNpi.name}} + + + + + Destination TON (Type of Number) + + + {{destinationTon.name}} + + + + + Destination NPI (Numbering Plan Identification) + + + {{destinationNpi.name}} + + + + + Address range + + + + Coding Scheme + + + {{codingScheme.name}} + + + +
diff --git a/ui-ngx/src/app/modules/home/components/sms/smpp-sms-provider-configuration.component.ts b/ui-ngx/src/app/modules/home/components/sms/smpp-sms-provider-configuration.component.ts new file mode 100644 index 0000000000..2cb7d8cbfb --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/sms/smpp-sms-provider-configuration.component.ts @@ -0,0 +1,163 @@ +import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; +import { + AwsSnsSmsProviderConfiguration, SmppSmsProviderConfiguration, + SmsProviderConfiguration, + SmsProviderType +} from '@shared/models/settings.models'; +import { isDefinedAndNotNull } from '@core/utils'; +import { coerceBooleanProperty } from '@angular/cdk/coercion'; + +@Component({ + selector: 'tb-smpp-sms-provider-configuration', + templateUrl: './smpp-sms-provider-configuration.component.html', + styleUrls: [], + providers: [{ + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => SmppSmsProviderConfigurationComponent), + multi: true + }] +}) +export class SmppSmsProviderConfigurationComponent implements ControlValueAccessor, OnInit{ + constructor(private fb: FormBuilder) { + } + private requiredValue: boolean; + + get required(): boolean { + return this.requiredValue; + } + + @Input() + set required(value: boolean) { + this.requiredValue = coerceBooleanProperty(value); + } + @Input() + disabled: boolean; + + smppSmsProviderConfigurationFormGroup: FormGroup; + bindTypes = [ + {value: 'TX', name: 'Transmitter'}, + {value: 'RX', name: 'Receiver'}, + {value: 'TRX', name: 'Transciever'}, + ] + + sourcesTon = [ + {value: 0, name: 'Unknown'}, + {value: 1, name: 'International'}, + {value: 2, name: 'National'}, + {value: 3, name: 'Network Specific'}, + {value: 4, name: 'Subscriber Number'}, + {value: 5, name: 'Alphanumeric'}, + {value: 6, name: 'Abbreviated'} + ] + + sourcesNpi = [ + {value: 0, name: 'Unknown'}, + {value: 1, name: 'ISDN/telephone numbering plan (E163/E164)'}, + {value: 3, name: 'Data numbering plan (X.121)'}, + {value: 4, name: 'Telex numbering plan (F.69)'}, + {value: 5, name: 'Land Mobile (E.212)'}, + {value: 8, name: 'National numbering plan'}, + {value: 9, name: 'Private numbering plan'}, + {value: 10, name: 'ERMES numbering plan (ETSI DE/PS 3 01-3)'}, + {value: 13, name: 'Internet (IP)'}, + {value: 18, name: 'WAP Client Id (to be defined by WAP Forum)'}, + ] + + destinationsTon = [ + {value: 0, name: 'Unknown'}, + {value: 1, name: 'International'}, + {value: 2, name: 'National'}, + {value: 3, name: 'Network Specific'}, + {value: 4, name: 'Subscriber Number'}, + {value: 5, name: 'Alphanumeric'}, + {value: 6, name: 'Abbreviated'}, + ] + + destinationsNpi = [ + {value: 0, name: 'Unknown'}, + {value: 1, name: 'ISDN/telephone numbering plan (E163/E164)'}, + {value: 3, name: 'Data numbering plan (X.121)'}, + {value: 4, name: 'Telex numbering plan (F.69)'}, + {value: 6, name: 'Land Mobile (E.212)'}, + {value: 8, name: 'National numbering plan'}, + {value: 9, name: 'Private numbering plan'}, + {value: 10, name: 'ERMES numbering plan (ETSI DE/PS 3 01-3)'}, + {value: 13, name: 'Internet (IP)'}, + {value: 18, name: 'WAP Client Id (to be defined by WAP Forum)'}, + ] + + codingSchemes = [ + {value: 0, name: 'SMSC Default Alphabet (ASCII for short and long code and to GSM for toll-free)'}, + {value: 1, name: 'IA5 (ASCII for short and long code, Latin 9 for toll-free (ISO-8859-9))'}, + {value: 2, name: 'Octet Unspecified (8-bit binary)'}, + {value: 3, name: 'Latin 1 (ISO-8859-1)'}, + {value: 4, name: 'Octet Unspecified (8-bit binary)'}, + {value: 5, name: 'JIS (X 0208-1990)'}, + {value: 6, name: 'Cyrillic (ISO-8859-5)'}, + {value: 7, name: 'Latin/Hebrew (ISO-8859-8)'}, + {value: 8, name: 'UCS2/UTF-16 (ISO/IEC-10646)'}, + {value: 9, name: 'Pictogram Encoding'}, + {value: 10, name: 'Music Codes (ISO-2022-JP)'}, + {value: 13, name: 'Extended Kanji JIS (X 0212-1990)'}, + {value: 14, name: 'Korean Graphic Character Set (KS C 5601/KS X 1001)'}, + ] + + private propagateChange = (v: any) => { }; + + ngOnInit(): void { + this.smppSmsProviderConfigurationFormGroup = this.fb.group({ + protocolVersion: [null, [Validators.required]], + host: [null, [Validators.required]], + port: [null, [Validators.required]], + systemId: [null, [Validators.required]], + password: [null, [Validators.required]], + systemType: [null], + bindType: [null, []], + serviceType: [null, []], + sourceAddress: [null, []], + sourceTon: [null, []], + sourceNpi: [null, []], + destinationTon: [null, []], + destinationNpi: [null, []], + addressRange: [null, []], + codingScheme: [null, []], + }); + + this.smppSmsProviderConfigurationFormGroup.valueChanges.subscribe(() => { + this.updateValue(); + }); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.disabled) { + this.smppSmsProviderConfigurationFormGroup.disable({emitEvent: false}); + } else { + this.smppSmsProviderConfigurationFormGroup.enable({emitEvent: false}); + } + } + + writeValue(value: AwsSnsSmsProviderConfiguration | null): void { + if (isDefinedAndNotNull(value)) { + this.smppSmsProviderConfigurationFormGroup.patchValue(value, {emitEvent: false}); + } + } + + private updateValue() { + let configuration: SmppSmsProviderConfiguration = null; + if (this.smppSmsProviderConfigurationFormGroup.valid) { + configuration = this.smppSmsProviderConfigurationFormGroup.value; + (configuration as SmsProviderConfiguration).type = SmsProviderType.SMPP; + } + this.propagateChange(configuration); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/sms/sms-provider-configuration.component.html b/ui-ngx/src/app/modules/home/components/sms/sms-provider-configuration.component.html index d9964285e3..ca21d30335 100644 --- a/ui-ngx/src/app/modules/home/components/sms/sms-provider-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/sms/sms-provider-configuration.component.html @@ -40,5 +40,11 @@ formControlName="configuration"> + + + + diff --git a/ui-ngx/src/app/shared/models/settings.models.ts b/ui-ngx/src/app/shared/models/settings.models.ts index b130bb6338..ede42bc033 100644 --- a/ui-ngx/src/app/shared/models/settings.models.ts +++ b/ui-ngx/src/app/shared/models/settings.models.ts @@ -15,7 +15,7 @@ /// import { ValidatorFn } from '@angular/forms'; -import { isNotEmptyStr } from '@core/utils'; +import { isNotEmptyStr, isNumber } from '@core/utils'; export const smtpPortPattern: RegExp = /^([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$/; @@ -72,13 +72,15 @@ export const phoneNumberPatternTwilio = /^\+[1-9]\d{1,14}$|^(MG|PN).*$/; export enum SmsProviderType { AWS_SNS = 'AWS_SNS', - TWILIO = 'TWILIO' + TWILIO = 'TWILIO', + SMPP = 'SMPP' } export const smsProviderTypeTranslationMap = new Map( [ [SmsProviderType.AWS_SNS, 'admin.sms-provider-type-aws-sns'], - [SmsProviderType.TWILIO, 'admin.sms-provider-type-twilio'] + [SmsProviderType.TWILIO, 'admin.sms-provider-type-twilio'], + [SmsProviderType.SMPP, 'admin.sms-provider-type-smpp'] ] ); @@ -94,7 +96,26 @@ export interface TwilioSmsProviderConfiguration { numberFrom?: string; } -export type SmsProviderConfigurations = AwsSnsSmsProviderConfiguration & TwilioSmsProviderConfiguration; +export interface SmppSmsProviderConfiguration { + protocolVersion?: string, + host?: string, + port?: number, + systemId?: string, + password?: string, + systemType?: string, + bindType?: string, + serviceType?: string, + sourceAddress?: string, + sourceTon?: number, + sourceNpi?: number, + destinationTon?: number, + destinationNpi?: number, + addressRange?: string, + codingScheme?: number +} + + +export type SmsProviderConfigurations = SmppSmsProviderConfiguration & AwsSnsSmsProviderConfiguration & TwilioSmsProviderConfiguration; export interface SmsProviderConfiguration extends SmsProviderConfigurations { type: SmsProviderType; @@ -118,6 +139,11 @@ export function smsProviderConfigurationValidator(required: boolean): ValidatorF valid = isNotEmptyStr(twilioConfiguration.numberFrom) && isNotEmptyStr(twilioConfiguration.accountSid) && isNotEmptyStr(twilioConfiguration.accountToken); break; + case SmsProviderType.SMPP: + const smppConfiguration: SmppSmsProviderConfiguration = configuration; + valid = isNotEmptyStr(smppConfiguration.protocolVersion) && isNotEmptyStr(smppConfiguration.host) + && isNumber(smppConfiguration.port) && isNotEmptyStr(smppConfiguration.systemId) && isNotEmptyStr(smppConfiguration.password); + break; } } if (!valid) { @@ -156,6 +182,26 @@ export function createSmsProviderConfiguration(type: SmsProviderType): SmsProvid }; smsProviderConfiguration = {...twilioSmsProviderConfiguration, type: SmsProviderType.TWILIO}; break; + case SmsProviderType.SMPP: + const smppSmsProviderConfiguration: SmppSmsProviderConfiguration = { + protocolVersion: '', + host: '', + port: null, + systemId: '', + password: '', + systemType: '', + bindType: 'TX', + serviceType: '', + sourceAddress: '', + sourceTon: 5, + sourceNpi: 0, + destinationTon: 5, + destinationNpi: 0, + addressRange: '', + codingScheme: 0 + }; + smsProviderConfiguration = {...smppSmsProviderConfiguration, type: SmsProviderType.SMPP}; + break; } } return smsProviderConfiguration; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 9775e5c09d..988722d3b5 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -112,6 +112,7 @@ "sms-provider-type-required": "SMS provider type is required.", "sms-provider-type-aws-sns": "Amazon SNS", "sms-provider-type-twilio": "Twilio", + "sms-provider-type-smpp": "SMPP", "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",