VanneControl
IoT piston control system with Kotlin backend, MQTT broker, and cross-platform mobile app support (KMM). Control up to 8 pistons per device remotely via REST API and real-time WebSocket updates.
Install / Use
/learn @YassineKaibi/VanneControlREADME
IoT Piston Control System
A production-ready self-hosted IoT platform for remotely controlling piston actuators via MQTT. Features comprehensive scheduling, admin management, and real-time monitoring capabilities.
Features
Core Functionality
- Secure Authentication - JWT-based authentication with refresh tokens and BCrypt password hashing
- REST API - Complete RESTful API for device management, user profiles, and system control
- MQTT Communication - Real-time bidirectional device communication via Mosquitto broker with TLS support
- WebSocket Support - Live status updates with JWT-authenticated WebSocket connections
- Automated Scheduling - Cron-based scheduling system using Quartz for automated piston control
Administration & Management
- Role-Based Access Control - User and admin roles with granular permissions
- Web Admin Dashboard - Full-featured HTML admin panel with FreeMarker templates
- Audit Logging - Comprehensive activity tracking for compliance and security
- User Management - Admin tools for user creation, role assignment, and deletion
- System Statistics - Real-time dashboard with user counts, device metrics, and activity logs
Infrastructure
- Docker Compose - Complete containerized deployment with health checks
- PostgreSQL Database - Robust data persistence with JSONB support and optimized indexes
- Redis Caching - High-performance caching layer for improved response times
- Nginx Reverse Proxy - SSL/TLS termination and load balancing
- Mobile App Support - Native Android application (see separate README)
Architecture
┌─────────────────┐
│ Mobile/Web App │
└────────┬────────┘
│ HTTPS/WSS
┌────▼─────┐
│ Nginx │ (Reverse Proxy)
└────┬─────┘
│
┌────▼─────┐
│ Ktor │ (Backend API)
│ Backend │
└─┬──┬──┬──┘
│ │ │
┌──▼──▼──▼────┐
│ PostgreSQL │
│ Redis │
└─────────────┘
│
┌────▼──────┐
│ Mosquitto │ (MQTT Broker)
└────┬──────┘
│
┌────▼──────┐
│ ESP32 │ (IoT Devices)
│ Devices │
└───────────┘
System Components
Backend Server (backend/)
- Ktor framework (Kotlin)
- REST API + WebSocket endpoints
- JWT authentication with role-based access
- MQTT client integration
- Quartz scheduler for automated tasks
- FreeMarker templating for admin web UI
Database Layer (PostgreSQL)
- User accounts & authentication
- Device registry with ownership
- Piston states & history (8 pistons per device)
- Telemetry data with JSONB payloads
- Schedules with cron expressions
- Audit logs for admin actions
Message Broker (Mosquitto MQTT)
- Device command/control messages
- Real-time status updates
- TLS encryption support
- Binary protocol for ESP32 devices
Caching Layer (Redis)
- Session storage
- API response caching
- Real-time data buffering
Reverse Proxy (Nginx)
- SSL/TLS termination
- Load balancing
- Static content serving
- WebSocket proxying
Quick Start
Prerequisites
- Docker & Docker Compose
- OpenSSL (for certificate generation)
- Python 3.8+ (for device client/simulator)
Installation
- Clone the repository
git clone https://github.com/YassineKaibi/VanneControl.git
cd VanneControl
- Generate certificates
chmod +x generate-certs.sh
./generate-certs.sh
- Configure environment
cp .env.example .env
# Edit .env with your secure passwords
nano .env
Required environment variables:
POSTGRES_PASSWORD=your_secure_postgres_password
REDIS_PASSWORD=your_secure_redis_password
JWT_SECRET=your_jwt_secret_key
JWT_ISSUER=piston-control
JWT_AUDIENCE=piston-app
SESSION_ENCRYPT_KEY=your_32_char_session_encrypt_key
SESSION_SIGN_KEY=your_32_char_session_sign_key
- Start services
docker compose build
docker compose up -d
- Verify deployment
curl http://localhost:8080/health
Expected response:
{
"status": "healthy",
"timestamp": 1234567890
}
API Documentation
Authentication
Register User
POST /auth/register
Content-Type: application/json
{
"email": "user@example.com",
"password": "SecurePass123!"
}
Response (201 Created):
{
"user": {
"id": "uuid",
"email": "user@example.com",
"role": "user"
},
"token": "eyJhbGc..."
}
Login
POST /auth/login
Content-Type: application/json
{
"email": "user@example.com",
"password": "SecurePass123!"
}
Response (200 OK):
{
"token": "eyJhbGc...",
"refreshToken": "refresh_token_here"
}
Device Management
List Devices
GET /devices
Authorization: Bearer <token>
Get Device Details
GET /devices/{deviceId}
Authorization: Bearer <token>
Control Piston
POST /devices/{deviceId}/pistons/{pistonNumber}
Authorization: Bearer <token>
Content-Type: application/json
{
"action": "activate"
}
Get Device Statistics
GET /devices/{deviceId}/stats
Authorization: Bearer <token>
Schedule Management
Create Schedule
POST /schedules
Authorization: Bearer <token>
Content-Type: application/json
{
"name": "Morning activation",
"deviceId": "device-uuid",
"pistonNumber": 1,
"action": "ACTIVATE",
"cronExpression": "0 0 8 * * ?",
"enabled": true
}
Cron expression format: second minute hour day month day-of-week
List User Schedules
GET /schedules
Authorization: Bearer <token>
Get Schedule by ID
GET /schedules/{scheduleId}
Authorization: Bearer <token>
Update Schedule
PUT /schedules/{scheduleId}
Authorization: Bearer <token>
Content-Type: application/json
{
"name": "Updated name",
"cronExpression": "0 0 20 * * ?",
"enabled": false
}
Delete Schedule
DELETE /schedules/{scheduleId}
Authorization: Bearer <token>
Get Schedules by Device
GET /schedules/device/{deviceId}
Authorization: Bearer <token>
User Profile
Get Profile
GET /user/profile
Authorization: Bearer <token>
Update Profile
PUT /user/profile
Authorization: Bearer <token>
Content-Type: application/json
{
"firstName": "John",
"lastName": "Doe",
"phoneNumber": "+1234567890",
"location": "New York"
}
Admin Endpoints
All admin endpoints require admin role.
List All Users
GET /admin/users?limit=50&offset=0
Authorization: Bearer <admin-token>
Get User by ID
GET /admin/users/{userId}
Authorization: Bearer <admin-token>
Update User Role
PATCH /admin/users/{userId}/role
Authorization: Bearer <admin-token>
Content-Type: application/json
{
"role": "admin"
}
Delete User
DELETE /admin/users/{userId}
Authorization: Bearer <admin-token>
Get System Statistics
GET /admin/stats
Authorization: Bearer <admin-token>
Response:
{
"totalUsers": 150,
"totalAdmins": 5,
"totalDevices": 75,
"totalSchedules": 200,
"recentAuditLogs": [...]
}
Get Audit Logs
GET /admin/audit-logs?limit=100&offset=0
Authorization: Bearer <admin-token>
Admin Web Dashboard
Access the web-based admin panel at:
http://localhost:8080/admin/login
Features:
- User management interface
- System statistics dashboard
- Audit log viewer
- Role assignment tools
- Session-based authentication
WebSocket Connection
Connect to WebSocket
const ws = new WebSocket('ws://localhost:8080/ws?token=<jwt-token>');
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('Device update:', data);
};
WebSocket message format:
{
"deviceId": "uuid",
"pistonNumber": 1,
"state": "active",
"timestamp": 1234567890
}
Security
Production Security Checklist
Environment Variables
- Change all default passwords in
.env - Use strong random values for JWT secrets
- Generate unique session encryption keys
TLS/SSL
- Enable HTTPS in Nginx configuration
- Use valid SSL certificates (Let's Encrypt recommended)
- Configure MQTT broker for TLS on port 8883
Database
- Use strong PostgreSQL password
- Restrict database network access
- Enable SSL connections in production
Authentication
- JWT tokens expire after configurable period
- Refresh tokens for long-lived sessions
- BCrypt password hashing with salt rounds
Admin Access
- Create admin users manually via database
- Use separate admin JWT authentication
- All admin actions logged to audit_logs table
Network Security
- Use Docker network isolation
- Configure firewall rules
- Limit exposed ports
Creating Admin User
- Register a normal user via
/auth/register - Promote to admin via database:
UPDATE users SET role = 'admin' WHERE email = 'admin@example.com';
Database Schema
Tables
users
id(UUID, PK)email(TEXT, UNIQUE)password_hash(TEXT)role(TEXT: 'user' | 'admin')first_name,last_name,phone_number,date_of_birth,locationavatar_url(TEXT)preferences(JSONB)created_at,updated_at(TIMESTAMP)
devices
id(UUID, PK)name(TEXT)owner_id(UUID, FK -> users)mqtt_client_id(TEXT, UNIQUE)status(TEXT: 'online' | 'offline')created_at,updated_at(TIMESTAMP)
pistons
id(UUID, PK)device_id(UUID, FK -> devices)piston_number(INT: 1-8)state(TEXT: 'active' | 'inactive')last_triggered(TIMESTAMP)
telemetry
id(BIGSERIAL, PK)device_id(UUID, FK -> devices)piston_id(UUID, FK -> pistons)event_type(TEXT: 'activated' | 'deactivated' | 'status_update')payload(JSONB)created_at(TIMESTAMP)
schedules
id(UUID, PK)name(TEXT)device_id(UUID, FK -> devices)piston_number(INT: 1-8)action(TEXT: 'ACTIVATE' | 'DEACTIVATE')cron_expression(TEXT)- `en
Related Skills
openhue
336.9kControl Philips Hue lights and scenes via the OpenHue CLI.
sag
336.9kElevenLabs text-to-speech with mac-style say UX.
weather
336.9kGet current weather and forecasts via wttr.in or Open-Meteo
tweakcc
1.4kCustomize Claude Code's system prompts, create custom toolsets, input pattern highlighters, themes/thinking verbs/spinners, customize input box & user message styling, support AGENTS.md, unlock private/unreleased features, and much more. Supports both native/npm installs on all platforms.
