Add doorcontrol app for managing HID door controllers
Just read-only access to the event log dump for now
This commit is contained in:
parent
12d5c9cba5
commit
a6c531c22f
@ -39,6 +39,7 @@ INSTALLED_APPS = [
|
||||
"rentals.apps.RentalsConfig",
|
||||
"membershipworks.apps.MembershipworksConfig",
|
||||
"paperwork.apps.PaperworkConfig",
|
||||
"doorcontrol.apps.DoorControlConfig",
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
@ -75,6 +76,7 @@ WSGI_APPLICATION = "cmsmanage.wsgi.application"
|
||||
|
||||
DATABASE_ROUTERS = [
|
||||
"membershipworks.routers.MembershipWorksRouter",
|
||||
"doorcontrol.routers.DoorControlRouter",
|
||||
]
|
||||
|
||||
# Default URL to redirect to after authentication
|
||||
|
0
doorcontrol/__init__.py
Normal file
0
doorcontrol/__init__.py
Normal file
19
doorcontrol/admin.py
Normal file
19
doorcontrol/admin.py
Normal file
@ -0,0 +1,19 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from .models import HIDEvent
|
||||
|
||||
|
||||
@admin.register(HIDEvent)
|
||||
class HIDEventAdmin(admin.ModelAdmin):
|
||||
search_fields = ["description", "forename", "surname", "cardholder_id"]
|
||||
list_display = ["door_name", "timestamp", "event_type", "description", "is_red"]
|
||||
list_filter = ["door_name", "event_type"]
|
||||
|
||||
def has_add_permission(self, request, obj=None):
|
||||
return False
|
||||
|
||||
def has_change_permission(self, request, obj=None):
|
||||
return False
|
||||
|
||||
def has_delete_permission(self, request, obj=None):
|
||||
return False
|
7
doorcontrol/apps.py
Normal file
7
doorcontrol/apps.py
Normal file
@ -0,0 +1,7 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class DoorControlConfig(AppConfig):
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "doorcontrol"
|
||||
verbose_name = "Door Control"
|
99
doorcontrol/migrations/0001_initial.py
Normal file
99
doorcontrol/migrations/0001_initial.py
Normal file
@ -0,0 +1,99 @@
|
||||
# Generated by Django 4.1.3 on 2023-01-25 02:18
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = []
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="HIDEvent",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("door_name", models.CharField(db_column="doorName", max_length=64)),
|
||||
("timestamp", models.DateTimeField()),
|
||||
(
|
||||
"event_type",
|
||||
models.IntegerField(
|
||||
choices=[
|
||||
(1022, "Denied Access: Card Not Found"),
|
||||
(1023, "Denied Access Access: PIN Not Found"),
|
||||
(2020, "Granted Access"),
|
||||
(2021, "Granted Access: Extended Time"),
|
||||
(2024, "Denied Access: Schedule"),
|
||||
(2029, "Denied Access: Wrong PIN"),
|
||||
(2036, "Denied Access: Card Expired"),
|
||||
(2042, "Denied Access: PIN Lockout"),
|
||||
(2043, "Denied Access: Unassigned Card"),
|
||||
(2044, "Denied Access: Unassigned Access PIN"),
|
||||
(2046, "Denied Access: PIN Expired"),
|
||||
(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"),
|
||||
(4051, "REX Switch Alarm"),
|
||||
(7020, "Time Set To"),
|
||||
(12031, "Granted Access: Manual"),
|
||||
(12032, "Door Unlocked"),
|
||||
(12033, "Door Locked"),
|
||||
],
|
||||
db_column="eventType",
|
||||
),
|
||||
),
|
||||
("reader_address", models.IntegerField(db_column="readerAddress")),
|
||||
(
|
||||
"cardholder_id",
|
||||
models.IntegerField(
|
||||
blank=True, db_column="cardholderID", null=True
|
||||
),
|
||||
),
|
||||
(
|
||||
"command_status",
|
||||
models.BooleanField(
|
||||
blank=True, db_column="commandStatus", null=True
|
||||
),
|
||||
),
|
||||
("forename", models.TextField(blank=True, null=True)),
|
||||
("surname", models.TextField(blank=True, null=True)),
|
||||
(
|
||||
"io_state",
|
||||
models.BooleanField(blank=True, db_column="ioState", null=True),
|
||||
),
|
||||
(
|
||||
"new_time",
|
||||
models.DateTimeField(blank=True, db_column="newTime", null=True),
|
||||
),
|
||||
(
|
||||
"old_time",
|
||||
models.DateTimeField(blank=True, db_column="oldTime", null=True),
|
||||
),
|
||||
(
|
||||
"raw_card_number",
|
||||
models.CharField(
|
||||
blank=True, db_column="rawCardNumber", max_length=8, null=True
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"db_table": "hidevent",
|
||||
"ordering": ("-timestamp",),
|
||||
"managed": False,
|
||||
},
|
||||
),
|
||||
]
|
0
doorcontrol/migrations/__init__.py
Normal file
0
doorcontrol/migrations/__init__.py
Normal file
122
doorcontrol/models.py
Normal file
122
doorcontrol/models.py
Normal file
@ -0,0 +1,122 @@
|
||||
from django.contrib import admin
|
||||
from django.db import models
|
||||
|
||||
|
||||
class HIDEvent(models.Model):
|
||||
class EventType(models.IntegerChoices):
|
||||
DENIED_ACCESS_CARD_NOT_FOUND = 1022, "Denied Access: Card Not Found"
|
||||
DENIED_ACCESS_ACCESS_PIN_NOT_FOUND = 1023, "Denied Access Access: PIN Not Found"
|
||||
GRANTED_ACCESS = 2020, "Granted Access"
|
||||
GRANTED_ACCESS_EXTENDED_TIME = 2021, "Granted Access: Extended Time"
|
||||
DENIED_ACCESS_SCHEDULE = 2024, "Denied Access: Schedule"
|
||||
DENIED_ACCESS_WRONG_PIN = 2029, "Denied Access: Wrong PIN"
|
||||
DENIED_ACCESS_CARD_EXPIRED = 2036, "Denied Access: Card Expired"
|
||||
DENIED_ACCESS_PIN_LOCKOUT = 2042, "Denied Access: PIN Lockout"
|
||||
DENIED_ACCESS_UNASSIGNED_CARD = 2043, "Denied Access: Unassigned Card"
|
||||
DENIED_ACCESS_UNASSIGNED_ACCESS_PIN = (
|
||||
2044,
|
||||
"Denied Access: Unassigned Access PIN",
|
||||
)
|
||||
DENIED_ACCESS_PIN_EXPIRED = 2046, "Denied Access: PIN Expired"
|
||||
ALARM_ACKNOWLEDGED = 4034, "Alarm Acknowledged"
|
||||
DOOR_LOCKED_SCHEDULED = 4035, "Door Locked: Scheduled"
|
||||
DOOR_UNLOCKED_SCHEDULED = 4036, "Door Unlocked: Scheduled"
|
||||
DOOR_FORCED_ALARM = 4041, "Door Forced Alarm"
|
||||
DOOR_HELD_ALARM = 4042, "Door Held Alarm"
|
||||
TAMPER_SWITCH_ALARM = 4043, "Tamper Switch Alarm"
|
||||
AC_FAILURE = 4044, "AC Failure"
|
||||
BATTERY_FAILURE = 4045, "Battery Failure"
|
||||
REX_SWITCH_ALARM = 4051, "REX Switch Alarm"
|
||||
TIME_SET_TO = 7020, "Time Set To"
|
||||
GRANTED_ACCESS_MANUAL = 12031, "Granted Access: Manual"
|
||||
DOOR_UNLOCKED = 12032, "Door Unlocked"
|
||||
DOOR_LOCKED = 12033, "Door Locked"
|
||||
|
||||
door_name = models.CharField(max_length=64, db_column="doorName")
|
||||
timestamp = models.DateTimeField()
|
||||
event_type = models.IntegerField(db_column="eventType", choices=EventType.choices)
|
||||
reader_address = models.IntegerField(db_column="readerAddress")
|
||||
cardholder_id = models.IntegerField(blank=True, null=True, db_column="cardholderID")
|
||||
command_status = models.BooleanField(
|
||||
blank=True, null=True, db_column="commandStatus"
|
||||
)
|
||||
forename = models.TextField(blank=True, null=True)
|
||||
surname = models.TextField(blank=True, null=True)
|
||||
io_state = models.BooleanField(blank=True, null=True, db_column="ioState")
|
||||
new_time = models.DateTimeField(blank=True, null=True, db_column="newTime")
|
||||
old_time = models.DateTimeField(blank=True, null=True, db_column="oldTime")
|
||||
raw_card_number = models.CharField(
|
||||
max_length=8, blank=True, null=True, db_column="rawCardNumber"
|
||||
)
|
||||
|
||||
@property
|
||||
def description(self):
|
||||
"""
|
||||
Based on `Global.localeStrings` from /html/en_EN/en_EN.js
|
||||
and `function eventDataHandler` from /html/modules/hid-dashboard.js
|
||||
on a HID EDGE EVO Solo
|
||||
"""
|
||||
|
||||
name = f"{self.forename} {self.surname}"
|
||||
direction = "IN" if self.reader_address == 0 else "OUT"
|
||||
|
||||
event_types = {
|
||||
1022: f"Denied Access, {direction} Card Not Found {self.raw_card_number}",
|
||||
1023: f"Denied Access, {direction} Access PIN Not Found {self.raw_card_number}",
|
||||
2020: f"Granted Access, {direction} {name}",
|
||||
2021: f"Granted Access, {direction} Extended Time {name}",
|
||||
2024: f"Denied Access, {direction} Schedule {name}",
|
||||
2029: f"Denied Access, {direction} Wrong PIN {name}",
|
||||
2036: f"Denied Access, {direction} Card Expired {name}",
|
||||
2042: f"Denied Access, {direction} PIN Lockout {name}",
|
||||
2043: f"Denied Access, {direction} Unassigned Card {self.raw_card_number}",
|
||||
2044: f"Denied Access, {direction} Unassigned Access PIN {self.raw_card_number}",
|
||||
2046: f"Denied Access - PIN Expired {name}",
|
||||
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",
|
||||
4051: "REX Switch Alarm",
|
||||
7020: f"Time Set to: {self.new_time}",
|
||||
12031: f"Granted Access, {direction} Manual",
|
||||
12032: "Door Unlocked",
|
||||
12033: "Door Locked",
|
||||
}
|
||||
|
||||
return event_types.get(self.event_type, f"Unknown Event Type {self.event_type}")
|
||||
|
||||
@admin.display(boolean=True)
|
||||
def is_red(self):
|
||||
"""Based on `function isRedEvent` from /html/hid-global.js on a HID EDGE EVO Solo"""
|
||||
return self.event_type in [
|
||||
1022,
|
||||
1023,
|
||||
2024,
|
||||
2029,
|
||||
2036,
|
||||
2042,
|
||||
2043,
|
||||
2046,
|
||||
4041,
|
||||
4042,
|
||||
4043,
|
||||
4044,
|
||||
4045,
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.door_name} {self.timestamp} - {self.description}"
|
||||
|
||||
class Meta:
|
||||
constraints = [
|
||||
models.UniqueConstraint(
|
||||
fields=["door_name", "timestamp", "event_type"], name="unique_hidevent"
|
||||
)
|
||||
]
|
||||
managed = False
|
||||
db_table = "hidevent"
|
||||
ordering = ("-timestamp",)
|
26
doorcontrol/routers.py
Normal file
26
doorcontrol/routers.py
Normal file
@ -0,0 +1,26 @@
|
||||
class DoorControlRouter:
|
||||
app_label = "doorcontrol"
|
||||
db = "doors"
|
||||
|
||||
def db_for_read(self, model, **hints):
|
||||
if model._meta.app_label == self.app_label:
|
||||
return self.db
|
||||
return None
|
||||
|
||||
def db_for_write(self, model, **hints):
|
||||
if model._meta.app_label == self.app_label:
|
||||
return self.db
|
||||
return None
|
||||
|
||||
def allow_migrate(self, db, app_label, model_name=None, **hints):
|
||||
if db == self.db:
|
||||
return False
|
||||
return None
|
||||
|
||||
def allow_relation(self, obj1, obj2, **hints):
|
||||
if (
|
||||
obj1._meta.app_label == self.app_label
|
||||
or obj2._meta.app_label == self.app_label
|
||||
):
|
||||
return True
|
||||
return None
|
3
doorcontrol/tests.py
Normal file
3
doorcontrol/tests.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
3
doorcontrol/views.py
Normal file
3
doorcontrol/views.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
Loading…
Reference in New Issue
Block a user