Hi,
I’m happy to share my rules. However, I’m not convinced it would help you. In my case, the keypad values do not need to be concatenated. Every individual keypress is send as a separate command to my alarm system.
This is the endresult in BasicUI:
Sitemap definition:
Text label="Alarm" icon="shield" {
Switch item=Tex_QuickArm label="Alarm" mappings=[ON="Nu inschakelen"] visibility=[Tex_QuickArm_visibility==ON]
Text item=gAllecontacten label="[%d contacten staan open]" icon="window" visibility=[Tex_QuickArm_visibility==OFF] {
Text item=Tex_Zone_Z001 visibility=[Tex_Zone_Z001!=0]
Text item=Tex_Zone_Z002 visibility=[Tex_Zone_Z002!=0]
Text item=Tex_Zone_Z003 visibility=[Tex_Zone_Z003!=0]
Text item=Tex_Zone_Z004 visibility=[Tex_Zone_Z004!=0]
Text item=Tex_Zone_Z005 visibility=[Tex_Zone_Z005!=0]
Text item=Tex_Zone_Z006 visibility=[Tex_Zone_Z006!=0]
Text item=Tex_Zone_Z007 visibility=[Tex_Zone_Z007!=0]
Text item=Tex_Zone_Z008 visibility=[Tex_Zone_Z008!=0]
Text item=Tex_Zone_Z009 visibility=[Tex_Zone_Z009!=0]
Text item=Tex_Zone_Z010 visibility=[Tex_Zone_Z010!=0]
}
Frame label="Alarm status" {
Text item=Tex_Alarm_Status label="Het alarm is" valuecolor=[0="green", 1="red", 2="orange", 3="red", 4="red"]
Text item=Tex_Alarm_Status_Update label="op"
Text item=Tex_Alarm_ArmDisarmUser label="door de gebruiker"
}
Frame label="Alarm klavier" {
Text item=Tex_Alarm_Textline1 label="Alarm scherm"
Text item=Tex_Alarm_Textline2 label="" icon="none"
Switch item=Tex_Alarm_Keypad label="Alarm klavier" icon="alarm" mappings=[KEY1="1", KEY2="2", KEY3="3"] visibility=[Tex_Alarm_Keypad_visibility==ON]
Switch item=Tex_Alarm_Keypad label="" icon="none" mappings=[KEY4="4", KEY5="5", KEY6="6"] visibility=[Tex_Alarm_Keypad_visibility==ON]
Switch item=Tex_Alarm_Keypad label="" icon="none" mappings=[KEY7="7", KEY8="8", KEY9="9"] visibility=[Tex_Alarm_Keypad_visibility==ON]
Switch item=Tex_Alarm_Keypad label="" icon="none" mappings=[KEYN="❌", KEY0="0", KEYY="✔️"] visibility=[Tex_Alarm_Keypad_visibility==ON]
// Selection item=Tex_Alarm_Keypad label="Alarm klavier" icon="alarm" mappings=[KEYO="Weglaten", KEYC="Deurbel", KEYP="Part", KEYA="Area", KEYR="Reset", KEYU="Pijl omhoog", KEYD="Pijl omlaag", KEYM="Menu", KEYF="Brandgevaar", "KEY+"="Medisch gevaar", KEYH="Keypad PA"] visibility=[Tex_Alarm_Keypad_visibility==ON]
}
Frame label="Historiek" {
Text item=Tex_History_01 icon="none"
Text item=Tex_History_02 icon="none"
Text item=Tex_History_03 icon="none"
Text item=Tex_History_04 icon="none"
Text item=Tex_History_05 icon="none"
Text item=Tex_History_06 icon="none"
Text item=Tex_History_07 icon="none"
Text item=Tex_History_08 icon="none"
Text item=Tex_History_09 icon="none"
Text item=Tex_History_10 icon="none"
}
Frame label="Status Alarmcontacten" {
Group item=gDeur
Group item=gRamen
Group item=gRaamcontact
Group item=gGlasbreuk
}
Some rules I use to drive the sitemap:
// ***************************
// Arm alarm by one button
// ***************************
rule "Quick Arm"
// Arm the alarm by a single switch. It automatically logs on to the alarm using code 9999 and sets the alarm to "armed". First it checks if certain doors/windows are open.
// Your alarm system requires a dummy user (with code 9999) to arm the alarm. Obviously, this user is not allowed to do anything else (disarm, change settings, view status,...).
// Please verify if your "quick arm user" is not able to disarm your alarm.
when
Item Tex_QuickArm changed
then
if (Tex_QuickArm.state == ON) {
if((gAlarmcontact.members.filter(s | s.state != 0).size == 0) && (Tex_Alarm_Status.state == 0)) {
// If this is "true", that means all relevant contacts (windows, doors) are closed and the alarm is currently disarmed. Now we can proceed with arming the alarm.
// 500ms sleep is introduced between each button press, so the Texecom has time to process the UDP packets in the right order.
// The get LSTATUS is temporarily turned off. I think Texecom gets confused if we ask LSTATUS while keying in a code.
sendCommand(Tex_Get_LSTATUS, OFF)
Thread::sleep(500)
sendCommand(Tex_UDP_Send, "KEY9")
Thread::sleep(500)
sendCommand(Tex_UDP_Send, "KEY9")
Thread::sleep(500)
sendCommand(Tex_UDP_Send, "KEY9")
Thread::sleep(500)
sendCommand(Tex_UDP_Send, "KEY9")
Thread::sleep(1000)
sendCommand(Tex_UDP_Send, "KEYY")
// Sometimes the first KEYY is not processed/received by Texecom, that's why we send it twice (it doesn't harm either)
Thread::sleep(750)
sendCommand(Tex_UDP_Send, "KEYY")
Thread::sleep(250)
sendCommand(Tex_Get_LSTATUS, ON)
// Put the alarm in exit mode
postUpdate(Tex_Alarm_Status, "2")
postUpdate(Tex_QuickArm, OFF)
} else {
logInfo("Alarm","Snel wapenen gefaald wegens openstaande contacten of het alarm niet in de juiste status")
postUpdate(Tex_QuickArm, OFF)
}
} else {
/* Do Nothing - luckily there is no one-button-disarm */
}
end
rule "Quick Arm Visibility"
// Determines if the "quick arm" option is visible or not
when
Item gAlarmcontact changed or
Item Tex_Alarm_Status changed
then
if((gAlarmcontact.members.filter(s | s.state != 0).size == 0) && (Tex_Alarm_Status.state == 0)) {
// If this is "true", quick arm can be visible
sendCommand(Tex_QuickArm_visibility, ON)
} else {
sendCommand(Tex_QuickArm_visibility, OFF)
}
end
// ***************************
// Limit access to alarm keypad
// ***************************
rule "Limit access to alarm keypad"
// After 20 key presses, there might be someone trying out various codes. The keypad will become unavailable for 5 minutes.
when
Item Tex_Alarm_Keypad received update
then
var teller = Tex_Alarm_Keypad_counter.state as DecimalType + 1
postUpdate(Tex_Alarm_Keypad_counter, teller)
if (Tex_Alarm_Keypad_counter.state < 21) {
sendCommand(Tex_UDP_Send, Tex_Alarm_Keypad.state.toString)
}
else {
sendCommand(Tex_Alarm_Keypad_visibility, OFF)
logInfo("Alarm","Er zijn teveel pogingen geweest. Keypad wordt tijdelijk afgesloten")
}
end
rule "Reset access to alarm keypad"
when
Time cron "0 0/5 * 1/1 * ? *"
then
// logInfo("Alarm","Teller alarmklavier reset")
sendCommand(Tex_Alarm_Keypad_counter, "0")
sendCommand(Tex_Alarm_Keypad_visibility, ON)
end
// ***************************
// Display the history of the last 10 events
// ***************************
rule "History of last 10 events - part 1"
when
Item Tex_History_00 changed
then
postUpdate(Tex_History_10, Tex_History_09.state.toString)
postUpdate(Tex_History_09, Tex_History_08.state.toString)
postUpdate(Tex_History_08, Tex_History_07.state.toString)
postUpdate(Tex_History_07, Tex_History_06.state.toString)
postUpdate(Tex_History_06, Tex_History_05.state.toString)
postUpdate(Tex_History_05, Tex_History_04.state.toString)
postUpdate(Tex_History_04, Tex_History_03.state.toString)
postUpdate(Tex_History_03, Tex_History_02.state.toString)
postUpdate(Tex_History_02, Tex_History_01.state.toString)
postUpdate(Tex_History_01, Tex_History_00.state.toString)
Thread::sleep(100)
Tex_History_10.label = Tex_History_10.state.toString
Tex_History_09.label = Tex_History_09.state.toString
Tex_History_08.label = Tex_History_08.state.toString
Tex_History_07.label = Tex_History_07.state.toString
Tex_History_06.label = Tex_History_06.state.toString
Tex_History_05.label = Tex_History_05.state.toString
Tex_History_04.label = Tex_History_04.state.toString
Tex_History_03.label = Tex_History_03.state.toString
Tex_History_02.label = Tex_History_02.state.toString
Tex_History_01.label = Tex_History_01.state.toString
end
rule "History of last 10 events - part 2 - ramen"
when
Item gRamen received update
then
// Hack to get at the most recently changed window contact
Thread::sleep(100) // experiment with this value, the purpose is to give persistence time to save the state so lastUpdate will work properly. You may not need it at all but if the wrong button keeps coming up, make this sleep longer
var event = gRamen.members.sortBy[lastUpdate].last
var event_update = event.lastUpdate.toDateTime.toString("dd-MM-yy HH:mm")
var event_label = event.label.toString.split(" ")
var event_status = transform("MAP","windows.map",event.state.toString)
postUpdate(Tex_History_00, event_update + " - " + event_label.get(0) + " - " + event_status)
end
rule "History of last 10 events - part 3 - deuren"
when
Item gDeur received update
then
// Hack to get at the most recently changed window contact
Thread::sleep(100) // experiment with this value, the purpose is to give persistence time to save the state so lastUpdate will work properly. You may not need it at all but if the wrong button keeps coming up, make this sleep longer
var event = gDeur.members.sortBy[lastUpdate].last
var event_update = event.lastUpdate.toDateTime.toString("dd-MM-yy HH:mm")
var event_label = event.label.toString.split(" ")
var event_status = transform("MAP","tex_contacts.map",event.state.toString)
postUpdate(Tex_History_00, event_update + " - " + event_label.get(0) + " - " + event_status)
end
rule "History of last 10 events - part 4 - glasbreuk"
when
Item gGlasbreuk received update
then
// Hack to get at the most recently changed window contact
Thread::sleep(100) // experiment with this value, the purpose is to give persistence time to save the state so lastUpdate will work properly. You may not need it at all but if the wrong button keeps coming up, make this sleep longer
var event = gGlasbreuk.members.sortBy[lastUpdate].last
var event_update = event.lastUpdate.toDateTime.toString("dd-MM-yy HH:mm")
var event_label = event.label.toString.split(" ")
var event_status = transform("MAP","tex_contacts.map",event.state.toString)
postUpdate(Tex_History_00, event_update + " - " + event_label.get(0) + " - " + event_status)
end
rule "History of last 10 events - part 5 - alarm status"
when
Item Tex_Alarm_Status changed
then
var event = Tex_Alarm_Status
var event_update = event.lastUpdate.toDateTime.toString("dd-MM-yy HH:mm")
var event_status = transform("MAP","tex_alarmstatus.map",event.state.toString)
var event_user = transform("MAP","tex_alarmusers.map",Tex_Alarm_ArmDisarmUser.state.toString)
if (Tex_Alarm_Status.state != 2) {
postUpdate(Tex_History_00, event_update + " - alarm " + event_status + " door " + event_user)
}
end
Let me know if you have questions…