Rule for Access-Control (RFID and Doorlock)

Hi,
i want to open my frontdoor with a wiegand RFID reader. The project is based on an arduino nano, which is the wiegand-controller. Now i can save some rfid-ids inside the arduino and if the right chip is in reach of the reader, i get a signal on a arduino-pin.

This is working, but is a little bit standalone.

Next step is to send the rfid-id on the knx-bus. Then openhab can do the logic, who is allowed to go in.

Can someone help me with the rule?

Do i have to make a “if” for every known number?

Example:

rule "access-control"
when
     item rfid-code received update
then
     if rfid-code=="12345" {open door}
     else if rfid-code=="33333" {open door}
     ... and so on

Or can i do this in a smarter way? Maybe with an extra file, which stores alle the rfid-codes?

rule "access-control"
when
     item rfid-code received update
then
     switch (rfid-code){
         case 12345: {open door}
         case 33333: {open door}
       }

Perhaps its possible to Store all Codes in 1 Array … and than search in the Array when Update Received …

Ok, thanks.

Will give this a try.

I’d be tempted to create a Group and add String items to it then cycle the group for validation:

Group grp_Users_RFIDs "User RFIDs" <group>

String usr_RFID_01 "RFID 01 [%s]" (grp_Users_RFIDs)
String usr_RFID_02 "RFID 02 [%s]" (grp_Users_RFIDs)
rule "access-control"
when
     item rfid-code received update
then
    grp_Users_RFIDs.allMembers.filter(user | user.state == rfid-code).forEach[ StringItem filteredItem |
logInfo("Door", "Valid RFID found: " + filteredItem.name)
      validEntry = true  {open door}
    ]
end

Then in this example

usr_RFID_01

–> 01 is the RFID Code of my RFID-Chip?

Or how can i store the RFID-Code into the String-item?

No 01 is just a sequence number for the item you can use whatever you like:

usr_RFID_Halloween

You do need to initialise the data in the String-item itself but you can do that in many ways, be it a one off rule (not ideal) or if you are using habpanel you can drop in a button and use that to send a fixed command to an item.

Ok, i understand this. So no real advantage. I can use the rule with “case …” and i have no benefit with this new example?

Only benefit i see, i have to declare the thing to do in {open door} only one time.

But advantage of the rule with (case …) is, i can use 1 special command for every rfid-token. Maybe rfid-token_01 --> open frontdoor, rfid-token_02 --> open garage, …

There are always pros and cons :slight_smile:

Putting hard coded data strings in your rule/business logic is not a good idea. Changing code always carries a risk of breaking code and that is not good if you only wanted to add another user for example.

Re your new feature request :wink: to do different things based on the rfid there are also ways to do that that are likely more flexible and data driven than hard coded. A slight change:

Group grp_Door_RFIDs "RFIDs that open door" <group>
Group grp_Garage_RFIDs "RFIDs that open garage" <group>

String usr_RFID_01 "RFID 01 [%s]" (grp_Garage_RFIDs, grp_Door_RFIDs)
String usr_RFID_02 "RFID 02 [%s]" (grp_Door_RFIDs)
rule "access-control"
when
     item rfid-code received update
then
    grp_Door_RFIDs.allMembers.filter(user | user.state == rfid-code).forEach[ StringItem filteredItem |
logInfo("Door", "Valid RFID found: " + filteredItem.name)
      validEntry = true  {open door}
    ]
    grp_Garage_RFIDs.allMembers.filter(user | user.state == rfid-code).forEach[ StringItem filteredItem |
logInfo("Garage", "Valid RFID found: " + filteredItem.name)
      validEntry = true  {open garage}
    ]
end

Your RFIDs can then belong to as many groups as you decide to create and it’s nicely data driven to create/delete users or change access.

It is your choice though in terms of what you prefer to do coding wise especially if it’s for you and you alone. If you don’t want to do groups and items then I would at the very least still do the array suggestion because although that will still end up with data in the rule file it does at least put a little degree of separation between the code and data.

Attributed to Martin Golding - Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.

Does the KNX binding support transformations? I ask because if I were implementing this with something like MQTT I would create a separate Switch or Contact to represent each RFID I care about. The actual RFID would be encoded in the MQTT binding config. So when a given RFID is presented, a Switch for that specific RFID would switch to ON or OPEN.

Next, put those Items associated with a IDs that should unlock the door into the same group and your Rule would look something like:

rule "Known RFID present"
when
    Item RFIDs received update
then
    val idsPresent = RFIDs.members.filter[id | id.state == ON]
    if(idsPresent.size == 0) {
        // no IDs present, lock the door?
    }
    else if(door.state != OPEN) { // door is not already unlocked
        // unlock the door
    }
end

