Compare commits

...

10 Commits

Author SHA1 Message Date
Cole Nicholas e080aba000 Add Docker containerization and Raspberry Pi support
- Add Dockerfile with multi-architecture support (AMD64/ARM64/ARMv7)
- Add docker-compose.yml with continuous and manual run options
- Add comprehensive Docker documentation (README-DOCKER.md)
- Add CLAUDE.md for future development guidance
- Update README.md with Docker deployment and UniFi Protect future goals
- Configure individual door device support (vs door groups)
- Add Raspberry Pi compatibility and deployment instructions
- Include memory limits for Pi optimization

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-27 10:48:55 -06:00
Keith Herrington ec9f997c73
Update README.md 2024-12-11 12:27:40 -08:00
Keith Herrington dd9b242876
Update README.md 2024-12-11 12:27:15 -08:00
Keith Herrington c2adacf7d8
Update README.md 2024-12-11 12:26:11 -08:00
Keith Herrington bf7c0fde39
Merge pull request #3 from keithah/enhance-logging-verify-ics
Enhanced visitor logging and cross-system verification closes #2
2024-12-11 12:17:50 -08:00
Keith Herrington 1c399b1b1c
Update ics_parser.py
Fixed date/datetime handling
Improved error logging
2024-12-11 12:16:17 -08:00
Keith Herrington 3b3c59d49b
xref ICS, enhanced logging+summary
added verify_across_systems function
Enhanced logging of Hostex entries
Added ICS verification
Updated summary handling
2024-12-11 12:15:31 -08:00
Keith Herrington 8976ef07c7
Update unifi_access.py - Added check_and_update_pins method, Updated generate_summary wording, Modified visitor processing logic 2024-12-11 12:14:05 -08:00
Keith Herrington 4ac3ab5e5b
Update hostex_api.py
Fixes #1. upped the reservation api call to the max, from 20 results to 100 (what happens if I have more than 100 reservations though??)
2024-12-11 11:49:57 -08:00
Keith Herrington f6ff47e47b
Update README.md 2024-09-12 09:12:21 -07:00
11 changed files with 511 additions and 38 deletions

View File

@ -0,0 +1,13 @@
{
"permissions": {
"allow": [
"Bash(cp:*)",
"Bash(python3:*)",
"Bash(pip install:*)",
"Bash(mkdir:*)",
"Bash(git add:*)"
],
"deny": [],
"ask": []
}
}

29
.dockerignore Normal file
View File

@ -0,0 +1,29 @@
# Ignore log files
*.log
logs/
# Ignore git
.git
.gitignore
# Ignore Docker files
Dockerfile
docker-compose.yml
.dockerignore
# Ignore Python cache
__pycache__/
*.pyc
*.pyo
*.pyd
.Python
# Ignore IDE files
.vscode/
.idea/
*.swp
*.swo
# Ignore OS files
.DS_Store
Thumbs.db

107
CLAUDE.md Normal file
View File

