- Python 73.9%
- HTML 25.2%
- Dockerfile 0.9%
|
|
||
|---|---|---|
| .forgejo/workflows | ||
| src | ||
| templates | ||
| .env.example | ||
| .gitignore | ||
| DEBUGGING_NO_LOGS.md | ||
| docker-compose.yml | ||
| Dockerfile | ||
| LABEL_MIGRATION_GUIDE.md | ||
| LICENSE | ||
| MQTT_LWT_FIX.md | ||
| MULTI_DEVICE_CHANGES.md | ||
| README.md | ||
| requirements.txt | ||
sispmctl-manager
MQTT bridge for Gembird USB power strips (SIS-PM series) with Home Assistant autodiscovery support.
Features
- Modern Web UI with light/dark theme and real-time updates
- MQTT Control - Full MQTT integration for automation
- Home Assistant Autodiscovery - Switches appear automatically
- Custom Labels - Label each socket in the web UI (web UI only, doesn't affect MQTT)
- Real-time State Sync - Changes reflected instantly across all interfaces
- MQTT over TLS - Secure encrypted communication
- Docker Deployment - Easy setup with USB passthrough
- Multi-Instance Support - Run multiple containers for multiple devices
- Configurable Device Names - Customize naming for easy identification
Quick Start
Prerequisites
- Docker and Docker Compose
- Gembird USB power strip (SIS-PM series)
- MQTT broker (with TLS recommended)
1. Clone Repository
git clone <repository-url>
cd sispmctl-manager
2. Configure MQTT Broker (Optional)
If using MQTT/Home Assistant, edit docker-compose.yml and set your MQTT broker details:
environment:
MQTT_ENABLED: "true"
MQTT_BROKER: mqtt.example.com
MQTT_USERNAME: your_username
MQTT_PASSWORD: your_password
To use web UI only (no MQTT):
environment:
MQTT_ENABLED: "false"
3. Set Up TLS Certificates (Optional)
If your MQTT broker uses a self-signed certificate, create a certs directory and add your CA certificate:
mkdir -p certs
cp /path/to/ca.crt certs/
Then uncomment and set in docker-compose.yml:
environment:
MQTT_TLS_CA_CERT: /app/certs/ca.crt
Note: If your broker uses a public CA (like Let's Encrypt), you can skip this step - the system's trusted CA certificates will be used automatically.
4. Run the Container
docker-compose up -d
5. Check Logs
docker-compose logs -f
6. Access the Web UI
Open your browser and navigate to:
http://localhost:8080
You can now control your power strip sockets and add custom labels!
7. Verify in Home Assistant
Go to Settings → Devices & Services → MQTT
You should see a "Sispm 0" device with all sockets (or your custom device name if configured).
Web UI
The web interface provides a modern, user-friendly way to control your power strip.
Features
- Real-time Control - Toggle sockets on/off instantly
- Live State Updates - See changes from MQTT or physical buttons immediately
- Custom Labels - Add descriptive labels to identify what each socket controls
- Labels are stored persistently in
/app/data/labels.json - Labels are for display only and don't affect MQTT operation
- Labels are stored persistently in
- Light/Dark Theme - Switch between themes with automatic localStorage persistence
- Responsive Design - Works on desktop, tablet, and mobile devices
- WebSocket Updates - Real-time synchronization using Socket.IO
Accessing the Web UI
By default, the web UI runs on port 8080. Access it at:
http://<your-server-ip>:8080
If running locally with Docker:
http://localhost:8080
Customizing Labels
Via Web UI:
- Click on any "Add label" input field
- Type a descriptive name (e.g., "Monitor", "Printer", "Desk Lamp")
- Press Enter or click outside the field to save
- Labels are saved automatically and persist across restarts
Via Configuration (Initial Setup):
You can pre-configure socket labels using environment variables. These labels are only applied on first startup if no label exists yet:
environment:
SOCKET_1_LABEL: "Monitor"
SOCKET_2_LABEL: "Printer"
SOCKET_3_LABEL: "Desk Lamp"
SOCKET_4_LABEL: "Speakers"
Important: Initial labels from environment variables will NOT overwrite labels you've already set in the web UI. They only apply when a socket has no label. This allows you to:
- Pre-configure labels for new deployments
- Share label configurations across multiple instances
- Maintain labels even when changing environment variables
Theme Switching
Click the theme toggle button in the header to switch between light and dark modes. Your preference is saved in the browser's localStorage.
Configuration
The web UI runs on gevent, a production-ready WSGI server optimized for WebSocket connections. No additional configuration needed.
The web UI can be configured via environment variables:
| Variable | Default | Description |
|---|---|---|
WEB_ENABLED |
true |
Enable/disable the web UI |
WEB_PORT |
8080 |
Port for the web UI |
WEB_HOST |
0.0.0.0 |
Host address to bind to |
To disable the web UI, set WEB_ENABLED=false in your docker-compose.yml.
Web Secret Key
The WEB_SECRET_KEY is used to cryptographically secure the web UI:
What it does:
- Signs session cookies to prevent tampering
- Provides CSRF protection for forms
- Secures WebSocket connections (Socket.IO)
- Protects any sensitive data Flask sends to clients
Default Behavior:
If not set, a random 64-character hexadecimal key is auto-generated on startup using Python's secrets module. However, sessions won't persist across container restarts with auto-generated keys.
For Production: Generate and set a persistent secret key:
# Generate a secure random key
python3 -c "import secrets; print(secrets.token_hex(32))"
# Example output:
# 57e50e3c6b2ec7f7087f1fc74888c8e35c544b872ec0bfc7158be0b2f177cddc
Then add it to your docker-compose.yml:
environment:
WEB_SECRET_KEY: "57e50e3c6b2ec7f7087f1fc74888c8e35c544b872ec0bfc7158be0b2f177cddc"
Security Notes:
- Minimum length: 16 characters (enforced by validation)
- Recommended: 32+ character random hex string
- Keep it secret - don't commit to version control
- If compromised, attackers can forge sessions and bypass CSRF protection
- Use a unique key per deployment
Security Considerations
The web UI does not include authentication by default. If exposing to the internet:
- Use a reverse proxy (nginx, Traefik) with authentication
- Use TLS/SSL certificates
- Restrict access via firewall rules
- Consider using a VPN
Configuration
All configuration is done via environment variables in docker-compose.yml.
MQTT Configuration
| Variable | Required | Default | Description |
|---|---|---|---|
MQTT_ENABLED |
No | true |
Enable/disable MQTT entirely |
MQTT_DISCOVERY_ENABLED |
No | true |
Enable/disable Home Assistant autodiscovery |
Note: You can run with just the web UI by setting MQTT_ENABLED=false, or with MQTT but without autodiscovery by setting MQTT_DISCOVERY_ENABLED=false.
MQTT Broker Settings
| Variable | Required | Default | Description |
|---|---|---|---|
MQTT_BROKER |
Conditional* | - | MQTT broker hostname or IP |
MQTT_PORT |
No | 8883 |
MQTT broker port |
MQTT_USERNAME |
No | "" |
MQTT username |
MQTT_PASSWORD |
No | "" |
MQTT password |
MQTT_CLIENT_ID |
No | sispmctl-manager |
MQTT client identifier |
*Required only if MQTT_ENABLED=true
TLS Settings
| Variable | Required | Default | Description |
|---|---|---|---|
MQTT_TLS_ENABLED |
No | true |
Enable MQTT over TLS |
MQTT_TLS_CA_CERT |
No | - | Path to CA certificate (uses system CA certs if not specified) |
MQTT_TLS_CLIENT_CERT |
No | - | Path to client certificate (optional) |
MQTT_TLS_CLIENT_KEY |
No | - | Path to client key (optional) |
MQTT_TLS_INSECURE |
No | false |
Skip hostname verification (dev only) |
Topic Settings
| Variable | Required | Default | Description |
|---|---|---|---|
MQTT_BASE_TOPIC |
No | homeassistant/sispm |
Base topic for device |
MQTT_DISCOVERY_PREFIX |
No | homeassistant |
HA discovery prefix |
Device Settings
| Variable | Required | Default | Description |
|---|---|---|---|
DEVICE_NAME |
No | sispm |
Friendly device name used in MQTT topics and HA (e.g., "office", "lab", "workbench") |
SISPM_DEVICE_INDEX |
No | 0 |
USB device index (0 for first device) |
SISPM_POLL_INTERVAL |
No | 30 |
Seconds between state polls |
Web UI Settings
| Variable | Required | Default | Description |
|---|---|---|---|
WEB_ENABLED |
No | true |
Enable/disable the web UI |
WEB_PORT |
No | 8080 |
Port for the web interface |
WEB_HOST |
No | 0.0.0.0 |
Host address to bind to |
WEB_SECRET_KEY |
No | Auto-generated | Flask secret key for session security (see Security section below) |
WEB_SOCKETS_PER_ROW |
No | 2 |
Number of sockets displayed per row in the web UI (1-8) |
Security Note: The WEB_SECRET_KEY is used to cryptographically sign session cookies and secure WebSocket connections. If not set, a random key is generated on startup. However, sessions won't persist across container restarts. For production, set a persistent secret key.
Layout Note: On mobile devices (screen width < 768px), sockets are automatically displayed in a single column regardless of the WEB_SOCKETS_PER_ROW setting.
Initial Socket Labels (Optional)
Configure initial labels for sockets using individual environment variables:
| Variable | Required | Default | Description |
|---|---|---|---|
SOCKET_1_LABEL |
No | - | Initial label for socket 1 |
SOCKET_2_LABEL |
No | - | Initial label for socket 2 |
SOCKET_3_LABEL |
No | - | Initial label for socket 3 |
SOCKET_4_LABEL |
No | - | Initial label for socket 4 |
| ... | ... | ... | ... (up to socket 16) |
Behavior:
- Initial labels are only applied when the label doesn't already exist in storage
- Once a label is set (via web UI or initial config), changing the environment variable won't overwrite it
- Useful for pre-configuring labels in new deployments or sharing configurations
Example:
environment:
SOCKET_1_LABEL: "Monitor"
SOCKET_2_LABEL: "Printer"
SOCKET_3_LABEL: "Desk Lamp"
Logging
| Variable | Required | Default | Description |
|---|---|---|---|
LOG_LEVEL |
No | INFO |
Logging level (DEBUG, INFO, WARNING, ERROR) |
MQTT Topics
The device ID is generated from DEVICE_NAME and SISPM_DEVICE_INDEX as: {device_name}_{device_index}
For example, with DEVICE_NAME=office and SISPM_DEVICE_INDEX=0, the device ID is office_0.
State Topics
homeassistant/sispm/office_0/socket_1/state
homeassistant/sispm/office_0/socket_2/state
...
Payloads: ON or OFF
Command Topics
homeassistant/sispm/office_0/socket_1/command
homeassistant/sispm/office_0/socket_2/command
...
Payloads: ON or OFF
Availability Topic
homeassistant/sispm/office_0/availability
Payloads: online or offline
Discovery Topics
homeassistant/switch/office_0_socket_1/config
homeassistant/switch/office_0_socket_2/config
...
TLS Certificate Setup
Using Public CA Certificates (Let's Encrypt, etc.)
If your MQTT broker uses a certificate from a public Certificate Authority (like Let's Encrypt, DigiCert, etc.), no additional configuration is needed - the container will use the system's trusted CA certificates automatically.
Simply ensure MQTT_TLS_ENABLED is set to true (default) and don't set MQTT_TLS_CA_CERT.
Using Self-Signed Certificates
If your MQTT broker uses a self-signed certificate, you need to provide the CA certificate.
Generate a self-signed CA certificate:
mkdir -p certs
openssl req -new -x509 -days 365 -extensions v3_ca \
-keyout certs/ca.key -out certs/ca.crt \
-subj "/CN=My MQTT CA"
Configure the CA certificate path in docker-compose.yml:
environment:
MQTT_TLS_CA_CERT: /app/certs/ca.crt
For development/testing with hostname mismatches, you can enable insecure mode:
environment:
MQTT_TLS_CA_CERT: /app/certs/ca.crt
MQTT_TLS_INSECURE: "true"
Warning: Do not use insecure mode in production.
Using Client Certificates
If your broker requires client certificates:
# Generate client key and certificate
openssl req -new -nodes -keyout certs/client.key -out certs/client.csr \
-subj "/CN=sispmctl-manager"
openssl x509 -req -in certs/client.csr -CA certs/ca.crt -CAkey certs/ca.key \
-CAcreateserial -out certs/client.crt -days 365
Update docker-compose.yml:
environment:
MQTT_TLS_CLIENT_CERT: /app/certs/client.crt
MQTT_TLS_CLIENT_KEY: /app/certs/client.key
USB Permissions
The container runs as root to access USB devices. This is the simplest approach and works with most setups.
Verify USB Device Access
First, verify the device is accessible on the host:
# Check if sispmctl can see the device on the host
sispmctl -s
# Find USB device details
lsusb | grep -i gembird
You should see output like:
Gembird #0
USB information: bus 001, device 004
device type: 4-socket SiS-PM
serial number: 01:01:5b:6a:70
Troubleshooting Container Access
If the container can't see the device:
1. Verify device passthrough:
# Check USB devices inside the container
docker exec sispmctl-manager lsusb
2. Check sispmctl inside container:
# Try running sispmctl directly in the container
docker exec sispmctl-manager sispmctl -s
3. For improved security (optional):
Instead of running as root, you can configure udev rules on the host to make the device world-readable:
# Find your device's vendor and product IDs
lsusb | grep -i gembird
# Example output: Bus 001 Device 004: ID 04b4:fd13 Cypress Semiconductor Corp.
# Create udev rule (replace vendor/product IDs with yours)
echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="04b4", ATTR{idProduct}=="fd13", MODE="0666"' | \
sudo tee /etc/udev/rules.d/60-sispmctl.rules
# Reload udev rules
sudo udevadm control --reload-rules
sudo udevadm trigger
# Replug the device or reboot
Then rebuild the Dockerfile to run as non-root user.
Common vendor/product IDs for Gembird devices:
04b4:fd13- Cypress Semiconductor (common for SIS-PM)067b:2303- Prolific Technology (some models)
Troubleshooting
Device Not Found
Problem: Logs show "USB device not found"
Solutions:
- Check USB connection:
lsusb - Verify sispmctl works on host:
sispmctl -s - Check container has USB access:
docker exec sispmctl-manager lsusb - Review USB permissions (see above)
MQTT Connection Failed
Problem: Cannot connect to MQTT broker
Solutions:
- Verify broker address and port
- Check network connectivity:
docker exec sispmctl-manager ping mqtt.example.com - Review TLS certificate configuration
- Check broker logs for connection attempts
- Try with
MQTT_TLS_ENABLED: "false"for testing (non-TLS)
Certificate Errors
Problem: TLS/SSL errors in logs
Solutions:
- Verify CA certificate is correct and readable
- Check certificate expiration:
openssl x509 -in certs/ca.crt -noout -dates - Ensure certificate path in container is correct
- For self-signed certs, enable
MQTT_TLS_INSECURE: "true"(dev only)
Switches Not Appearing in Home Assistant
Problem: Device doesn't show up in HA
Solutions:
- Check MQTT integration is configured in HA
- Verify discovery prefix matches HA configuration (default:
homeassistant) - Check MQTT logs in HA: Settings → System → Logs
- Manually subscribe to discovery topics to verify messages
State Not Updating
Problem: Switch state in HA doesn't reflect hardware
Solutions:
- Check polling interval (
SISPM_POLL_INTERVAL) - Verify device communication: check container logs
- Test sispmctl directly:
docker exec sispmctl-manager sispmctl -d 0 -g all - Review MQTT state topics with an MQTT client
Development
Building Locally
docker build -t sispmctl-manager .
Running Without Docker
# Install sispmctl
sudo apt-get install sispmctl
# Create virtual environment
python3 -m venv venv
source venv/bin/activate
# Install dependencies
pip install -r requirements.txt
# Set environment variables
export MQTT_BROKER=mqtt.example.com
export MQTT_USERNAME=user
export MQTT_PASSWORD=pass
# Run application
python src/main.py
Testing MQTT Commands
# Subscribe to state topics (replace 'office_0' with your device_id)
mosquitto_sub -h mqtt.example.com -p 8883 \
--cafile certs/ca.crt -u user -P pass \
-t 'homeassistant/sispm/#' -v
# Send command (replace 'office_0' with your device_id)
mosquitto_pub -h mqtt.example.com -p 8883 \
--cafile certs/ca.crt -u user -P pass \
-t 'homeassistant/sispm/office_0/socket_1/command' \
-m 'ON'
Architecture
┌─────────────────────────────────────────────┐
│ Home Assistant │
│ ┌─────────────────────────────────────┐ │
│ │ MQTT Integration │ │
│ │ • Autodiscovery │ │
│ │ • Switch entities │ │
│ └─────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
│
│ MQTT over TLS
▼
┌─────────────────────────────────────────────┐
│ MQTT Broker │
│ • State topics │
│ • Command topics │
│ • Availability topics │
└─────────────────────────────────────────────┘
│
│ MQTT over TLS
▼
┌─────────────────────────────────────────────┐
│ sispmctl-manager Container │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ MQTT Handler │ │ sispmctl │ │
│ │ • TLS │ │ Controller │ │
│ │ • Discovery │ │ • USB device │ │
│ │ • Commands │ │ • Subprocess │ │
│ └──────────────┘ └──────────────┘ │
│ │ │ │
│ └──────┬───────────┘ │
│ │ │
│ ┌───────▼────────┐ │
│ │ Main Loop │ │
│ │ • State poll │ │
│ │ • Sync states │ │
│ └────────────────┘ │
└─────────────────────────────────────────────┘
│
│ USB
▼
┌─────────────────────────────────────────────┐
│ Gembird USB Power Strip │
│ Socket 1 Socket 2 Socket 3 Socket 4 │
└─────────────────────────────────────────────┘
Multi-Instance Deployment
You can run multiple containers to control multiple USB power strips. Each instance needs a unique DEVICE_NAME to avoid conflicts.
Example: Two Power Strips
Create separate docker-compose configurations or use one file with multiple services:
version: '3.8'
services:
sispmctl-office:
build: .
container_name: sispmctl-office
restart: unless-stopped
devices:
- /dev/bus/usb:/dev/bus/usb
ports:
- "8080:8080"
environment:
# MQTT Configuration
MQTT_ENABLED: "true"
MQTT_DISCOVERY_ENABLED: "true"
MQTT_BROKER: mqtt.example.com
MQTT_PORT: 8883
MQTT_USERNAME: sispm_user
MQTT_PASSWORD: changeme
# TLS Configuration
MQTT_TLS_ENABLED: "true"
# MQTT_TLS_CA_CERT: /app/certs/ca.crt # Optional: use system CA certs if not specified
# MQTT Topics
MQTT_BASE_TOPIC: homeassistant/sispm
MQTT_DISCOVERY_PREFIX: homeassistant
# Device Configuration
DEVICE_NAME: office # First power strip
SISPM_DEVICE_INDEX: "0"
SISPM_POLL_INTERVAL: "30"
# Web UI
WEB_ENABLED: "true"
WEB_PORT: "8080"
WEB_HOST: "0.0.0.0"
WEB_SECRET_KEY: "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0e1f2"
WEB_SOCKETS_PER_ROW: "2"
# Initial socket labels
SOCKET_1_LABEL: "Monitor"
SOCKET_2_LABEL: "Printer"
SOCKET_3_LABEL: "Desk Lamp"
SOCKET_4_LABEL: "Speakers"
# Logging
LOG_LEVEL: INFO
volumes:
- ./certs:/app/certs:ro
- ./data-office:/app/data
sispmctl-lab:
build: .
container_name: sispmctl-lab
restart: unless-stopped
devices:
- /dev/bus/usb:/dev/bus/usb
ports:
- "8081:8081"
environment:
# MQTT Configuration
MQTT_ENABLED: "true"
MQTT_DISCOVERY_ENABLED: "true"
MQTT_BROKER: mqtt.example.com
MQTT_PORT: 8883
MQTT_USERNAME: sispm_user
MQTT_PASSWORD: changeme
# TLS Configuration
MQTT_TLS_ENABLED: "true"
# MQTT_TLS_CA_CERT: /app/certs/ca.crt # Optional: use system CA certs if not specified
# MQTT Topics
MQTT_BASE_TOPIC: homeassistant/sispm
MQTT_DISCOVERY_PREFIX: homeassistant
# Device Configuration
DEVICE_NAME: lab # Second power strip
SISPM_DEVICE_INDEX: "1"
SISPM_POLL_INTERVAL: "30"
# Web UI
WEB_ENABLED: "true"
WEB_PORT: "8081"
WEB_HOST: "0.0.0.0"
WEB_SECRET_KEY: "z9y8x7w6v5u4t3s2r1q0p9o8n7m6l5k4j3i2h1g0f9e8d7c6b5a4z3y2x1w0v9u8"
WEB_SOCKETS_PER_ROW: "2"
# Initial socket labels
SOCKET_1_LABEL: "Oscilloscope"
SOCKET_2_LABEL: "Soldering Iron"
SOCKET_3_LABEL: "Power Supply"
SOCKET_4_LABEL: "Test Equipment"
# Logging
LOG_LEVEL: INFO
volumes:
- ./certs:/app/certs:ro
- ./data-lab:/app/data
This will create two devices:
- Office 0 - Web UI at
http://localhost:8080, HA switches: Office 0 Socket 1, etc.- Pre-configured labels: Monitor, Printer, Desk Lamp, Speakers
- Lab 1 - Web UI at
http://localhost:8081, HA switches: Lab 1 Socket 1, etc.- Pre-configured labels: Oscilloscope, Soldering Iron, Power Supply, Test Equipment
Each instance:
- Uses different MQTT topics based on device ID (
office_0andlab_1) - Has its own web UI on a different port
- Has a unique
WEB_SECRET_KEYfor security isolation - Has pre-configured initial labels (can be changed later via web UI)
- Stores labels in a separate data directory
- No conflicts between instances
Note: Generate unique secret keys for each instance:
python3 -c "import secrets; print(secrets.token_hex(32))"
Usage Modes
The bridge supports three usage modes based on configuration:
1. Full Mode (Default)
Both MQTT and Web UI enabled - best for Home Assistant integration with manual control.
environment:
MQTT_ENABLED: "true"
MQTT_DISCOVERY_ENABLED: "true"
WEB_ENABLED: "true"
Features:
- Home Assistant autodiscovery and control
- Web UI for manual control and labeling
- Real-time sync between all interfaces
- MQTT automation support
2. Web UI Only Mode
Just the web interface - ideal for standalone use without MQTT/Home Assistant.
environment:
MQTT_ENABLED: "false"
WEB_ENABLED: "true"
Features:
- Simplified setup (no MQTT broker needed)
- Web UI control and labeling
- State polling and real-time updates
- No external dependencies
Note: You can omit MQTT_BROKER and related settings in this mode.
3. MQTT Only Mode
MQTT without web UI - for headless Home Assistant integration.
environment:
MQTT_ENABLED: "true"
MQTT_DISCOVERY_ENABLED: "true"
WEB_ENABLED: "false"
Features:
- Home Assistant integration only
- Lower resource usage (no Flask server)
- Automated control via MQTT
- No web interface
4. MQTT Without Autodiscovery
Manual MQTT control without Home Assistant discovery.
environment:
MQTT_ENABLED: "true"
MQTT_DISCOVERY_ENABLED: "false"
WEB_ENABLED: "true" # or "false"
Use case: Custom MQTT automation systems or when you want to manually configure Home Assistant entities.
Future Enhancements
- Web UI authentication
- Optional YAML configuration file
- Metrics and monitoring
- Power usage reporting (if hardware supports)
- Scheduled socket automation
License
MIT License - See LICENSE file for details.
Credits
- Uses sispmctl for USB device control
- Built with paho-mqtt for MQTT communication
- Designed for Home Assistant integration
Support
For issues, questions, or contributions, please open an issue on GitHub.