Hope it will be of use to someone.
I was looking at building a DIY home alarm system fully running on openHAB2 (installed on raspberry PI)
The GPIO for contacts, PIR, siren etc. and 7” wall mounted panels for Key Pads, Panic button etc., which are located around the house.
As there is already GPIO binding I’ve just focused on a widget for the keypad that can set an item state (ON/OFF) if alarm is set or not and rules to monitor GPIO state if alarm is ON.
The Widget contains Numeric Key Pad Buttons with a clear and Enter Button.
The widget links to one of your items for entered value and also has a max input length field for user’s choice this can be left empty and will ignore monitoring length. Items used are;
Below is an example of keypad updating Alarm State (for this example rule is set to 5 second delay when setting alarm ON to allow someone get out the house this can be adjusted to suit)
Here is a simple rule I am using to check if the entered code is the same as the Alarm code.
var Timer set_timer = null
rule "Alarm_Code_Entered"
when
Item Entered_Code received update
then
if (Entered_Code.state.toString==Alarm_Code.state.toString)
{
if(Alarm_State.state==ON)
{
//Turn off Alarm instantly
Alarm_State.postUpdate(OFF)
}
else if (Alarm_State.state==OFF)
{
if (set_timer != null)
{
set_timer.cancel()
set_timer = null
}
else
{
//Give user 15 seconds to get out and close door
set_timer = createTimer(now.plusSeconds(5)) [|
Alarm_State.postUpdate(ON)
set_timer = null
]
}
}
}
end
Let me know what you think also If you feel it would be worth creating a binding for a Home Alarm system tell us also.
Very nice @Murpher, thanks for sharing!
Unfortunately you can’t add Javascript code in the current state, since it can be injected externally it would be a security issue.
@Marijn_Oudijk, I’m current setting this in a startup rule, I’m away from my pi but think the rule is something like this;
rule "set password"
when
System started
then
Alarm_Code.postUpdate('1234')
Alarm_State.postUpdate('ON')
end
Currently I haven’t got round to it as my raspberry pi is on a UPS.
But if you add persistence to them two items Alarm_Code & Alarm_State (i’m looking at using mapdb “restoreOnStartup” when I get round to it), that will keep the Code/State values set on a restart.
Might not be the safest way as you put it but works for me.
I did notice that, if you limit the entering to 4 digits but you push 5 before confirming, the 4 digits are shown in the screen but the 5 digits are entered into the string for evaluation.
// Alarm Keypad
rule "KeyPad-Disarm"
when
Item AlarmArmDisarmKeypad received command 86421
then
AlarmArmDisarm.sendCommand(1)
logInfo("Security", "LOG: KeyPad Command 1 Received")
end
// Alarm Keypad
rule "KeyPad-Arm-Away"
when
Item AlarmArmDisarmKeypad received command 86422
then
AlarmArmDisarm.sendCommand(2)
logInfo("Security", "LOG: KeyPad Command 2 Received")
end
// Alarm Keypad
rule "KeyPad-Arm-Stay"
when
Item AlarmArmDisarmKeypad received command 86423
then
AlarmArmDisarm.sendCommand(3)
logInfo("Security", "LOG: KeyPad Command 3 Received")
end
So i just changed mine to the opposite of above. I added three new buttons above 1,2,3. They are Disarm, Away ,Stay. So you hit one of them first which adds that number first say 1 for disarm. Then you add your 4 digit pass so it ends up being 18642 like so…
// Alarm Keypad
rule "KeyPad-Arm-Away"
when
Item AlarmArmDisarmKeypad received command 18642
then
AlarmArmDisarm.sendCommand(2)
logInfo("Security", "LOG: KeyPad Command 2 Received")
end
I used your code to make this. This works with the alarmdecoder binding. It works much more like the physical keypad where it transmits 1 button press at a time. The stay, away, bypass and chime text changes color depending on their relative status. It also has the text output from keypad and alarm status icon. The alarm status will show 1 of 3 statuses: Ready, Not Ready, Armed.
My items file was just the example from here with the addition of the following item.
Number alarmDigit "alarmDigit" (gPanel) {alarmdecoder="SEND#1=1,2=2,3=3,4=4,5=5,6=6,7=7,8=8,9=9,10=*,0=0,11=POUND", autoupdate="false"}
Also found that this code works fine without defining settings within the custom widget.
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:
You don’t need to create the ‘alarmDigit’ item.
The widget sends each press to ‘alarmDigit’ change that to ‘vrt_Key_Press’
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’
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
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:
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
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
This can be done by adding either the line
-webkit-text-security: square;
or
-webkit-text-security: disc;
to the .mask code in the style section it should then read
.mask{
border: 1px solid #ccc;
background: #fff;
color:#333;
width:100%;
height: 45px;
-webkit-text-security: square;
}