@ -0,0 +1,107 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
This is a Python automation tool that integrates UniFi Access with Airbnb/Hostex reservations, automatically creating and managing visitor access for Airbnb guests. The system fetches reservations from either Hostex API or Airbnb ICS feed, creates UniFi Access visitor accounts with PIN codes based on phone numbers, and provides cross-verification between multiple booking systems.
## Core Commands
### Development and Execution
- **Run the application**: `python3 main.py`
- **Install dependencies**: `pip install -r requirements.txt`
- **Run with verbose logging**: `python3 main.py -v`
- **Specify custom log file**: `python3 main.py -l custom.log`
- **List available door groups**: `python3 main.py --list-door-groups`
### Configuration Setup
- **Setup configuration**: `cp unifi.conf.example unifi.conf` then edit with actual credentials
- **Configuration file**: `unifi.conf` (contains API tokens, URLs, and settings)
## Architecture Overview
### Core Components
1. **main.py** - Entry point and orchestration
- Handles command-line arguments and logging setup
- Coordinates all managers and performs cross-system verification
- Implements `verify_across_systems()` for data consistency checks
2. **unifi_access.py** - UniFi Access API integration
- `UnifiAccessManager` class handles all UniFi Access operations
- Creates/deletes visitors, assigns PIN codes, manages door group access
- Processes reservations and maintains visitor lifecycle
3. **hostex_api.py** - Hostex API integration
- `HostexManager` fetches reservations from Hostex booking platform
- Simple API wrapper for reservation data
4. **ics_parser.py** - Airbnb ICS calendar parsing
- `ICSParser` processes Airbnb calendar feeds
- Extracts reservation data and PIN codes from ICS events
5. **notification.py** - Simplepush notifications
- `NotificationManager` sends status updates and alerts
- Configurable notification system for operational updates
6. **config.py** - Configuration management
- Centralizes configuration loading from `unifi.conf`
- Handles API credentials, timing settings, and feature flags
7. **utils.py** - Shared utilities
- Logging setup and configuration
### Data Flow Architecture
```
Hostex API ──┐
├──► Cross-Verification ──► UniFi Access API
ICS Feed ────┘ (Visitor Management)
Simplepush Notifications
```
### Key Integration Points
- **Reservation Sources**: Supports both Hostex API and Airbnb ICS feeds
- **PIN Code Generation**: Uses last N digits of guest phone numbers
- **Cross-Verification**: Matches reservations across systems to detect discrepancies
- **Visitor Lifecycle**: Automatically creates visitors for upcoming stays, deletes past/completed visits
- **Door Access**: Assigns visitors to configurable door groups in UniFi Access
## Configuration Structure
The `unifi.conf` file contains these key sections:
- `[UniFi]`: Access controller API endpoint and authentication token
- `[Hostex]`: Hostex API credentials (optional if using ICS only)
- `[Airbnb]`: ICS calendar feed URL (optional if using Hostex only)
- `[Door]`: Default door group ID for visitor access
- `[Visitor]`: Check-in/out times and PIN code settings
- `[Simplepush]`: Notification service configuration
- `[General]`: Logging and PIN code digit count
## Development Notes
### Dependencies
- `requests`: HTTP API calls to UniFi Access and Hostex
- `icalendar`: Parsing Airbnb ICS calendar feeds
- `urllib3`: HTTP utilities (with SSL warning suppression)
- `configparser`: Configuration file parsing
### Logging Strategy
- Comprehensive debug logging available with `-v` flag
- File-based logging for operational history
- Different log levels for console vs file output
### API Integrations
- **UniFi Access**: RESTful API for visitor management, requires authentication token
- **Hostex**: Booking platform API for reservation data
- **Simplepush**: Simple HTTP-based notification service
- **Airbnb ICS**: Calendar feed parsing for reservation extraction
### Testing and Verification
- Use `--list-door-groups` to verify UniFi Access connectivity and available door groups
- Run with `-v` flag to see detailed operation logs
- Check log files for historical operation data and error analysis

22
Dockerfile Normal file
View File

@ -0,0 +1,22 @@
# Use Python 3.11 slim image (supports amd64, arm64, arm/v7)
FROM python:3.11-slim
# Set working directory
WORKDIR /app
# Copy requirements and install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application code
COPY *.py ./
COPY unifi.conf ./
# Create logs directory
RUN mkdir -p /app/logs
# Set environment variables
ENV PYTHONUNBUFFERED=1
# Default command (can be overridden)
CMD ["python", "main.py"]

99
README-DOCKER.md Normal file
View File

@ -0,0 +1,99 @@
# Docker Setup for UniFi Access Airbnb Integration
## Quick Start
1. **Build and run continuously** (checks every 6 hours):
```bash
docker-compose up -d
```
2. **Run once manually**:
```bash
docker-compose --profile manual up unifi-access-airbnb-once
```
3. **View logs**:
```bash
docker-compose logs -f
# Or check the logs directory: ./logs/unifi_access_airbnb.log
```
## Configuration
- **Config file**: Edit `unifi.conf` on the host - it's mounted read-only into the container
- **Logs**: Persistent in `./logs/` directory
- **Timezone**: Set in docker-compose.yml (default: America/New_York)
## Automation Options
### Option 1: Continuous Container (Recommended)
```bash
docker-compose up -d
```
Runs every 6 hours automatically. Change interval by modifying `sleep 21600` in docker-compose.yml.
### Option 2: Cron Job + Manual Container
```bash
# Add to crontab to run daily at 8 AM:
0 8 * * * cd /path/to/project && docker-compose --profile manual up unifi-access-airbnb-once
```
### Option 3: Windows Task Scheduler
Create a scheduled task that runs:
```cmd
docker-compose --profile manual up unifi-access-airbnb-once
```
## Commands
- **Stop**: `docker-compose down`
- **Rebuild**: `docker-compose build`
- **View container status**: `docker-compose ps`
- **Interactive shell**: `docker-compose exec unifi-access-airbnb sh`
## Raspberry Pi Deployment
### Compatibility
**Raspberry Pi 4/5** (ARM64) - Full support
**Raspberry Pi 3B+** (ARM32/ARMv7) - Full support
**Raspberry Pi Zero 2 W** (ARM64) - Works but slower
### Pi-Specific Setup
```bash
# 1. Install Docker (if not already installed)
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker pi
# 2. Install docker-compose
sudo apt update
sudo apt install docker-compose-plugin
# 3. Clone and run your project
git clone <your-repo>
cd unifi-access-airbnb
docker-compose up -d
```
### Performance Notes
- **Pi 4/5**: Excellent performance, recommended for production
- **Pi 3B+**: Good performance, slightly slower container startup
- **Pi Zero 2**: Functional but slower, consider running less frequently
### Power Management
For reliable operation on Pi:
```yaml
# Add to docker-compose.yml services section:
restart: always
deploy:
resources:
limits:
memory: 128M
```
## Troubleshooting
- **Check logs**: `docker-compose logs unifi-access-airbnb`
- **Test config**: `docker-compose --profile manual up unifi-access-airbnb-once`
- **Network issues**: Ensure container can reach UniFi controller IP (10.0.1.20)
- **Pi-specific**: Check `docker --version` and ensure Docker is running: `sudo systemctl status docker`

120
README.md
View File

