325 lines
8.9 KiB
Markdown
325 lines
8.9 KiB
Markdown
# ESPHome Gitea Sync Service
|
|
|
|
A Docker-based synchronization service that bridges Gitea repositories with ESPHome device configurations. Automatically syncs YAML configuration files between your Gitea git repository and your ESPHome installation.
|
|
|
|
## Features
|
|
|
|
- **Bidirectional Git Sync**: Sync configurations between Gitea and ESPHome
|
|
- **File Watching**: Automatically detect YAML changes and push to Gitea
|
|
- **Gitea Webhooks**: Receive push events and pull changes automatically
|
|
- **REST API**: Manual sync endpoints and device listing
|
|
- **Docker Polling**: Uses filesystem polling for reliable change detection on Docker bind mounts
|
|
- **Thread-Safe**: Concurrent operation handling with locks
|
|
- **Archive Support**: Detects when files are moved to archive folders
|
|
|
|
## Architecture
|
|
|
|
This service works alongside the official ESPHome container:
|
|
- **ESPHome Container**: Handles all compilation and device deployment (immutable)
|
|
- **Sync Service**: Manages git synchronization between Gitea and ESPHome (this service)
|
|
|
|
The ESPHome container remains unchanged and simply consumes the YAML configs. This service only handles git operations.
|
|
|
|
## Quick Start
|
|
|
|
### 1. Configure Environment Variables
|
|
|
|
Edit `docker-compose.yml` and set your Gitea details:
|
|
|
|
```yaml
|
|
environment:
|
|
- GITEA_URL=https://your-gitea-instance.com
|
|
- GITEA_REPO=username/esphome-configs
|
|
- GITEA_TOKEN=your_gitea_access_token
|
|
- GITEA_BRANCH=main
|
|
- AUTO_PUSH=false # Set to true for automatic push on file changes
|
|
```
|
|
|
|
### 2. Start the Services
|
|
|
|
```bash
|
|
docker-compose up -d
|
|
```
|
|
|
|
This starts two services:
|
|
- **ESPHome Dashboard**: `http://localhost:6052` (compilation and deployment)
|
|
- **Gitea Sync Service**: `http://localhost:5000` (git synchronization)
|
|
|
|
### 3. Access the Services
|
|
|
|
- ESPHome Dashboard: http://localhost:6052
|
|
- Sync API Health: http://localhost:5000/health
|
|
- Device List: http://localhost:5000/devices
|
|
|
|
### 4. Configure Gitea Webhook (Optional)
|
|
|
|
To automatically pull changes when you push to Gitea:
|
|
|
|
1. Go to your Gitea repository → Settings → Webhooks
|
|
2. Add Webhook → Gitea
|
|
3. Set Target URL: `http://your-server:5000/webhook/gitea`
|
|
4. Content Type: `application/json`
|
|
5. Trigger On: `Push Events`
|
|
6. Click "Add Webhook"
|
|
|
|
## Environment Variables
|
|
|
|
Configure the sync service in `docker-compose.yml`:
|
|
|
|
| Variable | Default | Description |
|
|
|----------|---------|-------------|
|
|
| `ESPHOME_CONFIG_DIR` | `/config` | Directory containing device configs |
|
|
| `DEBOUNCE_SECONDS` | `5` | Delay before triggering after file change |
|
|
| `USE_POLLING` | `true` | Use polling for Docker compatibility (required) |
|
|
| `POLLING_INTERVAL` | `1.0` | Seconds between filesystem polls |
|
|
| `GITEA_URL` | - | URL of your Gitea instance |
|
|
| `GITEA_REPO` | - | Repository path (username/repo) |
|
|
| `GITEA_TOKEN` | - | Gitea access token (generate in user settings) |
|
|
| `GITEA_BRANCH` | `main` | Git branch to sync |
|
|
| `AUTO_PUSH` | `false` | Automatically push local changes to Gitea |
|
|
| `GIT_USER_NAME` | `ESPHome Sync Service` | Git commit author name |
|
|
| `GIT_USER_EMAIL` | `esphome-sync@localhost` | Git commit author email |
|
|
|
|
## API Endpoints
|
|
|
|
### Health Check
|
|
|
|
```bash
|
|
curl http://localhost:5000/health
|
|
```
|
|
|
|
### List Devices
|
|
|
|
Lists all YAML files in the config directory:
|
|
|
|
```bash
|
|
curl http://localhost:5000/devices
|
|
```
|
|
|
|
Returns:
|
|
```json
|
|
{
|
|
"devices": [
|
|
{
|
|
"name": "bedroom-light",
|
|
"config_path": "/config/bedroom-light.yaml",
|
|
"last_modified": "2026-01-13T10:30:00"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
### Gitea Webhook
|
|
|
|
Receives push events from Gitea and pulls changes:
|
|
|
|
```bash
|
|
curl -X POST http://localhost:5000/webhook/gitea \
|
|
-H "Content-Type: application/json" \
|
|
-H "X-Gitea-Event: push" \
|
|
-d '{"repository": {"full_name": "user/repo"}}'
|
|
```
|
|
|
|
### Manual Pull from Gitea
|
|
|
|
Manually trigger a git pull:
|
|
|
|
```bash
|
|
curl -X POST http://localhost:5000/sync/pull
|
|
```
|
|
|
|
### Manual Push to Gitea
|
|
|
|
Manually trigger a git push:
|
|
|
|
```bash
|
|
curl -X POST http://localhost:5000/sync/push \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"message": "Manual sync from API"}'
|
|
```
|
|
|
|
## File Watching
|
|
|
|
The service monitors all `.yaml` and `.yml` files in the root of the config directory.
|
|
|
|
### Detected Events
|
|
|
|
1. **File Modified** - Edit existing YAML file
|
|
2. **File Created** - Add new YAML file
|
|
3. **File Deleted** - Remove YAML file
|
|
4. **File Renamed** - Rename YAML file
|
|
5. **File Archived** - Move YAML file to subdirectory (e.g., `archive/`)
|
|
6. **File Restored** - Move YAML file from subdirectory back to root
|
|
|
|
When `AUTO_PUSH=true`, all these events automatically trigger a git commit and push to Gitea.
|
|
|
|
### Commit Messages
|
|
|
|
- Modified/Created: `Auto-sync: device-name.yaml changed`
|
|
- Deleted: `Auto-sync: device-name.yaml deleted`
|
|
- Renamed: `Auto-sync: Renamed old-name.yaml to new-name.yaml`
|
|
- Archived: `Auto-sync: device-name.yaml archived`
|
|
- Restored: `Auto-sync: device-name.yaml restored from archive`
|
|
|
|
## Directory Structure
|
|
|
|
```
|
|
config/
|
|
├── bedroom-light.yaml # Device configs (flat structure)
|
|
├── kitchen-sensor.yaml
|
|
├── garage-door.yaml
|
|
├── .gitignore # Optional: ignore archive folder
|
|
├── archive/ # Optional: archived configs (not monitored)
|
|
│ └── old-device.yaml
|
|
└── .git/ # Git repository (managed by sync service)
|
|
```
|
|
|
|
## Workflows
|
|
|
|
### Editing in ESPHome Dashboard
|
|
|
|
1. Edit YAML files in ESPHome dashboard
|
|
2. File watcher detects the change
|
|
3. If `AUTO_PUSH=true`: Changes automatically committed and pushed to Gitea
|
|
4. If `AUTO_PUSH=false`: Manually trigger push via API
|
|
|
|
### Editing in Gitea (or git client)
|
|
|
|
1. Commit and push changes to Gitea repository
|
|
2. Gitea webhook triggers `/webhook/gitea` endpoint
|
|
3. Service runs `git pull` to fetch changes
|
|
4. ESPHome sees updated files and reloads dashboard
|
|
|
|
### Manual Sync
|
|
|
|
```bash
|
|
# Pull latest changes from Gitea
|
|
curl -X POST http://localhost:5000/sync/pull
|
|
|
|
# Push local changes to Gitea
|
|
curl -X POST http://localhost:5000/sync/push \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"message": "My commit message"}'
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### Service Won't Start
|
|
|
|
Check logs:
|
|
```bash
|
|
docker-compose logs webhook
|
|
docker-compose logs esphome
|
|
```
|
|
|
|
### File Watcher Not Detecting Changes
|
|
|
|
**Cause**: inotify events don't propagate through Docker bind mounts on WSL2, Unraid, and macOS.
|
|
|
|
**Solution**: The service uses polling mode by default (`USE_POLLING=true`). Ensure this is enabled in docker-compose.yml.
|
|
|
|
Check logs for:
|
|
```
|
|
Using PollingObserver (interval: 1.0s) for Docker bind mount compatibility
|
|
```
|
|
|
|
### Git Operations Failing
|
|
|
|
1. Verify Gitea token has correct permissions (read/write repository)
|
|
2. Check `GITEA_URL`, `GITEA_REPO`, and `GITEA_TOKEN` are set correctly
|
|
3. Check logs for git command errors:
|
|
```bash
|
|
docker-compose logs webhook | grep -i "git command"
|
|
```
|
|
|
|
### Webhook Not Receiving Events
|
|
|
|
1. Verify webhook URL is accessible from Gitea (firewall, network)
|
|
2. Check Gitea webhook delivery logs (Repository → Settings → Webhooks → Recent Deliveries)
|
|
3. Ensure `Content-Type` is `application/json` and trigger is `Push Events`
|
|
|
|
### Changes Not Syncing to Gitea
|
|
|
|
1. Check if `AUTO_PUSH=true` in docker-compose.yml
|
|
2. Verify file is in the root of `/config/` (not in subdirectory)
|
|
3. Check logs for file change detection:
|
|
```bash
|
|
docker-compose logs webhook | grep -i "detected"
|
|
```
|
|
|
|
## Development
|
|
|
|
### Run Locally (Without Docker)
|
|
|
|
```bash
|
|
# Install dependencies
|
|
pip install -r requirements.txt
|
|
|
|
# Set environment variables
|
|
export ESPHOME_CONFIG_DIR=/path/to/config
|
|
export DEBOUNCE_SECONDS=5
|
|
export USE_POLLING=true
|
|
export GITEA_URL=https://gitea.example.com
|
|
export GITEA_REPO=username/esphome-configs
|
|
export GITEA_TOKEN=your_token
|
|
export GITEA_BRANCH=main
|
|
export AUTO_PUSH=false
|
|
export GIT_USER_NAME="Your Name"
|
|
export GIT_USER_EMAIL="your@email.com"
|
|
|
|
# Run the service
|
|
python app.py
|
|
```
|
|
|
|
### Build Custom Image
|
|
|
|
```bash
|
|
docker build -t esphome-gitea-sync:custom .
|
|
```
|
|
|
|
### Testing File Watcher
|
|
|
|
Create, modify, delete, or move files in the config directory and watch the logs:
|
|
|
|
```bash
|
|
docker-compose logs -f webhook
|
|
```
|
|
|
|
You should see:
|
|
```
|
|
Detected change in /config/test.yaml
|
|
Change detected in device: test
|
|
AUTO_PUSH enabled, pushing changes to Gitea
|
|
```
|
|
|
|
## Security Considerations
|
|
|
|
- The sync service has no authentication by default
|
|
- Gitea token is stored in environment variables (keep docker-compose.yml secure)
|
|
- Only expose port 5000 on trusted networks
|
|
- Use a reverse proxy (nginx, Traefik) with authentication for external access
|
|
- Keep `AUTO_PUSH=false` unless you trust all changes made in ESPHome dashboard
|
|
- Review the `.gitignore` file to avoid committing sensitive data
|
|
|
|
## Migration from Old Structure
|
|
|
|
If migrating from `device-name/main.yaml` to flat `device-name.yaml` structure:
|
|
|
|
```bash
|
|
cd config
|
|
for dir in */; do
|
|
if [ -f "${dir}main.yaml" ]; then
|
|
device_name="${dir%/}"
|
|
mv "${dir}main.yaml" "${device_name}.yaml"
|
|
rmdir "${dir}"
|
|
fi
|
|
done
|
|
git add -A
|
|
git commit -m "Migrate to flat YAML structure"
|
|
git push origin main
|
|
```
|
|
|
|
## License
|
|
|
|
MIT License - See LICENSE file for details.
|