With an approach like this you can add and revoke access to specific IDs simply by adding/removing Items to and from the RFIDs group. You can further do special things for individual RFIDs by creating a new Rule triggered just by that one RFID’s Item.

Personally, even if you cannot do this with the KNX binding like one could with MQTT and transformations, I would probably create a Rule to parse the RFID and sendCommand to individual Items like this anyway. Something like:

rule "rfid seen"
when
    Item rfid-code received update
then
    // Get the Item associated with this RFID
    val idItem = AllRFIDs.members.findFirst[id | id.name == "RFID_"+rfid-code.state.toString]
    if(idItem != null) idItem.sendCommand(ON)
    else logInfo("access", "Received unknown/unauthorized RFID: " + rfid-code.state.toString)
end

So with this approach, you still get to add/revoke RFIDs from the access list only now the actual ID is encoded in the Item name rather than the binding config. Then keep the rules above to actually process the events.

In the short run this is probably a little more complex to implement than a switch statement, but, like Ray, I prefer to make updates in the .items rather than .rules when all I’m doing is adding, removing, or modifying something like an access list.

The full code:

Items:

String rfid-code { knx config }

Group:Switch AllRFIDs
Group:Switch AuthRFIDs
Group:Switch UnauthRFIDs

Switch RFID_1234 (AllRFIDs,AuthRFIDs) { expire="1m,command=OFF" }
Switch RFID_5678 (AllRFIDs,AuthRFIDs) { expire="1m,command=OFF" }
Switch RFID_9012 (AllRFIDs,UnauthRFIDs) { expire="1m,command=OFF" }

Switch DoorLock { ??? }
rule "rfid seen"
when
    Item rfid-code received update
then
    // Get the Item assocaited with this RFID
    val idItem = AllRFIDs.members.findFirst[if | id.name == "RFID_"+rfid-code.state.toString]

    // Unknown ID
    if(idItem == null) logWarn("access", "Unknown RFID: " + rfid-code.state.toString)

    // Known ID
    else idItem.sendCommand(ON)
end

rule "Authorized rfid seen or left"
when
    Item AuthRFIDs received update
then
    // should we lock or unlock?
    val newState = if(AuthRFIDs.members.filter[id | id.state == ON].size > 0) ON else OFF

   if(DoorLock.state != newState) DoorLock.sendCommand(newState)
end

rule "Unauthorized rfid seen"
when
    Item UnauthRFIDs received update
then
    // generate an alert if desired or simply ignore
end

rule "Specific rfid seen"
when
    Item RFID_1234 received command ON
then
    createTimer(now.plusMinutes(1), [ | say("Welcome home Halloween!")]
end

So i don´t have to put the rfid_code into a string-item, the switch-name itself represents the rfid_code of my rfid-token?

Correct, that is what this filter does:

val idItem = AllRFIDs.members.findFirst[if | id.name == "RFID_"+rfid-code.state.toString]

You can see it clearly checks against the id.name rather than id.state.

The rest of the suggestion from Rich is more subtlety linked to the potential binding side of the items you are using with knx so well worth considering how that plays in to it as well.

I think there is a Typing Error :wink:

if | id.name -> id | id.name

You’re right there is. It’s copied out of the second section Rich put up so just propagated from there.

I think Rich does a great job at providing a lot of support for people and often types examples up on the fly so you’ll occasionally get a typo as none of us type code completely clean. Hopefully someone who needs the implementation and can deploy and test it will resubmit a checked and proofed version :ok_hand:

1 Like

Yes … Rick does a very good Job for the Community :slight_smile:

I have seen this Error because I copy this Code for another Rule in my House …

Correct.

Yes. It’s hard to type code on a phone.

1 Like

Can somebody tell me, if a nfc smartphone can send a signal to a rfid-reader?

Which rfid-reader do i have to use for this? I now have a cheap rfid-reader with wiegand-controller from aliexpress, but this can only read simple 125mhz tags. I also have a sebury glas-reader/touchpad, which can read 125mhz and also some other tags i don´t know and credit cards. This is not working with the cheap aliexpress reader.

I have seen this: https://iamrobot.de/shop/nfc-rfid-wandleser/

But is this also possible with a cheap rfid reader? How can i identify a nfc-phone? Is there a unique number saved in the phone (like in the cheap rfid-tags) or can i choose the number by my own?

I’m using ironlogic cp-z-2 (mod mf-1) wiegand/ibutton mifare reader, which is capable of reading mifare 1k-4k cards, it reads smartphone nfc codes and cheap nfc-tags, but phones do send different nfc-codes every time.

Isn’t it better to associate rfid keys to people by some kind of metadata, so the code could be used for both door opening and presence updating? Like if rfid-code assigned to user was read, then update presence status, disarm alarm system, open lock?

Four years ago there was no such things as Item metadata. And even today, you can’t access Item metadata from Rules DSL.