mirror of
https://github.com/brmlab/brmdoor_libnfc.git
synced 2025-06-08 08:34:00 +02:00
Added IRC reporting functionality
This commit is contained in:
parent
ae1b31de79
commit
d67e39e88e
3 changed files with 150 additions and 9 deletions
|
@ -24,7 +24,7 @@ shows four interactions with NFC smartcards:
|
||||||
|
|
||||||
It is much more general in use than to use it as authenthicator to open door.
|
It is much more general in use than to use it as authenthicator to open door.
|
||||||
|
|
||||||
## Building
|
## Building and dependencies
|
||||||
|
|
||||||
You need just to run `make`. Additional dependencies:
|
You need just to run `make`. Additional dependencies:
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ You need just to run `make`. Additional dependencies:
|
||||||
- [python-axolotl-curve25519](https://github.com/tgalal/python-axolotl-curve25519), in Ubuntu and Debian install python-axolotl-curve25519
|
- [python-axolotl-curve25519](https://github.com/tgalal/python-axolotl-curve25519), in Ubuntu and Debian install python-axolotl-curve25519
|
||||||
- [SWIG](http://www.swig.org/)
|
- [SWIG](http://www.swig.org/)
|
||||||
- [WiringPi2 pythonic binding](https://github.com/WiringPi/WiringPi2-Python) (for switching lock on Raspberry)
|
- [WiringPi2 pythonic binding](https://github.com/WiringPi/WiringPi2-Python) (for switching lock on Raspberry)
|
||||||
|
- [python-irc](https://pypi.python.org/pypi/irc) >= 16.0, use "pip install irc", the one in repos is old
|
||||||
|
|
||||||
All dependencies except for wiring can be installed via:
|
All dependencies except for wiring can be installed via:
|
||||||
|
|
||||||
|
|
|
@ -23,3 +23,21 @@ unlocker = UnlockerWiringPi
|
||||||
# lock_pin - which pin needs to be pulled high to disengage the lock (GPIO numbering)
|
# lock_pin - which pin needs to be pulled high to disengage the lock (GPIO numbering)
|
||||||
[UnlockerWiringPi]
|
[UnlockerWiringPi]
|
||||||
lock_pin = 17
|
lock_pin = 17
|
||||||
|
|
||||||
|
[irc]
|
||||||
|
# enabled - True or False; rest of the options are not necessary if set to False
|
||||||
|
# server - IRC server to connect to
|
||||||
|
# port - IRC server port
|
||||||
|
# nick - nickname of bot
|
||||||
|
# password - password for the nick, may be omitted
|
||||||
|
# channels - space separated list of channels to join
|
||||||
|
# tls - True or False whether we should connect over TLS
|
||||||
|
# reconnect_delay - wait this many seconds until next reconnect attempt
|
||||||
|
enabled = True
|
||||||
|
server = irc.freenode.net
|
||||||
|
port = 6697
|
||||||
|
nick = bjornbot
|
||||||
|
password = whatevs
|
||||||
|
channels = #test-bjornbot
|
||||||
|
tls = True
|
||||||
|
reconnect_delay = 300
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python2
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
import ConfigParser
|
import ConfigParser
|
||||||
|
import threading
|
||||||
|
import irc.client
|
||||||
|
import ssl
|
||||||
|
import Queue
|
||||||
|
|
||||||
from binascii import hexlify
|
from binascii import hexlify
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
from nfc_smartcard import NFCDevice, NFCError
|
from nfc_smartcard import NFCDevice, NFCError
|
||||||
from brmdoor_authenticator import UidAuthenticator, YubikeyHMACAuthenthicator, DesfireEd25519Authenthicator
|
from brmdoor_authenticator import UidAuthenticator, YubikeyHMACAuthenthicator, DesfireEd25519Authenthicator
|
||||||
|
@ -46,6 +50,15 @@ class BrmdoorConfig(object):
|
||||||
self.logFile = self.config.get("brmdoor", "log_file")
|
self.logFile = self.config.get("brmdoor", "log_file")
|
||||||
self.logLevel = self.convertLoglevel(self.config.get("brmdoor", "log_level"))
|
self.logLevel = self.convertLoglevel(self.config.get("brmdoor", "log_level"))
|
||||||
self.unlocker = self.config.get("brmdoor", "unlocker")
|
self.unlocker = self.config.get("brmdoor", "unlocker")
|
||||||
|
self.useIRC = self.config.getboolean("irc", "enabled")
|
||||||
|
if self.useIRC:
|
||||||
|
self.ircServer = self.config.get("irc", "server")
|
||||||
|
self.ircPort = self.config.getint("irc", "port")
|
||||||
|
self.ircNick = self.config.get("irc", "nick")
|
||||||
|
self.ircPassword = self.config.get("irc", "password") if self.config.has_option("irc", "password") else None
|
||||||
|
self.ircChannels = self.config.get("irc", "channels").split(" ")
|
||||||
|
self.ircUseTLS = self.config.getboolean("irc", "tls")
|
||||||
|
self.ircReconnectDelay = self.config.getint("irc", "reconnect_delay")
|
||||||
|
|
||||||
def convertLoglevel(self, levelString):
|
def convertLoglevel(self, levelString):
|
||||||
"""Converts string 'debug', 'info', etc. into corresponding
|
"""Converts string 'debug', 'info', etc. into corresponding
|
||||||
|
@ -61,7 +74,7 @@ class BrmdoorConfig(object):
|
||||||
class NFCScanner(object):
|
class NFCScanner(object):
|
||||||
"""Thread reading data from NFC reader"""
|
"""Thread reading data from NFC reader"""
|
||||||
|
|
||||||
def __init__(self, config):
|
def __init__(self, config, msgQueue, ircThread):
|
||||||
"""Create worker reading UIDs from PN53x reader.
|
"""Create worker reading UIDs from PN53x reader.
|
||||||
"""
|
"""
|
||||||
self.authenticator = UidAuthenticator(config.authDbFilename)
|
self.authenticator = UidAuthenticator(config.authDbFilename)
|
||||||
|
@ -69,6 +82,8 @@ class NFCScanner(object):
|
||||||
self.desfireAuthenticator = None
|
self.desfireAuthenticator = None
|
||||||
self.unknownUidTimeoutSecs = config.unknownUidTimeoutSecs
|
self.unknownUidTimeoutSecs = config.unknownUidTimeoutSecs
|
||||||
self.lockOpenedSecs = config.lockOpenedSecs
|
self.lockOpenedSecs = config.lockOpenedSecs
|
||||||
|
self.msgQueue = msgQueue
|
||||||
|
self.ircThread = ircThread
|
||||||
|
|
||||||
unlockerClassName = config.unlocker
|
unlockerClassName = config.unlocker
|
||||||
unlockerClass = getattr(unlocker, unlockerClassName)
|
unlockerClass = getattr(unlocker, unlockerClassName)
|
||||||
|
@ -110,6 +125,16 @@ class NFCScanner(object):
|
||||||
except Exception:
|
except Exception:
|
||||||
logging.exception("Exception in main unlock thread")
|
logging.exception("Exception in main unlock thread")
|
||||||
|
|
||||||
|
def sendIrcMessage(self, msg):
|
||||||
|
"""
|
||||||
|
Send message to IRC bot. Message is dropped if bot is not connected
|
||||||
|
:param msg: message to be displayed in joined channels
|
||||||
|
"""
|
||||||
|
if not self.ircThread:
|
||||||
|
return
|
||||||
|
if self.ircThread.getConnected():
|
||||||
|
self.msgQueue.put(msg)
|
||||||
|
|
||||||
def actOnUid(self, uid_hex):
|
def actOnUid(self, uid_hex):
|
||||||
"""
|
"""
|
||||||
Do something with the UID scanned. Try to authenticate it against
|
Do something with the UID scanned. Try to authenticate it against
|
||||||
|
@ -120,6 +145,7 @@ class NFCScanner(object):
|
||||||
#direct UID match
|
#direct UID match
|
||||||
if record is not None:
|
if record is not None:
|
||||||
logging.info("Unlocking for UID %s", record)
|
logging.info("Unlocking for UID %s", record)
|
||||||
|
self.sendIrcMessage("Unlocking door")
|
||||||
self.unlocker.unlock()
|
self.unlocker.unlock()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -128,6 +154,7 @@ class NFCScanner(object):
|
||||||
|
|
||||||
if record is not None:
|
if record is not None:
|
||||||
logging.info("Unlocking after HMAC for UID %s", record)
|
logging.info("Unlocking after HMAC for UID %s", record)
|
||||||
|
self.sendIrcMessage("Unlocking door")
|
||||||
self.unlocker.unlock()
|
self.unlocker.unlock()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -136,12 +163,100 @@ class NFCScanner(object):
|
||||||
|
|
||||||
if record is not None:
|
if record is not None:
|
||||||
logging.info("Unlocking after Desfire NDEF ed25519 check for UID %s", record)
|
logging.info("Unlocking after Desfire NDEF ed25519 check for UID %s", record)
|
||||||
|
self.sendIrcMessage("Unlocking door")
|
||||||
self.unlocker.unlock()
|
self.unlocker.unlock()
|
||||||
return
|
return
|
||||||
|
|
||||||
logging.info("Unknown UID %s", uid_hex)
|
logging.info("Unknown UID %s", uid_hex)
|
||||||
|
self.sendIrcMessage("Denied unauthorized card")
|
||||||
time.sleep(self.unknownUidTimeoutSecs)
|
time.sleep(self.unknownUidTimeoutSecs)
|
||||||
|
|
||||||
|
class IrcThread(threading.Thread):
|
||||||
|
"""
|
||||||
|
Class for showing messages about lock events and denied/accepted cards
|
||||||
|
"""
|
||||||
|
def __init__(self, config, msgQueue):
|
||||||
|
"""
|
||||||
|
Create thread for IRC connection.
|
||||||
|
|
||||||
|
:param config - BrmdoorConfig object
|
||||||
|
:param msgQueue: Queue.Queue instance where we will get messages to show
|
||||||
|
"""
|
||||||
|
self.server = config.ircServer
|
||||||
|
self.port = config.ircPort
|
||||||
|
self.nick = config.ircNick
|
||||||
|
self.password = config.ircPassword
|
||||||
|
self.channels = config.ircChannels
|
||||||
|
self.useSSL = config.ircUseTLS
|
||||||
|
self.reconnectDelay = config.ircReconnectDelay
|
||||||
|
self.msgQueue = msgQueue
|
||||||
|
self.connection = None
|
||||||
|
self.reactor = None
|
||||||
|
self.connected = False
|
||||||
|
self.threadLock = threading.Lock()
|
||||||
|
|
||||||
|
threading.Thread.__init__(self)
|
||||||
|
|
||||||
|
def setConnected(self, connected):
|
||||||
|
with self.threadLock:
|
||||||
|
self.connected = connected
|
||||||
|
|
||||||
|
def getConnected(self):
|
||||||
|
with self.threadLock:
|
||||||
|
return self.connected
|
||||||
|
|
||||||
|
def connect(self):
|
||||||
|
"""
|
||||||
|
Connect to server.
|
||||||
|
:returns true if connection was successful
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
ssl_factory = irc.connection.Factory(wrapper=ssl.wrap_socket)
|
||||||
|
self.reactor = irc.client.Reactor()
|
||||||
|
self.connection = self.reactor.server().connect(
|
||||||
|
self.server,
|
||||||
|
self.port,
|
||||||
|
self.nick,
|
||||||
|
self.password,
|
||||||
|
"brmdoor-libnfc",
|
||||||
|
connect_factory=ssl_factory if self.useSSL else lambda sock: sock,
|
||||||
|
)
|
||||||
|
|
||||||
|
return True
|
||||||
|
except irc.client.ServerConnectionError, e:
|
||||||
|
logging.error("Could not connect to IRC server: %s", e)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def onConnect(self, connection, event):
|
||||||
|
for channel in self.channels:
|
||||||
|
connection.join(channel)
|
||||||
|
|
||||||
|
def onDisconnect(self, connection, event):
|
||||||
|
logging.info("Disconnected, waiting for %s seconds before reconnect", self.reconnectDelay)
|
||||||
|
self.setConnected(False)
|
||||||
|
time.sleep(self.reconnectDelay)
|
||||||
|
self.setConnected(self.connect())
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
logging.debug("Starting IRC thread")
|
||||||
|
while True:
|
||||||
|
connected = self.connect()
|
||||||
|
logging.info("IRC connected: %s", connected)
|
||||||
|
self.setConnected(connected)
|
||||||
|
self.connection.add_global_handler("welcome", partial(IrcThread.onConnect, self))
|
||||||
|
self.connection.add_global_handler("disconnect", partial(IrcThread.onDisconnect, self))
|
||||||
|
|
||||||
|
while self.getConnected():
|
||||||
|
self.reactor.process_once(timeout=5)
|
||||||
|
try:
|
||||||
|
msg = self.msgQueue.get_nowait()
|
||||||
|
for channel in self.channels:
|
||||||
|
self.connection.privmsg(channel, msg)
|
||||||
|
except Queue.Empty:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
time.sleep(self.reconnectDelay)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
@ -159,6 +274,13 @@ if __name__ == "__main__":
|
||||||
logging.basicConfig(filename=config.logFile, level=config.logLevel,
|
logging.basicConfig(filename=config.logFile, level=config.logLevel,
|
||||||
format="%(asctime)s %(levelname)s %(message)s [%(pathname)s:%(lineno)d]")
|
format="%(asctime)s %(levelname)s %(message)s [%(pathname)s:%(lineno)d]")
|
||||||
|
|
||||||
nfcScanner = NFCScanner(config)
|
ircMsgQueue = Queue.Queue()
|
||||||
|
ircThread = None
|
||||||
|
if config.useIRC:
|
||||||
|
ircThread = IrcThread(config, ircMsgQueue)
|
||||||
|
ircThread.setDaemon(True)
|
||||||
|
ircThread.start()
|
||||||
|
|
||||||
|
nfcScanner = NFCScanner(config, ircMsgQueue, ircThread)
|
||||||
nfcScanner.run()
|
nfcScanner.run()
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue