#!/usr/bin/env python3 from collections import defaultdict from xml.etree import ElementTree as ET import os import re import requests from common import * def getStrings(targetIP): """Parses out the message strings from source.""" r = requests.get('https://' + targetIP + '/html/en_EN/en_EN.js', auth=requests.auth.HTTPDigestAuth(DOOR_USERNAME, DOOR_PASSWORD), verify=False) regex = re.compile(r'([0-9]+)="([^"]*)') strings = [regex.search(s) for s in r.text.split(';') if s.startswith('localeStrings.eventDetails')] print({int(g.group(1)): g.group(2) for g in strings}) # hardcoded for less bandwidth usage eventStrings = { 1022: 'Denied Access{6} Card Not Found {3}', 1023: 'Denied Access{6} Access PIN Not Found {3}', 2020: 'Granted Access{6} {2}', 2021: 'Granted Access{6} Extended Time {2}', 2024: 'Denied Access{6} Schedule {2}', 2029: 'Denied Access{6} Wrong PIN {2}', 2036: 'Denied Access{6} Card Expired {2}', 2042: 'Denied Access{6} PIN Lockout {2}', 2043: 'Denied Access{6} Unassigned Card {3}', 2044: 'Denied Access{6} Unassigned Access PIN {3}', 2046: 'Denied Access - PIN Expired {2}', 4051: 'REX Switch Alarm', 7020: 'Time Set to: {5}', 12031: 'Granted Access{6} Manual', 12032: 'Door Unlocked', 12033: 'Door Locked', 4034: 'Alarm Acknowledged', 4035: 'Door Locked-Scheduled', 4036: 'Door Unlocked-Scheduled', 4041: 'Door Forced Alarm', 4042: 'Door Held Alarm', 4043: 'Tamper Switch Alarm', 4044: 'AC Failure', 4045: 'Battery Failure', } def formatMessage(event): att = defaultdict(str, event.attrib) eventType = int(att["eventType"]) return att["timestamp"], eventStrings[eventType].format( 'ios-' + att['ioState'], 'status-' + att['commandStatus'], att['forename'] + " " + att['surname'], att['rawCardNumber'], att['oldTime'], att['newTime'], " IN" if att['readerAddress'] == '0' else " OUT") def getMessages(doorName, doorIP): # get parameters for messages to get? # honestly not really sure why this is required, their API is confusing parXMLIn = ET.Element("VertXMessage") ET.SubElement(parXMLIn, "hid:EventMessages", attrib={"action": "LR"}) parXMLOut = doXMLRequest(doorIP, ET.tostring(parXMLIn)) ET.dump(parXMLOut) if os.path.exists("logs/" + doorName + ".xml"): # read last log tree = ET.ElementTree(None, "logs/" + doorName + ".xml") root = tree.getroot() recordCount = int(parXMLOut[0].attrib["historyRecordMarker"]) - \ int(root[0][0].attrib["recordMarker"]) else: # first run for this door root = None recordCount = 1000 if recordCount == 0: print("No records to get!") return print("Getting", recordCount, "records") # get the actual messages eventsXMLIn = ET.Element("VertXMessage") ET.SubElement(eventsXMLIn, "hid:EventMessages", attrib={"action": "LR", "recordCount": str(recordCount), "historyRecordMarker": parXMLOut[0].attrib["historyRecordMarker"], "historyTimestamp": parXMLOut[0].attrib["historyTimestamp"]}) eventsXMLOut = doXMLRequest(doorIP, ET.tostring(eventsXMLIn)) #TODO: handle modeRecords=true for index, event in enumerate(eventsXMLOut[0]): event.attrib["recordMarker"] = str(int(parXMLOut[0].attrib["historyRecordMarker"]) - index) # print(formatMessage(event)) if root is None: tree = ET.ElementTree(eventsXMLOut) else: for event in reversed(eventsXMLOut[0]): root[0].insert(0, event) tree.write("logs/" + doorName + ".xml") def main(): for doorName, doorData in config["doors"].items(): getMessages(doorName, doorData["ip"]) if __name__ == '__main__': main()