Custom widget: Keypad

As per @Hawkevilcat I’ve been using the design where it transmits 1 button press at a time. In fact James thanks for the widget as it has much nicer styling than mine so I’ve adopted it to suit.

I am not using the alarmdecoder though so I have a rule to do the processing as below. It also addresses the requirement to mask the display and clear entered characters after a time out period.

You will need to create 3 items to handle the key press:
String item vrt_Key_Press **
String item vrt_Key_Entry
String item vrt_Key_Entry_Save

** You do need to use a widget like the one James has created, but it needs some small modifications to work with my rule code:

  1. You don’t need to create the ‘alarmDigit’ item.
  2. The widget sends each press to ‘alarmDigit’ change that to ‘vrt_Key_Press’
  3. I use CLEAR and ENTER rather than * and #, in the widget you’ll find it sends 10 and 11 respectively, change to send ‘CLEAR’ and ‘ENTER’
  4. I don’t use the ‘alarmPanelStatusAway’ item that is part of the alarmdecoder so the I’ve stripped the text out from below the numbers. Each to there own though :slight_smile:
  5. At the top change ‘alarmPanelDisplay’ to ‘vrt_Key_Entry’

To handle the validation of any code entered I have a group of ‘users’. If you want to do a different validation then you can change the allMembers.filter lines and adjust to suit your own item check, plenty of examples above.

My group and ‘user’ structure is simple enough though so if you want it create:
Group item grp_Users_PIN
String item usr_Yourname (and add it to the group grp_Users_PIN)

The rule file does also deal with:
vrt_Alarm_Message - simple string item I display to the user, not on the widget but you can add if you want it on the widget or elsewhere/nowhere
vrt_Alarm_State - happens to also be a string item but has a whole host of stuff behind it. Basically only used when a valid code is entered so it’s the easy bit you need to customise to suit your environment.

That’s about it once you’ve initialised your user(s).

Future nice to do’s:

  1. I’d really like to encrypt the PINs rather than store them in clear text. One way salted hash sort of thing, any sample code or suggestions welcome :wink:
  2. Maybe identify user levels so that the alarm can be reset/cleared from any state, admins basically.
// Monitors for key press and reset cache after timeout
var Timer alarmKeyResetTimer = null

rule "Alarm key press system started reset."
when
  System started
then
  vrt_Key_Entry.postUpdate("")
  vrt_Key_Entry_Save.postUpdate("")
end

rule "Alarm key press..."
when
  Item vrt_Key_Press received update
then
logDebug("Alarm", "keyPress state: " + vrt_Key_Press.state)

  if (vrt_Key_Entry.state == null || vrt_Key_Entry_Save.state == null) {
    vrt_Key_Entry.postUpdate("")
    vrt_Key_Entry_Save.postUpdate("")
  }

  if (vrt_Key_Press.state == "CLEAR") {
    // Clear pressed (some like to use *)
    vrt_Key_Entry.postUpdate("")
    vrt_Key_Entry_Save.postUpdate("")
  } else if (vrt_Key_Press.state == "ENTER") {
    // Enter pressed (some like to use #)

    var Boolean validPIN = new Boolean(false)
    grp_Users_PIN.allMembers.filter(user | user.state == vrt_Key_Entry_Save.state).forEach[ StringItem filteredItem |
logInfo("Alarm", "Valid alarm code entered by user: " + filteredItem.name)
      validPIN = true
    ]

    if (validPIN == true) {
      vrt_Alarm_Message.postUpdate("Code OK")
      // Code validated there fore disarm the alarm
      if (vrt_Alarm_State.state == "ARMING" || vrt_Alarm_State.state == "ARMED" || vrt_Alarm_State.state == "DISARMING") {
        vrt_Alarm_State.sendCommand("DISARMED")
      }
    } else {
      vrt_Alarm_Message.postUpdate("Incorrect code")
    }

    vrt_Key_Entry.postUpdate("")
    vrt_Key_Entry_Save.postUpdate("")
  } else {
    // Received a key press, capture it
    vrt_Key_Entry_Save.postUpdate(vrt_Key_Entry_Save.state.toString() + vrt_Key_Press.state.toString())
    var String tempStr = vrt_Key_Entry.state.toString()

    // These two lines allow a briefly display of the entered character
    // I've taken them out because you'd need to make the whole routine thread safe to really use them
    //vrt_Key_Entry.postUpdate(tempStr + vrt_Key_Press.state.toString())
    //Thread::sleep(300)

    vrt_Key_Entry.postUpdate(tempStr + "*")
  }

  // Clear any existing timer
  alarmKeyResetTimer?.cancel
  alarmKeyResetTimer = null

  // If any text in the buffer then set a timer to clear it if no further presses
  if (vrt_Key_Press.state != null && vrt_Key_Press.state.toString().length() > 0) {
    alarmKeyResetTimer = createTimer(now.plusSeconds(20), [ | 
      alarmKeyResetTimer = null
//logDebug("Alarm", "Reset alarm key pad entry")
      vrt_Key_Entry.postUpdate("")
      vrt_Key_Entry_Save.postUpdate("")
    ])
  }

end


1 Like