From ae77e94cd6e4c1aa4e2bcb253740917a5de83f82 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 30 Aug 2016 00:27:13 -0700 Subject: [PATCH] Added support for Linear AE-100 and AE-500 --- README.md | 41 ++- .../tesremoteprogrammer/MainActivity.kt | 16 +- .../ProgrammingActivity.kt | 71 +++- .../filters/AlphaFilter.kt | 10 +- .../tesremoteprogrammer/models/Field.kt | 16 +- .../tesremoteprogrammer/models/Params.kt | 15 +- .../android/tesremoteprogrammer/util/Dtmf.kt | 151 +++++--- app/src/main/res/raw/dks_1802.json | 18 +- app/src/main/res/raw/dks_1802_epd.json | 32 +- app/src/main/res/raw/dks_1803_1808_1810.json | 12 +- app/src/main/res/raw/dks_1812.json | 16 +- app/src/main/res/raw/dks_1819.json | 12 +- app/src/main/res/raw/linear_ae_100.json | 162 +++++++++ app/src/main/res/raw/linear_ae_500.json | 342 ++++++++++++++++++ app/src/main/res/values/strings.xml | 1 + 15 files changed, 780 insertions(+), 135 deletions(-) create mode 100644 app/src/main/res/raw/linear_ae_100.json create mode 100644 app/src/main/res/raw/linear_ae_500.json diff --git a/README.md b/README.md index de98ac0..9567ff8 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,11 @@ Program your Telephone Entry System remotely (off-site) using your phone. Most Telephone Entry Systems can be programmed from the front keypad or remotely from an off-premise location using any touch-tone phone. TES Remote Programmer streamlines the often cumbersome remote programming process by providing data entry screens and automated dial-in options. -By default, TES Remote Programmer is configured to work with the [DoorKing (DKS) 1802, 1802-EPD, 1803, 1808, 1810 and 1819](http://www.doorking.com/telephone/) systems, additional [configurations](#configurations) can be created for most systems supporting remote programming. +### DoorKing (DKS) & Linear + +By default, TES Remote Programmer is configured to work with the [DoorKing (DKS) 1802, 1802-EPD, 1803, 1808, 1810 and 1819](http://www.doorking.com/telephone/) as well as the [Linear AE-100 and AE-500](http://www.linearproaccess.com/access-controls/telephone-entry-systems/) systems, additional [configurations](#configurations) can be created for most systems supporting remote programming. + +### Directory Code Please note that all default configurations are set for 3-digits directory code length. If your system is setup differently, you will need to modify the [default configuration](app/src/main/res/raw/) and re-import it. Look for all `Directory Code` fields and modify their `size` attributes accordingly. @@ -43,8 +47,8 @@ Parameters define the configuration's global settings. ```json "params": { "name": "DKS 1802-EPD", - "star": "*", - "hash": "#", + "ack": "*", + "alt": "#", "end": "0 + #", "size": 4 } @@ -53,8 +57,9 @@ Parameters define the configuration's global settings. | Parameter | Description | Required | |:-----------|:-------------------------------------------------------------------------------------------------|:---------| |`name` | The name of the configuration. | Yes | -|`star` | The key used to start, acknowledge or terminate programming steps. Most systems use the `*` key. | Yes | -|`hash` | They key used to in place of numbers when applicable. Most system use the `#` key | No | +|`ack` | The key used to acknowledge or terminate programming steps. Most systems use the `*` or `#` key. | Yes | +|`alt` | They key used to in place of numbers when applicable. DKS systems use the `#` key | No | +|`begin` | The begin programming manual sequence. For example Linear use `0` and `2` pressed together. | No | |`end` | The end programming manual sequence. For example DoorKing uses `0` and `#` pressed together. | No | |`size` | The size (number of digits) of the master code. Most systems use 4 or 5. | Yes | @@ -83,7 +88,7 @@ which would translate into: { "hint": "Phone Number", "size": 7, - "hash": true + "alt": true } ], "dtmf": "*01[MASTER],[FIELD:1]*,[FIELD:2]*" @@ -93,7 +98,7 @@ which would translate into: Step 4 is configured in the `end` [parameter](#parameters) since it only applies to manual/keypad programming. -| Elements | Description | +| Property | Description | |:---------|:--------------------------------------------------------------------------------------------------| |`title` | The title of the option. | |`fields` | See [Fields](#fields) | @@ -114,21 +119,23 @@ All are required, except `nodial` and `nosteps` which are mutually exclusive. { "hint": "Phone Number", "size": 7, - "hash": true + "alt": true } ] ``` Fields represent the data entry text fields on option screens. -| Element | Description | Required | -|:--------|:--------------------------------------------------------------------------------------------------|:---------| -|`hint` | Set the hint/label of the field. | Yes | -|`size` | Set the size of the field. | Yes | -|`min` | Set the minimum value of a numeric field. | No | -|`max` | Set the maximum value of a numeric field. | No | -|`alpha` | Set to `true` if the field is alphanumeric. | No | -|`hash` | Set to `true` if the field accept the `hash` [parameter](#parameters) value in place of a digit. | No | +| Property | Description | Required | +|:---------|:------------------------------------------------------------------------------------------------------|:---------| +|`hint` | Set the hint/label of the field. | Yes | +|`size` | Set the size of the field. | Yes | +|`minSize` | Set the minimum size of the field. If set, `size` is the maximum size of the field. | No | +|`min` | Set the minimum value of a numeric field. | No | +|`max` | Set the maximum value of a numeric field. | No | +|`alpha` | Set to `dks` or `linear` for alphanumeric fields. | No | +|`alt` | Set to `true` if the field accepts the `alt` [parameter](#parameters) value in place of a digit. | No | +|`zeros` | Set to `true` by default. Allows numeric values with leading zeros (i.e. `001`), based on the `size`. | No | #### DTMF @@ -153,5 +160,3 @@ Imported configurations will be validated. While the validation is not perfect, Please sure to use a JSON editor to make creating configurations a whole lot easier. When in doubt be sure to look at the [default configurations](app/src/main/res/raw/). - - diff --git a/app/src/main/java/net/thauvin/erik/android/tesremoteprogrammer/MainActivity.kt b/app/src/main/java/net/thauvin/erik/android/tesremoteprogrammer/MainActivity.kt index 82ec8ec..18e1b52 100644 --- a/app/src/main/java/net/thauvin/erik/android/tesremoteprogrammer/MainActivity.kt +++ b/app/src/main/java/net/thauvin/erik/android/tesremoteprogrammer/MainActivity.kt @@ -64,6 +64,8 @@ class MainActivity : AppCompatActivity(), AnkoLogger { R.raw.dks_1802_epd, R.raw.dks_1812, R.raw.dks_1819, + R.raw.linear_ae_100, + R.raw.linear_ae_500, R.raw.dks_1803_1808_1810) val read_request_code = 42 @@ -348,8 +350,8 @@ class MainActivity : AppCompatActivity(), AnkoLogger { errors.append(getString(R.string.validate_invalid_param, "size")) } - if (params.star.isBlank()) { - errors.append(getString(R.string.validate_missing_param, "star")) + if (params.ack.isBlank()) { + errors.append(getString(R.string.validate_missing_param, "ack")) } if (opts.size == 0) { @@ -374,7 +376,13 @@ class MainActivity : AppCompatActivity(), AnkoLogger { errors.append(getString(R.string.validate_invalid_attr, i + 1, j + 1, "size")) } - if (!field.alpha) { + if (field.minSize == 0) { + errors.append(getString(R.string.validate_invalid_attr, i + 1, j + 1, "minSize")) + } else if (field.minSize > 0 && field.minSize > field.size) { + errors.append(getString(R.string.validate_invalid_attr, i + 1, j + 1, "minSize/size")) + } + + if (field.alpha.isBlank()) { if (field.min >= 0 || field.max >= 0) { if (field.max < 1) { errors.append(getString(R.string.validate_invalid_attr, i + 1, j + 1, "max")) @@ -393,7 +401,7 @@ class MainActivity : AppCompatActivity(), AnkoLogger { val blank = "\\0" val dtmf = Dtmf.mock(option, blank) - if (!Dtmf.validate(dtmf, "${MainActivity.Companion.PAUSE}${params.star}${params.hash}$blank")) { + if (!Dtmf.validate(dtmf, "${MainActivity.Companion.PAUSE}${params.ack}${params.alt}$blank")) { errors.append(getString(R.string.validate_invalid_dtmf, i + 1, dtmf.replace(blank, "✓"))) } } diff --git a/app/src/main/java/net/thauvin/erik/android/tesremoteprogrammer/ProgrammingActivity.kt b/app/src/main/java/net/thauvin/erik/android/tesremoteprogrammer/ProgrammingActivity.kt index f812334..91734cf 100644 --- a/app/src/main/java/net/thauvin/erik/android/tesremoteprogrammer/ProgrammingActivity.kt +++ b/app/src/main/java/net/thauvin/erik/android/tesremoteprogrammer/ProgrammingActivity.kt @@ -98,15 +98,21 @@ class ProgrammingActivity : AppCompatActivity(), AnkoLogger { val editText = editText() { hint = field.hint - if (field.alpha) { - inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS - inputFilters.add(AlphaFilter()) + if (field.alpha.isNotBlank()) { + if (field.alpha.equals(Dtmf.DKS, true)) { + inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS + inputFilters.add(AlphaFilter(Dtmf.DKS_EXTRAS)) + } else if (field.alpha.equals(Dtmf.LINEAR, true)) { + inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_CAP_SENTENCES + inputFilters.add(AlphaFilter(Dtmf.LINEAR_EXTRAS)) + } } else { inputType = InputType.TYPE_CLASS_PHONE inputFilters.add(NumberFilter("0123456789" + - if (field.hash) "${params.hash}" else "")) + if (field.alt) "${params.alt}" else "")) if (field.max != -1 && field.min != -1) { - inputFilters.add(MinMaxFilter(field.min, field.max, field.size)) + inputFilters.add( + MinMaxFilter(field.min, field.max, field.size, field.zeros)) } } @@ -148,10 +154,22 @@ class ProgrammingActivity : AppCompatActivity(), AnkoLogger { onClick { if (validateFields(fields, option)) { - val dtmf = Dtmf.build(params.master, params.star, option, fields) - if (Dtmf.validate(dtmf, "${MainActivity.PAUSE}${params.star}${params.hash}")) { + val dtmf = Dtmf.build(params.master, params.ack, option, fields) + if (Dtmf.validate(dtmf, "${MainActivity.PAUSE}${params.ack}${params.alt}")) { + val begin = if (params.begin.isNotBlank()) { + "${params.begin}${MainActivity.PAUSE}" + } else { + "" + } + + val end = if (params.end.isNotBlank()) { + "${MainActivity.PAUSE}${params.end}" + } else { + "" + } + startActivity( - StepsActivity.EXTRA_STEPS to "$dtmf${MainActivity.PAUSE}${params.end}".split(',')) + StepsActivity.EXTRA_STEPS to "$begin$dtmf$end".split(',')) } else { Snackbar.make(this@coordinatorLayout, getString(R.string.error_invalid_dtmf, dtmf), Snackbar.LENGTH_LONG).show() @@ -181,8 +199,8 @@ class ProgrammingActivity : AppCompatActivity(), AnkoLogger { imageResource = R.drawable.fab_ic_call onClick { if (validateFields(fields, option)) { - val dtmf = Dtmf.build(params.master, params.star, option, fields) - if (Dtmf.validate(dtmf, "${MainActivity.PAUSE}${params.star}${params.hash}")) { + val dtmf = Dtmf.build(params.master, params.ack, option, fields) + if (Dtmf.validate(dtmf, "${MainActivity.PAUSE}${params.ack}${params.alt}")) { ProgrammingActivityPermissionsDispatcher.callWithCheck( this@ProgrammingActivity, params.phone, dtmf) } else { @@ -214,7 +232,9 @@ class ProgrammingActivity : AppCompatActivity(), AnkoLogger { @NeedsPermission(Manifest.permission.CALL_PHONE) fun call(phone: String, dtmf: String) { - info("$phone${MainActivity.PAUSE}${MainActivity.PAUSE}$dtmf") + if (BuildConfig.DEBUG) { + info(">>> $phone${MainActivity.PAUSE}${MainActivity.PAUSE}$dtmf") + } makeCall("$phone${MainActivity.PAUSE}${MainActivity.PAUSE}$dtmf") } @@ -226,15 +246,40 @@ class ProgrammingActivity : AppCompatActivity(), AnkoLogger { v.error = getString(R.string.error_required) isValid = false } else { - val size = option.fields[i].size - if (!option.fields[i].alpha && size > 0 && (v.length() != size)) { + val size = if (option.fields[i].minSize > 0) { + option.fields[i].minSize + } else { + option.fields[i].size + } + if (option.fields[i].alpha.isBlank() && + !validateSize(v.length(), option.fields[i].minSize, option.fields[i].size)) { v.error = getString(R.string.error_invalid_size, size, resources.getQuantityString(R.plurals.error_digit, size)) isValid = false } + + if (option.fields[i].min > 0 && option.fields[i].max > 0) { + try { + if (v.text.toString().toInt() !in IntRange(option.fields[i].min, option.fields[i].max)) { + v.error = getString(R.string.error_invalid) + isValid = false + } + } catch (nfe: NumberFormatException) { + v.error = getString(R.string.error_invalid) + isValid = false + } + } } } return isValid } + + fun validateSize(size: Int, min: Int, max: Int): Boolean { + if (min > 0) { + return size in IntRange(min, max) + } else { + return size == max + } + } } diff --git a/app/src/main/java/net/thauvin/erik/android/tesremoteprogrammer/filters/AlphaFilter.kt b/app/src/main/java/net/thauvin/erik/android/tesremoteprogrammer/filters/AlphaFilter.kt index ae6a2d3..4f66cce 100644 --- a/app/src/main/java/net/thauvin/erik/android/tesremoteprogrammer/filters/AlphaFilter.kt +++ b/app/src/main/java/net/thauvin/erik/android/tesremoteprogrammer/filters/AlphaFilter.kt @@ -22,6 +22,12 @@ import android.text.SpannableStringBuilder import android.text.Spanned class AlphaFilter : InputFilter { + private val extras: String + + constructor(extras: String) { + this.extras = extras + } + override fun filter(source: CharSequence, start: Int, end: Int, @@ -32,7 +38,7 @@ class AlphaFilter : InputFilter { if (source is SpannableStringBuilder) { for (i in end - 1 downTo start) { val c = source[i] - if (!c.isLetterOrDigit() && !Character.isSpaceChar(c)) { + if (!c.isLetterOrDigit() && !extras.contains(c)) { source.delete(i, i + 1) } } @@ -41,7 +47,7 @@ class AlphaFilter : InputFilter { val sb = StringBuilder() for (i in start..end - 1) { val c = source[i] - if (c.isLetterOrDigit() || Character.isSpaceChar(c)) { + if (c.isLetterOrDigit() || extras.contains(c)) { sb.append(c.toUpperCase()) } } diff --git a/app/src/main/java/net/thauvin/erik/android/tesremoteprogrammer/models/Field.kt b/app/src/main/java/net/thauvin/erik/android/tesremoteprogrammer/models/Field.kt index 8974673..640bbcd 100644 --- a/app/src/main/java/net/thauvin/erik/android/tesremoteprogrammer/models/Field.kt +++ b/app/src/main/java/net/thauvin/erik/android/tesremoteprogrammer/models/Field.kt @@ -22,8 +22,10 @@ import android.os.Parcelable import java.io.Serializable data class Field(var hint: String, - var alpha: Boolean, - val hash: Boolean, + var alpha: String, + val alt: Boolean, + var zeros: Boolean, + var minSize: Int, var size: Int, var min: Int, var max: Int) : Parcelable, Serializable { @@ -37,22 +39,26 @@ data class Field(var hint: String, } } - constructor() : this("", false, false, -1, -1, -1) + constructor() : this("", "", false, true, -1, -1, -1, -1) constructor(source: Parcel) : this( + source.readString(), source.readString(), 1.equals(source.readInt()), 1.equals(source.readInt()), source.readInt(), source.readInt(), + source.readInt(), source.readInt()) override fun describeContents() = 0 override fun writeToParcel(dest: Parcel?, flags: Int) { dest?.writeString(hint) - dest?.writeInt((if (alpha) 1 else 0)) - dest?.writeInt((if (hash) 1 else 0)) + dest?.writeString(alpha) + dest?.writeInt((if (alt) 1 else 0)) + dest?.writeInt((if (zeros) 1 else 0)) + dest?.writeInt(minSize) dest?.writeInt(size) dest?.writeInt(min) dest?.writeInt(max) diff --git a/app/src/main/java/net/thauvin/erik/android/tesremoteprogrammer/models/Params.kt b/app/src/main/java/net/thauvin/erik/android/tesremoteprogrammer/models/Params.kt index 23ec3db..d9142bb 100644 --- a/app/src/main/java/net/thauvin/erik/android/tesremoteprogrammer/models/Params.kt +++ b/app/src/main/java/net/thauvin/erik/android/tesremoteprogrammer/models/Params.kt @@ -25,8 +25,9 @@ data class Params(var name: String, var phone: String, var master: String, var size: Int, - var star: String, - var hash: String, + var ack: String, + var alt: String, + var begin: String, var end: String) : Parcelable, Serializable { companion object { @@ -38,7 +39,7 @@ data class Params(var name: String, } } - constructor() : this("", "", "", -1, "", "", "") + constructor() : this("", "", "", -1, "", "", "", "") constructor(source: Parcel) : this( source.readString(), @@ -47,7 +48,8 @@ data class Params(var name: String, source.readInt(), source.readString(), source.readString(), - source.readString()) + source.readString(), + source.readString()) override fun describeContents() = 0 @@ -56,8 +58,9 @@ data class Params(var name: String, dest?.writeString(phone) dest?.writeString(master) dest?.writeInt(size) - dest?.writeString(star) - dest?.writeString(hash) + dest?.writeString(ack) + dest?.writeString(alt) + dest?.writeString(begin) dest?.writeString(end) } } \ No newline at end of file diff --git a/app/src/main/java/net/thauvin/erik/android/tesremoteprogrammer/util/Dtmf.kt b/app/src/main/java/net/thauvin/erik/android/tesremoteprogrammer/util/Dtmf.kt index 4692b93..34a5296 100644 --- a/app/src/main/java/net/thauvin/erik/android/tesremoteprogrammer/util/Dtmf.kt +++ b/app/src/main/java/net/thauvin/erik/android/tesremoteprogrammer/util/Dtmf.kt @@ -26,74 +26,141 @@ class Dtmf { companion object { val DTMF_MASTER = "[MASTER]" val DTMF_FIELD = "[FIELD:%1\$d]" + val DKS = "dks" + val LINEAR = "linear" + val DKS_EXTRAS = " " + val LINEAR_EXTRAS = ", -." - private fun alphaToDigits(text: String, star: String): String { + private fun linearAlphaToDigits(text: String): String { val result = StringBuffer() text.toUpperCase().forEach { c -> when (c) { - 'A' -> result.append("2$star${MainActivity.PAUSE}") - 'B' -> result.append("22$star${MainActivity.PAUSE}") - 'C' -> result.append("2222$star${MainActivity.PAUSE}") + 'A' -> result.append("1${MainActivity.PAUSE}") + 'B' -> result.append("11${MainActivity.PAUSE}") + 'C' -> result.append("111${MainActivity.PAUSE}") - 'D' -> result.append("3$star${MainActivity.PAUSE}") - 'E' -> result.append("33$star${MainActivity.PAUSE}") - 'F' -> result.append("333$star${MainActivity.PAUSE}") + 'D' -> result.append("2${MainActivity.PAUSE}") + 'E' -> result.append("22${MainActivity.PAUSE}") + 'F' -> result.append("222${MainActivity.PAUSE}") - 'G' -> result.append("4$star${MainActivity.PAUSE}") - 'H' -> result.append("44$star${MainActivity.PAUSE}") - 'I' -> result.append("444$star${MainActivity.PAUSE}") + 'G' -> result.append("3${MainActivity.PAUSE}") + 'H' -> result.append("33${MainActivity.PAUSE}") + 'I' -> result.append("333${MainActivity.PAUSE}") - 'J' -> result.append("5$star${MainActivity.PAUSE}") - 'K' -> result.append("55$star${MainActivity.PAUSE}") - 'L' -> result.append("555$star${MainActivity.PAUSE}") + 'J' -> result.append("4${MainActivity.PAUSE}") + 'K' -> result.append("44${MainActivity.PAUSE}") + 'L' -> result.append("444${MainActivity.PAUSE}") - 'M' -> result.append("6$star${MainActivity.PAUSE}") - 'N' -> result.append("66$star${MainActivity.PAUSE}") - 'O' -> result.append("666$star${MainActivity.PAUSE}") + 'M' -> result.append("5${MainActivity.PAUSE}") + 'N' -> result.append("55${MainActivity.PAUSE}") + 'O' -> result.append("555${MainActivity.PAUSE}") - 'P' -> result.append("7$star${MainActivity.PAUSE}") - 'Q' -> result.append("77$star${MainActivity.PAUSE}") - 'R' -> result.append("777$star${MainActivity.PAUSE}") - 'S' -> result.append("7777$star${MainActivity.PAUSE}") + 'P' -> result.append("6${MainActivity.PAUSE}") + 'Q' -> result.append("66${MainActivity.PAUSE}") + 'R' -> result.append("666${MainActivity.PAUSE}") - 'T' -> result.append("8$star${MainActivity.PAUSE}") - 'U' -> result.append("88$star${MainActivity.PAUSE}") - 'V' -> result.append("888$star${MainActivity.PAUSE}") + 'S' -> result.append("7${MainActivity.PAUSE}") + 'T' -> result.append("77${MainActivity.PAUSE}") + 'U' -> result.append("777${MainActivity.PAUSE}") - 'W' -> result.append("9$star${MainActivity.PAUSE}") - 'X' -> result.append("99$star${MainActivity.PAUSE}") - 'Y' -> result.append("999$star${MainActivity.PAUSE}") - 'Z' -> result.append("9999$star${MainActivity.PAUSE}") + 'V' -> result.append("8${MainActivity.PAUSE}") + 'W' -> result.append("88${MainActivity.PAUSE}") + 'X' -> result.append("888${MainActivity.PAUSE}") - '0' -> result.append("0$star${MainActivity.PAUSE}") - '1' -> result.append("11$star${MainActivity.PAUSE}") - '2' -> result.append("2222$star${MainActivity.PAUSE}") - '3' -> result.append("3333$star${MainActivity.PAUSE}") - '4' -> result.append("4444$star${MainActivity.PAUSE}") - '5' -> result.append("5555$star${MainActivity.PAUSE}") - '6' -> result.append("6666$star${MainActivity.PAUSE}") - '7' -> result.append("77777$star${MainActivity.PAUSE}") - '8' -> result.append("8888$star${MainActivity.PAUSE}") - '9' -> result.append("99999$star${MainActivity.PAUSE}") + 'Y' -> result.append("9${MainActivity.PAUSE}") + 'Z' -> result.append("99${MainActivity.PAUSE}") + ',' -> result.append("999${MainActivity.PAUSE}") - ' ' -> result.append("1$star${MainActivity.PAUSE}") + '0' -> result.append("0000${MainActivity.PAUSE}") + '1' -> result.append("1111${MainActivity.PAUSE}") + '2' -> result.append("2222${MainActivity.PAUSE}") + '3' -> result.append("3333${MainActivity.PAUSE}") + '4' -> result.append("4444${MainActivity.PAUSE}") + '5' -> result.append("5555${MainActivity.PAUSE}") + '6' -> result.append("6666${MainActivity.PAUSE}") + '7' -> result.append("7777${MainActivity.PAUSE}") + '8' -> result.append("8888${MainActivity.PAUSE}") + '9' -> result.append("9999${MainActivity.PAUSE}") + + ' ' -> result.append("0${MainActivity.PAUSE}") + '-' -> result.append("00${MainActivity.PAUSE}") + '.' -> result.append("0000${MainActivity.PAUSE}") + } + } + return result.toString() + } + + private fun dksAlphaToDigits(text: String, ack: String): String { + val result = StringBuffer() + + text.toUpperCase().forEach { c -> + when (c) { + 'A' -> result.append("2$ack${MainActivity.PAUSE}") + 'B' -> result.append("22$ack${MainActivity.PAUSE}") + 'C' -> result.append("222$ack${MainActivity.PAUSE}") + + 'D' -> result.append("3$ack${MainActivity.PAUSE}") + 'E' -> result.append("33$ack${MainActivity.PAUSE}") + 'F' -> result.append("333$ack${MainActivity.PAUSE}") + + 'G' -> result.append("4$ack${MainActivity.PAUSE}") + 'H' -> result.append("44$ack${MainActivity.PAUSE}") + 'I' -> result.append("444$ack${MainActivity.PAUSE}") + + 'J' -> result.append("5$ack${MainActivity.PAUSE}") + 'K' -> result.append("55$ack${MainActivity.PAUSE}") + 'L' -> result.append("555$ack${MainActivity.PAUSE}") + + 'M' -> result.append("6$ack${MainActivity.PAUSE}") + 'N' -> result.append("66$ack${MainActivity.PAUSE}") + 'O' -> result.append("666$ack${MainActivity.PAUSE}") + + 'P' -> result.append("7$ack${MainActivity.PAUSE}") + 'Q' -> result.append("77$ack${MainActivity.PAUSE}") + 'R' -> result.append("777$ack${MainActivity.PAUSE}") + 'S' -> result.append("7777$ack${MainActivity.PAUSE}") + + 'T' -> result.append("8$ack${MainActivity.PAUSE}") + 'U' -> result.append("88$ack${MainActivity.PAUSE}") + 'V' -> result.append("888$ack${MainActivity.PAUSE}") + + 'W' -> result.append("9$ack${MainActivity.PAUSE}") + 'X' -> result.append("99$ack${MainActivity.PAUSE}") + 'Y' -> result.append("999$ack${MainActivity.PAUSE}") + 'Z' -> result.append("9999$ack${MainActivity.PAUSE}") + + '0' -> result.append("0$ack${MainActivity.PAUSE}") + '1' -> result.append("11$ack${MainActivity.PAUSE}") + '2' -> result.append("2222$ack${MainActivity.PAUSE}") + '3' -> result.append("3333$ack${MainActivity.PAUSE}") + '4' -> result.append("4444$ack${MainActivity.PAUSE}") + '5' -> result.append("5555$ack${MainActivity.PAUSE}") + '6' -> result.append("6666$ack${MainActivity.PAUSE}") + '7' -> result.append("77777$ack${MainActivity.PAUSE}") + '8' -> result.append("8888$ack${MainActivity.PAUSE}") + '9' -> result.append("99999$ack${MainActivity.PAUSE}") + + ' ' -> result.append("1$ack${MainActivity.PAUSE}") } } return result.toString() } fun build(master: String, - star: String, + ack: String, option: Option, fields: ArrayList): String { val replace = arrayListOf(Pair("$DTMF_MASTER", master)) fields.forEachIndexed { i, field -> replace.add(Pair(DTMF_FIELD.format(i + 1), - if (option.fields[i].alpha) { - alphaToDigits(field.text.toString(), star) - } else { + if (option.fields[i].alpha.equals(DKS, true)) { + dksAlphaToDigits(field.text.toString(), ack) + } else if (option.fields[i].alpha.equals(LINEAR, true)) { + linearAlphaToDigits(field.text.toString()) + } + else { field.text.toString() })) } diff --git a/app/src/main/res/raw/dks_1802.json b/app/src/main/res/raw/dks_1802.json index 3c091ac..02b46f9 100644 --- a/app/src/main/res/raw/dks_1802.json +++ b/app/src/main/res/raw/dks_1802.json @@ -1,8 +1,8 @@ { "params": { "name": "DKS 1802", - "star": "*", - "hash": "#", + "ack": "*", + "alt": "#", "end": "0 + #", "size": 4 }, @@ -17,7 +17,7 @@ { "hint": "Phone Number", "size": 7, - "hash": true + "alt": true } ], "dtmf": "*01[MASTER],[FIELD:1]*,[FIELD:2]*" @@ -32,7 +32,7 @@ { "hint": "Phone Number", "size": 16, - "hash": true + "alt": true } ], "dtmf": "*01[MASTER],[FIELD:1]*,[FIELD:2]*" @@ -225,7 +225,7 @@ "size": 4, "min": 1, "max": 15, - "hash": true + "alt": true } ], "dtmf": "*24[MASTER],[FIELD:1]*,[FIELD:2]*" @@ -330,7 +330,7 @@ { "hint": "Days of Week (Sun=1, Sat=7 or #)", "size": 7, - "hash": true + "alt": true } ], "dtmf": "*35[MASTER],[FIELD:1]*,[FIELD:2]*,[FIELD:3][FIELD:4]*,[FIELD:5]*,[FIELD:6][FIELD:7]*,[FIELD:8]*,[FIELD:9]*" @@ -383,7 +383,7 @@ { "hint": "Days of Week (Sun=1..Sat=7 or #)", "size": 7, - "hash": true + "alt": true }, { "hint": "Lower 4-Digit Boundary", @@ -456,7 +456,7 @@ { "hint": "Days of Week (Sun=1..Sat=7 or #)", "size": 7, - "hash": true + "alt": true }, { "hint": "Lower 4-Digit Boundary", @@ -497,7 +497,7 @@ { "hint": "Phone Number", "size": 7, - "hash": true + "alt": true } ], "dtmf": "*41[MASTER],[FIELD:1]*,[FIELD:2]*,[FIELD:3]*" diff --git a/app/src/main/res/raw/dks_1802_epd.json b/app/src/main/res/raw/dks_1802_epd.json index a564655..140cbda 100644 --- a/app/src/main/res/raw/dks_1802_epd.json +++ b/app/src/main/res/raw/dks_1802_epd.json @@ -1,8 +1,8 @@ { "params": { "name": "DKS 1802-EPD", - "star": "*", - "hash": "#", + "ack": "*", + "alt": "#", "end": "0 + #", "size": 4 }, @@ -17,7 +17,7 @@ { "hint": "Phone Number", "size": 7, - "hash": true + "alt": true } ], "dtmf": "*01[MASTER],[FIELD:1]*,[FIELD:2]*" @@ -32,7 +32,7 @@ { "hint": "Phone Number", "size": 16, - "hash": true + "alt": true } ], "dtmf": "*01[MASTER],[FIELD:1]*,[FIELD:2]*" @@ -225,7 +225,7 @@ "size": 4, "min": 1, "max": 15, - "hash": true + "alt": true } ], "dtmf": "*24[MASTER],[FIELD:1]*,[FIELD:2]*" @@ -330,7 +330,7 @@ { "hint": "Days of Week (Sun=1, Sat=7 or #)", "size": 7, - "hash": true + "alt": true } ], "dtmf": "*35[MASTER],[FIELD:1]*,[FIELD:2]*,[FIELD:3][FIELD:4]*,[FIELD:5]*,[FIELD:6][FIELD:7]*,[FIELD:8]*,[FIELD:9]*" @@ -383,7 +383,7 @@ { "hint": "Days of Week (Sun=1..Sat=7 or #)", "size": 7, - "hash": true + "alt": true }, { "hint": "Lower 4-Digit Boundary", @@ -456,7 +456,7 @@ { "hint": "Days of Week (Sun=1..Sat=7 or #)", "size": 7, - "hash": true + "alt": true }, { "hint": "Lower 4-Digit Boundary", @@ -497,7 +497,7 @@ { "hint": "Phone Number", "size": 7, - "hash": true + "alt": true } ], "dtmf": "*41[MASTER],[FIELD:1]*,[FIELD:2]*,[FIELD:3]*" @@ -512,7 +512,7 @@ { "hint": "Name", "size": 11, - "alpha": true + "alpha": "dks" } ], "nodial": true, @@ -524,17 +524,17 @@ { "hint": "Message (Line 1)", "size": 16, - "alpha": true + "alpha": "dks" }, { "hint": "Message (Line 2)", "size": 16, - "alpha": true + "alpha": "dks" }, { "hint": "Message (Line 3)", "size": 16, - "alpha": true + "alpha": "dks" } ], "nodial": true, @@ -546,17 +546,17 @@ { "hint": "Message (Line 1)", "size": 16, - "alpha": true + "alpha": "dks" }, { "hint": "Message (Line 2)", "size": 16, - "alpha": true + "alpha": "dks" }, { "hint": "Message (Line 3)", "size": 16, - "alpha": true + "alpha": "dks" } ], "nodial": true, diff --git a/app/src/main/res/raw/dks_1803_1808_1810.json b/app/src/main/res/raw/dks_1803_1808_1810.json index 7f2c684..b50324c 100644 --- a/app/src/main/res/raw/dks_1803_1808_1810.json +++ b/app/src/main/res/raw/dks_1803_1808_1810.json @@ -1,8 +1,8 @@ { "params": { "name": "DKS 1803/1808/1810", - "star": "*", - "hash": "#", + "ack": "*", + "alt": "#", "end": "0 + #", "size": 4 }, @@ -17,7 +17,7 @@ { "hint": "Phone Number", "size": 16, - "hash": true + "alt": true } ], "dtmf": "*01[MASTER],[FIELD:1]*,[FIELD:2]*" @@ -202,7 +202,7 @@ { "hint": "Days of Week (Sun=1, Sat=7 or #)", "size": 7, - "hash": true + "alt": true } ], "dtmf": "*35[MASTER],[FIELD:1][FIELD:2]*,[FIELD:3][FIELD:4]*,[FIELD:5]*,[FIELD:6]*,[FIELD:7]*" @@ -231,7 +231,7 @@ { "hint": "Days of Week (Sun=1..Sat=7 or #)", "size": 7, - "hash": true + "alt": true }, { "hint": "Lower 4-Digit Boundary", @@ -268,7 +268,7 @@ { "hint": "Days of Week (Sun=1, Sat=7 or #)", "size": 7, - "hash": true + "alt": true }, { "hint": "Lower 5-Digit Boundary", diff --git a/app/src/main/res/raw/dks_1812.json b/app/src/main/res/raw/dks_1812.json index 18f0acc..34c9d03 100644 --- a/app/src/main/res/raw/dks_1812.json +++ b/app/src/main/res/raw/dks_1812.json @@ -1,8 +1,8 @@ { "params": { "name": "DKS 1812", - "star": "*", - "hash": "#", + "ack": "*", + "alt": "#", "end": "0 + #", "size": 4 }, @@ -113,7 +113,7 @@ { "hint": "Active Days of Week (Sun=1..Sat=7, or #)", "size": 7, - "hash": true + "alt": true } ], "dtmf": "*34[MASTER],[FIELD:1]*,[FIELD:2][FIELD:3]*,[FIELD:4]*,[FIELD:5]*" @@ -148,7 +148,7 @@ { "hint": "Active Days of Week (Sun=1..Sat=7, or #)", "size": 7, - "hash": true + "alt": true } ], "dtmf": "*35[MASTER],[FIELD:1]*,[FIELD:2][FIELD:3]*,[FIELD:4]*,[FIELD:5]*" @@ -201,7 +201,7 @@ { "hint": "Active Days of Week (Sun=1..Sat=7, or #)", "size": 7, - "hash": true + "alt": true } ], "dtmf": "*36[MASTER],[FIELD:1]*,[FIELD:2][FIELD:3][FIELD:4]*,[FIELD:5][FIELD:6][FIELD:7]*,[FIELD:8]" @@ -254,7 +254,7 @@ { "hint": "Active Days of Week (Sun=1..Sat=7, or #)", "size": 7, - "hash": true + "alt": true } ], "dtmf": "*37[MASTER],[FIELD:1]*,[FIELD:2][FIELD:3][FIELD:4]*,[FIELD:5][FIELD:6][FIELD:7]*,[FIELD:8]" @@ -283,7 +283,7 @@ { "hint": "Area Code (1..., #... or ####)", "size": 4, - "hash": true + "alt": true }, { "hint": "Phone Number", @@ -316,7 +316,7 @@ { "hint": "Area Code (1..., #... or ####)", "size": 4, - "hash": true + "alt": true }, { "hint": "Phone Number", diff --git a/app/src/main/res/raw/dks_1819.json b/app/src/main/res/raw/dks_1819.json index d7bf8fe..bc7c4ad 100644 --- a/app/src/main/res/raw/dks_1819.json +++ b/app/src/main/res/raw/dks_1819.json @@ -1,8 +1,8 @@ { "params": { "name": "DKS 1819", - "star": "*", - "hash": "#", + "ack": "*", + "alt": "#", "end": "0 + #", "size": 4 }, @@ -25,7 +25,7 @@ { "hint": "Phone Number", "size": 7, - "hash": true + "alt": true } ], "dtmf": "*01[MASTER],001*,[FIELD:1]*" @@ -44,7 +44,7 @@ "size": 4, "min": 1, "max": 15, - "hash": true + "alt": true } ], "dtmf": "*24[MASTER],[FIELD:1]*,[FIELD:2]*" @@ -61,7 +61,7 @@ { "hint": "Phone Number", "size": 7, - "hash": true + "alt": true } ], "dtmf": "*41[MASTER],001,[FIELD:1]*,[FIELD:2]*" @@ -160,7 +160,7 @@ { "hint": "Days of Week (Sun=1, Sat=7 or #)", "size": 7, - "hash": true + "alt": true } ], "dtmf": "*35[MASTER],[FIELD:1]*,[FIELD:2]*,[FIELD:3][FIELD:4]*,[FIELD:5][FIELD:6]*,[FIELD:7]*,[FIELD:8]*" diff --git a/app/src/main/res/raw/linear_ae_100.json b/app/src/main/res/raw/linear_ae_100.json new file mode 100644 index 0000000..5715a93 --- /dev/null +++ b/app/src/main/res/raw/linear_ae_100.json @@ -0,0 +1,162 @@ +{ + "params": { + "name": "Linear AE-100", + "ack": "#", + "begin": "0 + 2", + "size": 6 + }, + "opts": [ + { + "title": "Add Resident to Directory", + "fields": [ + { + "hint": "Unit Number (1..4)", + "size": 1, + "min": 1, + "max": 4 + }, + { + "hint": "Directory Code", + "size": 3 + }, + { + "hint": "Name", + "size": 16, + "alpha": "linear" + }, + { + "hint": "Phone", + "size": 14 + }, + { + "hint": "Entry Code", + "size": 4 + } + ], + "dtmf": "[FIELD:1]#,[MASTER]#,21#,[FIELD:2]#,[FIELD:3]#,[FIELD:4]#,[FIELD:5]#,[FIELD:5]#,99#" + }, + { + "title": "Modify Resident in Directory", + "fields": [ + { + "hint": "Unit Number (1..4)", + "size": 1, + "min": 1, + "max": 4 + }, + { + "hint": "Directory Code", + "size": 3 + }, + { + "hint": "Name", + "size": 16, + "alpha": "linear" + }, + { + "hint": "Phone", + "size": 14 + }, + { + "hint": "Entry Code", + "size": 4 + } + ], + "dtmf": "[FIELD:1]#,[MASTER]#,22#,[FIELD:2]#,[FIELD:3]#,[FIELD:4]#,[FIELD:5]#,99#" + }, + { + "title": "Delete Resident from Directory", + "fields": [ + { + "hint": "Unit Number (1..4)", + "size": 1, + "min": 1, + "max": 4 + }, + { + "hint": "Directory Code", + "size": 3 + } + ], + "dtmf": "[FIELD:1]#,[MASTER]#,23#,[FIELD:2]#,[FIELD:2]#,99#" + }, + { + "title": "Add Stand-Alone Entry Code", + "fields": [ + { + "hint": "Unit Number (1..4)", + "size": 1, + "min": 1, + "max": 4 + }, + { + "hint": "Entry Code", + "size": 4 + } + ], + "dtmf": "[FIELD:1]#,[MASTER]#,31#,[FIELD:2]#,[FIELD:2]#,99#" + }, + { + "title": "Delete Stand-Alone Entry Code", + "fields": [ + { + "hint": "Unit Number (1..4)", + "size": 1, + "min": 1, + "max": 4 + }, + { + "hint": "Entry Code", + "size": 4 + } + ], + "dtmf": "[FIELD:1]#,[MASTER]#,33#,[FIELD:2]#,[FIELD:2]#,99#" + }, + { + "title": "Change Welcome Display Text", + "fields": [ + { + "hint": "Unit Number (1..4)", + "size": 1, + "min": 1, + "max": 4 + }, + { + "hint": "Line 1", + "size": 16, + "alpha": "linear" + }, + { + "hint": "Line 2", + "size": 16, + "alpha": "linear" + } + ], + "dtmf": "[FIELD:1]#,[MASTER]#,57#,[FIELD:2]#,[FIELD:3]#,99#" + }, + { + "title": "Latch Access Relay", + "fields": [ + { + "hint": "Unit Number (1..4)", + "size": 1, + "min": 1, + "max": 4 + } + ], + "dtmf": "[FIELD:1]#,[MASTER]#,81#,99#" + }, + { + "title": "Release Access Relay", + "fields": [ + { + "hint": "Unit Number (1..4)", + "size": 1, + "min": 1, + "max": 4 + } + ], + "dtmf": "[FIELD:1]#,[MASTER]#,83#,99#" + } + ] +} \ No newline at end of file diff --git a/app/src/main/res/raw/linear_ae_500.json b/app/src/main/res/raw/linear_ae_500.json new file mode 100644 index 0000000..a79bacc --- /dev/null +++ b/app/src/main/res/raw/linear_ae_500.json @@ -0,0 +1,342 @@ +{ + "params": { + "name": "Linear AE-500", + "ack": "#", + "begin": "0 + 2", + "size": 6 + }, + "opts": [ + { + "title": "Add Resident to Directory", + "fields": [ + { + "hint": "Unit Number (1..4)", + "size": 1, + "min": 1, + "max": 4 + }, + { + "hint": "Directory Code", + "size": 3 + }, + { + "hint": "Name", + "size": 16, + "alpha": "linear" + }, + { + "hint": "Phone", + "size": 14 + }, + { + "hint": "Entry Code", + "size": 4 + } + ], + "dtmf": "[FIELD:1]#,[MASTER]#,21#,[FIELD:2]#,[FIELD:3]#,[FIELD:4]#,[FIELD:5]#,[FIELD:5]#,99#" + }, + { + "title": "Modify Resident in Directory", + "fields": [ + { + "hint": "Unit Number (1..4)", + "size": 1, + "min": 1, + "max": 4 + }, + { + "hint": "Directory Code", + "size": 3 + }, + { + "hint": "Name", + "size": 16, + "alpha": "linear" + }, + { + "hint": "Phone", + "size": 14 + }, + { + "hint": "Entry Code", + "size": 4 + } + ], + "dtmf": "[FIELD:1]#,[MASTER]#,22#,[FIELD:2]#,[FIELD:3]#,[FIELD:4]#,[FIELD:5]#,99#" + }, + { + "title": "Delete Resident from Directory", + "fields": [ + { + "hint": "Unit Number (1..4)", + "size": 1, + "min": 1, + "max": 4 + }, + { + "hint": "Directory Code", + "size": 3 + } + ], + "dtmf": "[FIELD:1]#,[MASTER]#,23#,[FIELD:2]#,[FIELD:2]#,99#" + }, + { + "title": "Add Entry Code", + "fields": [ + { + "hint": "Unit Number (1..4)", + "size": 1, + "min": 1, + "max": 4 + }, + { + "hint": "Entry Code", + "size": 4 + }, + { + "hint": "Relay (1=A, 2=B, 3=AB, 4=A-ON/OFF 5=B-ON/OFF)", + "size": 1, + "min": 1, + "max": 5 + }, + { + "hint": "Number of Uses", + "size": 3, + "min": 1, + "max": 250, + "nozeros": true + } + ], + "dtmf": "[FIELD:1]#,[MASTER]#,31#,[FIELD:2]#,[FIELD:2]#,[FIELD:3]#,[FIELD:4],99#" + }, + { + "title": "Modify Entry Code", + "fields": [ + { + "hint": "Unit Number (1..4)", + "size": 1, + "min": 1, + "max": 4 + }, + { + "hint": "Entry Code", + "size": 4 + }, + { + "hint": "Relay (1=A, 2=B, 3=AB, 4=A-ON/OFF 5=B-ON/OFF)", + "size": 1, + "min": 1, + "max": 5 + }, + { + "hint": "Number of Uses", + "size": 3, + "min": 1, + "max": 250, + "nozeros": true + } + ], + "dtmf": "[FIELD:1]#,[MASTER]#,32#,[FIELD:2]#,[FIELD:3]#,[FIELD:4],99#" + }, + { + "title": "Delete Entry Code", + "fields": [ + { + "hint": "Unit Number (1..4)", + "size": 1, + "min": 1, + "max": 4 + }, + { + "hint": "Entry Code", + "size": 4 + } + ], + "dtmf": "[FIELD:1]#,[MASTER]#,33#,[FIELD:2]#,[FIELD:2]#,99#" + }, + { + "title": "Change Welcome Display Text", + "fields": [ + { + "hint": "Unit Number (1..4)", + "size": 1, + "min": 1, + "max": 4 + }, + { + "hint": "Line 1", + "size": 16, + "alpha": "linear" + }, + { + "hint": "Line 2", + "size": 16, + "alpha": "linear" + } + ], + "dtmf": "[FIELD:1]#,[MASTER]#,57#,[FIELD:2]#,[FIELD:3]#,99#" + }, + { + "title": "Latch Access Relay", + "fields": [ + { + "hint": "Unit Number (1..4)", + "size": 1, + "min": 1, + "max": 4 + }, + { + "hint": "Relay Channel: [A=1, B=2)", + "size": 1, + "min": 1, + "max": 2 + } + ], + "dtmf": "[FIELD:1]#,[MASTER]#,8[FIELD:2]#,99#" + }, + { + "title": "Release Access Relay", + "fields": [ + { + "hint": "Unit Number (1..4)", + "size": 1, + "min": 1, + "max": 4 + }, + { + "hint": "Relay Channel: [A=1, B=2)", + "size": 1, + "min": 1, + "max": 2 + } + ], + "dtmf": "[FIELD:1]#,[MASTER]#,8[FIELD:2]#,99#" + }, + { + "title": "Assign Block Transmitter", + "fields": [ + { + "hint": "Unit Number (1..4)", + "size": 1, + "min": 1, + "max": 4 + }, + { + "hint": "Number of Transmitters", + "size": 3, + "min": 1, + "max": 500 + }, + { + "hint": "Facility Code (0=None..15)", + "size": 2, + "min": 0, + "max": 15 + }, + { + "hint": "First/Lowest Transmitter ID", + "size": 2, + "min": 2, + "max": 6 + } + ], + "dtmf": "[FIELD:1]#,[MASTER]#,41#,[FIELD:2]#,[FIELD:3]#,[FIELD:4]#,99#" + }, + { + "title": "Assign Single Transmitter", + "fields": [ + { + "hint": "Unit Number (1..4)", + "size": 1, + "min": 1, + "max": 4 + }, + { + "hint": "Facility Code (0=None..15)", + "size": 2, + "min": 0, + "max": 15 + }, + { + "hint": "Transmitter ID", + "size": 2, + "min": 2, + "max": 6 + } + ], + "dtmf": "[FIELD:1]#,[MASTER]#,45#,[FIELD:2]#,[FIELD:3]#,99#" + }, + { + "title": "Suspend Transmitter", + "fields": [ + { + "hint": "Unit Number (1..4)", + "size": 1, + "min": 1, + "max": 4 + }, + { + "hint": "Facility Code (0=None..15)", + "size": 2, + "min": 0, + "max": 15 + }, + { + "hint": "Transmitter ID", + "size": 2, + "min": 2, + "max": 6 + } + ], + "dtmf": "[FIELD:1]#,[MASTER]#,43#,[FIELD:2]#,[FIELD:3]#,99#" + }, + { + "title": "Activate Transmitter", + "fields": [ + { + "hint": "Unit Number (1..4)", + "size": 1, + "min": 1, + "max": 4 + }, + { + "hint": "Facility Code (0=None..15)", + "size": 2, + "min": 0, + "max": 15 + }, + { + "hint": "Transmitter ID", + "size": 2, + "min": 2, + "max": 6 + } + ], + "dtmf": "[FIELD:1]#,[MASTER]#,42#,[FIELD:2]#,[FIELD:3]#,99#" + }, + { + "title": "Delete Single Transmitter", + "fields": [ + { + "hint": "Unit Number (1..4)", + "size": 1, + "min": 1, + "max": 4 + }, + { + "hint": "Facility Code (0=None..15)", + "size": 2, + "min": 0, + "max": 15 + }, + { + "hint": "Transmitter ID", + "size": 2, + "min": 2, + "max": 6 + } + ], + "dtmf": "[FIELD:1]#,[MASTER]#,47#,[FIELD:2]#,[FIELD:3]#,99#" + } + ] +} \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9bf8da0..dc9b1c7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -12,6 +12,7 @@ Invalid DTMF: %1$s Missing or invalid fields data. Invalid: %1$d %2$s required + Invalid Required Master Code Phone Number