@ -1,65 +1,153 @@
# UniFi Access Airbnb Integration
This project integrates UniFi Access with Airbnb reservations, automating the process of creating and managing visitor access for your Airbnb guests. This project integrates UniFi Access with Airbnb reservations, automating the process of creating and managing visitor access for your Airbnb guests.
## Features ## Features
- Fetch reservations from Hostex API or Airbnb ICS feed - Fetch reservations from Hostex API or Airbnb ICS feed
- Create UniFi Access visitor accounts for upcoming guests - Create UniFi Access visitor accounts for upcoming guests
- Assign PIN codes to visitors based on their phone number - Assign PIN codes to visitors based on their phone number
- Automatically delete past or completed visitor accounts - Automatically delete past or completed visitor accounts
- Send notifications via Simplepush for updates and failures - Send notifications via Simplepush for updates and failures
- Cross-verify reservations between Hostex and ICS calendar
- Monitor and report discrepancies in booking data
- Detailed logging of all visitor management operations
## Prerequisites ## Prerequisites
- Python 3.7+ - Python 3.7+
- UniFi Access system - UniFi Access system
- Airbnb account with ICS feed URL or Hostex API access - Airbnb account with ICS feed URL or Hostex API access
- [Optional] Simplepush account for notifications
## Installation ## Installation
1. Clone the repository: 1. Clone the repository:
git clone https://github.com/yourusername/unifi-access-airbnb.git git clone https://github.com/keithah/unifi-access-airbnb.git
cd unifi-access-airbnb cd unifi-access-airbnb
3. Install the required packages: 2. Install the required packages:
pip install -r requirements.txt pip install -r requirements.txt
4. Copy the example configuration file and edit it with your settings: 3. Copy the example configuration file and edit it with your settings:
cp unifi.conf.example unifi.conf cp unifi.conf.example unifi.conf
nano unifi.conf nano unifi.conf
## Usage ## Usage
Run the script using: Run the script using:
python3 main.py python3 main.py
Optional arguments: Optional arguments:
- `-v` or `--verbose`: Increase output verbosity - `-v` or `--verbose`: Increase output verbosity
- `-l [LOG_FILE]` or `--log [LOG_FILE]`: Specify a log file - `-l [LOG_FILE]` or `--log [LOG_FILE]`: Specify a log file (default: unifi_access.log)
- `--list-door-groups`: List available door groups - `--list-door-groups`: List available door groups
## Configuration ## Configuration
Edit the `unifi.conf` file with your specific settings. Key sections include: Edit the `unifi.conf` file with your specific settings. Key sections include:
- `[UniFi]`: UniFi Access API settings - `[UniFi]`: UniFi Access API settings
- api_host: UniFi Access controller URL
- api_token: Authentication token
- `[Hostex]`: Hostex API settings (if used) - `[Hostex]`: Hostex API settings (if used)
- api_url: Hostex API endpoint
- api_key: Authentication key
- `[Airbnb]`: Airbnb ICS feed URL (if used) - `[Airbnb]`: Airbnb ICS feed URL (if used)
- `[Door]`: Default door group ID for visitor access - ics_url: Calendar feed URL
- `[Visitor]`: Check-in and check-out times - `[Door]`: Door access settings
- default_group_id: Default door group ID for visitor access
- `[Visitor]`: Visit timing settings
- check_in_time: Default check-in time (e.g., "14:30")
- check_out_time: Default check-out time (e.g., "11:30")
- `[General]`: General settings
- log_file: Path to log file
- pin_code_digits: Number of digits for PIN codes
- `[Simplepush]`: Notification settings
- enabled: Enable/disable notifications
- key: Simplepush key
- url: Simplepush API URL
## Logging and Monitoring
The script provides detailed logging of all operations:
- Reservation processing from Hostex and ICS
- Visitor creation and deletion in UniFi Access
- PIN code assignments and updates
- Cross-system verification results
- Errors and warnings
Logs can be viewed in real-time using the `-v` flag or reviewed in the log file.
## System Verification
The script performs several verification checks:
- Matches Hostex reservations with ICS calendar entries
- Verifies phone numbers and PIN codes across systems
- Reports discrepancies in dates or guest information
- Monitors UniFi Access visitor status
## Docker Deployment
For easy deployment and automation, this project includes Docker support:
### Quick Start with Docker
```bash
# Build and run continuously (checks every 6 hours)
docker-compose up -d
# Run once manually
docker-compose --profile manual up unifi-access-airbnb-once
# View logs
docker-compose logs -f
```
See [README-DOCKER.md](README-DOCKER.md) for complete Docker setup instructions.
### Raspberry Pi Support
The Docker container runs perfectly on Raspberry Pi (ARM64/ARM32). Simply:
```bash
# On Raspberry Pi
docker-compose up -d
```
Docker automatically pulls the correct architecture. See [README-DOCKER.md](README-DOCKER.md) for Pi-specific notes.
## Future Goals
### UniFi Protect Integration
Plans to extend this project with UniFi Protect video recording capabilities:
**Required Hardware:**
- UniFi Gateway Max (UDM-Pro Max) with built-in NVR
- OR UniFi Cloud Key Gen2+ with Protect
**Planned Features:**
- **Guest Check-in Recording**: Automatically start recording when guests arrive
- **Stay Duration Recording**: Record during entire guest stay period
- **Checkout Verification**: Capture departure recordings
- **Incident Documentation**: Archive recordings for property protection
- **Guest Privacy**: Automatic recording pause/resume based on occupancy
**Implementation Ideas:**
- Integrate with UniFi Protect API for camera control
- Coordinate recording schedules with visitor access periods
- Provide guest notification of recording periods (legal compliance)
- Archive system for easy retrieval of guest-specific recordings
**Benefits:**
- Property protection during guest stays
- Incident documentation and liability protection
- Automated recording management tied to booking system
- Enhanced security without manual camera management
This would create a complete property automation ecosystem: **Access Control + Video Security** synchronized with your Airbnb bookings.
## Contributing ## Contributing
Contributions are welcome! Please feel free to submit a Pull Request. Contributions are welcome! Please feel free to submit a Pull Request.
## License ## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
## Support
If you encounter any issues or have questions:
1. Check the logs using verbose mode
2. Review your configuration file
3. Open an issue on GitHub with relevant logs and details

36
docker-compose.yml Normal file
View File

@ -0,0 +1,36 @@
version: '3.8'
services:
unifi-access-airbnb:
build: .
container_name: unifi-access-airbnb
restart: unless-stopped
volumes:
# Mount config file (edit this on host)
- ./unifi.conf:/app/unifi.conf:ro
# Mount logs directory for persistence
- ./logs:/app/logs
environment:
- TZ=America/New_York # Set your timezone
# Resource limits for Raspberry Pi compatibility
deploy:
resources:
limits:
memory: 128M
reservations:
memory: 64M
# Run every 6 hours by default
command: sh -c "while true; do python main.py; sleep 21600; done"
# Alternative: Run once and exit (for cron-like usage)
unifi-access-airbnb-once:
build: .
container_name: unifi-access-airbnb-once
profiles:
- manual # Only start with: docker-compose --profile manual up
volumes:
- ./unifi.conf:/app/unifi.conf:ro
- ./logs:/app/logs
environment:
- TZ=America/New_York
command: python main.py -v

View File

@ -8,7 +8,7 @@ class HostexManager:
self.logger = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)
def fetch_reservations(self): def fetch_reservations(self):
url = f"{self.api_url}/reservations" url = f"{self.api_url}/reservations?limit=100"
headers = { headers = {
"Authorization": f"Bearer {self.api_key}", "Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json" "Content-Type": "application/json"

View File

@ -17,7 +17,8 @@ class ICSParser:
end = event.get("DTEND").dt end = event.get("DTEND").dt
description = event.get("DESCRIPTION", "") description = event.get("DESCRIPTION", "")
if not description: if not description:
self.logger.debug(f"Skipping event with start date {start.date()} due to missing description") start_date = start if isinstance(start, datetime.date) else start.date()
self.logger.debug(f"Skipping event with start date {start_date} due to missing description")
continue continue
pin_code = "" pin_code = ""
for line in description.split("\n"): for line in description.split("\n"):
@ -25,8 +26,8 @@ class ICSParser:
pin_code = line.split(": ")[1].strip() pin_code = line.split(": ")[1].strip()
break break
reservations.append({ reservations.append({
"check_in_date": start.date() if isinstance(start, datetime.datetime) else start, "check_in_date": start if isinstance(start, datetime.date) else start.date(),
"check_out_date": end.date() if isinstance(end, datetime.datetime) else end, "check_out_date": end if isinstance(end, datetime.date) else end.date(),
"guests": [{"name": "Airbnb Guest", "phone": pin_code}], "guests": [{"name": "Airbnb Guest", "phone": pin_code}],
"status": "accepted" "status": "accepted"
}) })

96
main.py
View File

@ -1,6 +1,7 @@
import argparse import argparse
import logging import logging
import urllib3 import urllib3
import datetime
from config import load_config from config import load_config
from unifi_access import UnifiAccessManager from unifi_access import UnifiAccessManager
from hostex_api import HostexManager from hostex_api import HostexManager
@ -11,6 +12,48 @@ from utils import setup_logging
# Suppress InsecureRequestWarning # Suppress InsecureRequestWarning
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
def verify_across_systems(hostex_reservations, ics_reservations, unifi_visitors):
discrepancies = []
today = datetime.date.today()
next_month = today + datetime.timedelta(days=30)
# Filter relevant Hostex reservations
relevant_hostex = [
r for r in hostex_reservations
if today <= datetime.datetime.strptime(r["check_in_date"], "%Y-%m-%d").date() <= next_month
and r["status"] == "accepted"
]
# Create lookup dictionaries by date range
hostex_lookup = {
(r["check_in_date"], r["check_out_date"]): r
for r in relevant_hostex
}
ics_lookup = {
(r["check_in_date"].strftime("%Y-%m-%d"), r["check_out_date"].strftime("%Y-%m-%d")): r
for r in ics_reservations
}
# Check Hostex entries against ICS
for dates, hostex_res in hostex_lookup.items():
if dates not in ics_lookup:
discrepancies.append(
f"Hostex reservation for {hostex_res['guests'][0]['name']} "
f"({dates[0]} to {dates[1]}) not found in ICS calendar"
)
else:
# Verify phone number last 4 digits match
hostex_phone = hostex_res['guests'][0].get('phone', '')[-4:]
ics_phone = ics_lookup[dates]['guests'][0].get('phone', '')[-4:]
if hostex_phone and ics_phone and hostex_phone != ics_phone:
discrepancies.append(
f"Phone number mismatch for {hostex_res['guests'][0]['name']}: "
f"Hostex: {hostex_phone}, ICS: {ics_phone}"
)
return discrepancies
def main(): def main():
parser = argparse.ArgumentParser(description="UniFi Access Visitor Management") parser = argparse.ArgumentParser(description="UniFi Access Visitor Management")
parser.add_argument('-v', '--verbose', action='store_true', help="Increase output verbosity") parser.add_argument('-v', '--verbose', action='store_true', help="Increase output verbosity")
@ -49,27 +92,60 @@ def main():
if config['use_hostex']: if config['use_hostex']:
logger.info("Fetching reservations from Hostex") logger.info("Fetching reservations from Hostex")
reservations = hostex_manager.fetch_reservations() hostex_reservations = hostex_manager.fetch_reservations()
elif config['use_ics']:
logger.info("Parsing ICS file")
reservations = ics_parser.parse_ics()
else: else:
logger.error("No valid reservation source configured") hostex_reservations = []
return
logger.info(f"Processing {len(reservations)} reservations") if config['use_ics']:
unifi_manager.process_reservations(reservations) logger.info("Parsing ICS file")
ics_reservations = ics_parser.parse_ics()
else:
ics_reservations = []
# Filter and log relevant reservations
today = datetime.date.today()
next_month = today + datetime.timedelta(days=30)
relevant_reservations = [
r for r in hostex_reservations
if today <= datetime.datetime.strptime(r["check_in_date"], "%Y-%m-%d").date() <= next_month
and r["status"] == "accepted"
]
logger.info(f"Found {len(relevant_reservations)} entries in Hostex API within the next 30 days")
for res in relevant_reservations:
guest_name = res["guests"][0]["name"] if res["guests"] else "Guest"
phone_number = res["guests"][0].get("phone", "") if res["guests"] else ""
logger.debug(
f"Hostex Guest: {guest_name}, "
f"Stay: {res['check_in_date']} to {res['check_out_date']}, "
f"Phone: {phone_number}"
)
# Verify consistency across systems
discrepancies = verify_across_systems(
hostex_reservations,
ics_reservations,
unifi_manager.fetch_visitors()
)
# Process reservations
logger.info(f"Processing {len(relevant_reservations)} reservations")
unifi_manager.process_reservations(relevant_reservations)
logger.info("Checking and updating PINs for existing visitors") logger.info("Checking and updating PINs for existing visitors")
unifi_manager.check_and_update_pins() unifi_manager.check_and_update_pins()
summary = unifi_manager.generate_summary() summary = unifi_manager.generate_summary()
if discrepancies:
summary += "\n\nDiscrepancies Found:\n" + "\n".join(discrepancies)
logger.info(summary) logger.info(summary)
total_visitors = len(unifi_manager.fetch_visitors()) total_visitors = len(unifi_manager.fetch_visitors())
logger.info(f"Total visitors remaining after cleanup: {total_visitors}") logger.info(f"Total UniFi Access visitors remaining after cleanup: {total_visitors}")
if config['simplepush_enabled'] and unifi_manager.has_changes(): if config['simplepush_enabled'] and (unifi_manager.has_changes() or discrepancies):
notification_manager.send_notification("UniFi Access Update", summary) notification_manager.send_notification("UniFi Access Update", summary)
logger.info("Simplepush notification sent") logger.info("Simplepush notification sent")
else: else:

View File

@ -16,10 +16,10 @@ class UnifiAccessManager:
self.logger.debug(f"Loaded default_door_group_id from config: {self.default_door_group_id}") self.logger.debug(f"Loaded default_door_group_id from config: {self.default_door_group_id}")
if not self.default_door_group_id: if not self.default_door_group_id:
self.logger.warning("default_door_group_id is not set in config. Attempting to fetch available door groups.") self.logger.error("default_group_id is not set in config. Please set it to your door device ID.")
self.set_default_door_group() raise ValueError("default_group_id is required in config")
else: else:
self.logger.debug(f"Initialized with default_door_group_id: {self.default_door_group_id}") self.logger.debug(f"Initialized with door device ID: {self.default_door_group_id}")
def set_default_door_group(self): def set_default_door_group(self):
door_groups = self.fetch_door_groups() door_groups = self.fetch_door_groups()
@ -50,7 +50,7 @@ class UnifiAccessManager:
"resources": [ "resources": [
{ {
"id": self.default_door_group_id, "id": self.default_door_group_id,
"type": "door_group" "type": "door"
} }
] ]
} }
@ -220,19 +220,20 @@ class UnifiAccessManager:
def generate_summary(self): def generate_summary(self):
summary = "Hostex-UniFi Access Summary:\n" summary = "Hostex-UniFi Access Summary:\n"
unchanged_names = ", ".join(self.changes['unchanged']) unchanged_names = ", ".join(self.changes['unchanged'])
summary += f"{len(self.changes['unchanged'])} existing visitors unchanged ({unchanged_names})\n" summary += f"{len(self.changes['unchanged'])} existing UniFi Access visitors unchanged ({unchanged_names})\n"
if self.changes['deleted']: if self.changes['deleted']:
deleted_names = ", ".join(self.changes['deleted']) deleted_names = ", ".join(self.changes['deleted'])
summary += f"{len(self.changes['deleted'])} visitor(s) deleted ({deleted_names})\n" summary += f"{len(self.changes['deleted'])} UniFi Access visitor(s) deleted ({deleted_names})\n"
if self.changes['added']: if self.changes['added']:
added_names = ", ".join(self.changes['added']) added_names = ", ".join(self.changes['added'])
summary += f"{len(self.changes['added'])} visitor(s) added ({added_names})\n" summary += f"{len(self.changes['added'])} UniFi Access visitor(s) added ({added_names})\n"
return summary.strip() return summary.strip()
def has_changes(self): def has_changes(self):
return bool(self.changes['added'] or self.changes['deleted']) return bool(self.changes['added'] or self.changes['deleted'])
def fetch_door_groups(self): def fetch_door_groups(self):
# Try door_groups first, then user_groups if empty
url = f"{self.api_host}/api/v1/developer/door_groups" url = f"{self.api_host}/api/v1/developer/door_groups"
headers = { headers = {
"Authorization": f"Bearer {self.api_token}", "Authorization": f"Bearer {self.api_token}",
@ -240,6 +241,7 @@ class UnifiAccessManager:
} }
response = requests.get(url, headers=headers, verify=False) response = requests.get(url, headers=headers, verify=False)
self.logger.debug(f"Fetch door groups API response status code: {response.status_code}") self.logger.debug(f"Fetch door groups API response status code: {response.status_code}")
self.logger.debug(f"Fetch door groups API response content: {response.text}")
if response.status_code == 200: if response.status_code == 200:
data = response.json() data = response.json()