ML-based cloud detection for AllSky cameras with MQTT and ASCOM Alpaca
10K+
A Machine Learning-based cloud detection system for AllSky cameras with MQTT and ASCOM Alpaca SafetyMonitor integration.
Docker (Recommended):
mkdir -p /path/to/config
sudo chown 1000:1000 /path/to/config
docker run -d --name simple-cloud-detect --network=host \
-v /path/to/config:/config \
-v /path/to/keras_model.h5:/app/keras_model.h5 \
-v /path/to/labels.txt:/app/labels.txt \
-e IMAGE_URL="http://your-allsky-camera/image.jpg" \
-e MQTT_BROKER="192.168.1.250" \
-e MQTT_DISCOVERY_MODE="homeassistant" \
-e DETECT_INTERVAL="60" \
-e VERIFY_SSL="false" \
-e DEVICE_ID="clouddetect_001" \
chvvkumar/simpleclouddetect:latest
Important: The model files (
keras_model.h5andlabels.txt) must have read+write permissions for the container user. To ensure proper permissions:# Set appropriate permissions (Linux/macOS) chmod 666 /path/to/keras_model.h5 chmod 666 /path/to/labels.txt # Or set ownership to your user and make group-writable chown $USER:$USER /path/to/keras_model.h5 /path/to/labels.txt chmod 664 /path/to/keras_model.h5 /path/to/labels.txt
That's it! Your device will automatically appear in Home Assistant under Settings → Devices & Services → MQTT.
| Condition | Example |
|---|---|
| Clear Skies | ![]() |
| Majority Clouds | ![]() |
| Wisps of Clouds | ![]() |
| Overcast | ![]() |
| View | Screenshot |
|---|---|
| Settings Collapsed | ![]() |
| Settings Expanded | ![]() |
docker pull chvvkumar/simpleclouddetect:latest
| Variable | Description | Example |
|---|---|---|
IMAGE_URL | URL or file path to AllSky camera image | http://allskypi.lan/image.jpg |
MQTT_BROKER | MQTT broker address | 192.168.1.250 |
| Variable | Default | Description |
|---|---|---|
MQTT_PORT | 1883 | MQTT broker port |
MQTT_USERNAME | - | MQTT authentication username (optional) |
MQTT_PASSWORD | - | MQTT authentication password (optional) |
DETECT_INTERVAL | 60 | Detection interval in seconds |
VERIFY_SSL | false | Set to true to enable SSL certificate verification for HTTPS IMAGE_URLs. Defaults to false for convenience with self-signed certificates. |
| Variable | Default | Description |
|---|---|---|
MQTT_DISCOVERY_MODE | legacy | Mode: legacy or homeassistant |
Legacy Mode (manual YAML configuration):
MQTT_TOPIC - Topic for publishing (e.g., Astro/SimpleCloudDetect)Home Assistant Discovery Mode (automatic setup):
DEVICE_ID - Unique device identifier (e.g., clouddetect_001)DEVICE_NAME - Custom device name (default: Cloud Detector)MQTT_DISCOVERY_PREFIX - HA discovery prefix (default: homeassistant)| Variable | Default | Description |
|---|---|---|
ALPACA_PORT | 11111 | HTTP API port |
ALPACA_DEVICE_NUMBER | 0 | Device number |
ALPACA_UPDATE_INTERVAL | 30 | Update interval in seconds |
MAX_IMAGE_AGE_SEC | 600 | Report unsafe if the source image is unchanged for this many seconds. 0 disables the check. Applies only until alpaca_config.json exists; afterwards use the setup page or the JSON key max_image_age_sec |
Note: For detailed Alpaca configuration, see ALPACA_README.md
To ensure your settings (device name, thresholds, etc.) are saved across container restarts, mount the /config directory:
-v /path/to/config:/config
The application will save alpaca_config.json in this directory. If a config file exists, it will take precedence for environment variables for the settings it contains.
Setting Proper Permissions:
The config directory must be writable by the container user. To ensure proper permissions:
# Create the config directory
mkdir -p /path/to/config
# Set appropriate permissions (Linux/macOS)
chmod 777 /path/to/config
# Or set ownership to a specific user (uses current user's UID, recommended for better security)
sudo chown $(id -u):$(id -g) /path/to/config
chmod 755 /path/to/config
Note: The container runs as user ID 1000 by default. If you encounter permission errors like
[Errno 13] Permission denied: '/config/alpaca_config.json', verify that the config directory is writable by the container user.
Multi-architecture support: The Docker images are built for both AMD64 (x86_64) and ARM64 (Raspberry Pi 4/5). Docker will automatically pull the correct image for your platform.
For Raspberry Pi, use the same docker commands. The ARM64 build uses full TensorFlow instead of tensorflow-cpu for compatibility.
Note: First run on Raspberry Pi may take longer as it downloads the ARM64 image (~500MB).
With URL-based image:
docker run -d --name simple-cloud-detect --network=host \
-v /path/to/config:/config \
-v /path/to/keras_model.h5:/app/keras_model.h5 \
-v /path/to/labels.txt:/app/labels.txt \
-e IMAGE_URL="http://allskypi5.lan/current/resized/image.jpg" \
-e MQTT_BROKER="192.168.1.250" \
-e MQTT_PORT="1883" \
-e MQTT_DISCOVERY_MODE="homeassistant" \
-e DEVICE_ID="clouddetect_001" \
-e DEVICE_NAME="AllSky Cloud Detector" \
-e MQTT_USERNAME="your_username" \
-e MQTT_PASSWORD="your_password" \
-e DETECT_INTERVAL="60" \
-e VERIFY_SSL="false" \
chvvkumar/simpleclouddetect:latest
With local file:
docker run -d --name simple-cloud-detect --network=host \
-v /path/to/config:/config \
-v $HOME/path/to/image.jpg:/tmp/image.jpg \
-v /path/to/keras_model.h5:/app/keras_model.h5 \
-v /path/to/labels.txt:/app/labels.txt \
-e IMAGE_URL="file:///tmp/image.jpg" \
-e MQTT_BROKER="192.168.1.250" \
-e MQTT_DISCOVERY_MODE="homeassistant" \
-e DETECT_INTERVAL="60" \
-e DEVICE_ID="clouddetect_001" \
chvvkumar/simpleclouddetect:latest
With URL-based image:
docker run -d --name simple-cloud-detect --network=host \
-v /path/to/config:/config \
-v /path/to/keras_model.h5:/app/keras_model.h5 \
-v /path/to/labels.txt:/app/labels.txt \
-e IMAGE_URL="http://allskypi5.lan/current/resized/image.jpg" \
-e MQTT_BROKER="192.168.1.250" \
-e MQTT_TOPIC="Astro/SimpleCloudDetect" \
-e DETECT_INTERVAL="60" \
-e MQTT_USERNAME="your_username" \
-e MQTT_PASSWORD="your_password" \
-e VERIFY_SSL="false" \
chvvkumar/simpleclouddetect:latest
To use your own trained model and labels:
docker run -d --name simple-cloud-detect --network=host \
-v /path/to/config:/config \
-v /path/to/your/keras_model.h5:/app/keras_model.h5 \
-v /path/to/your/labels.txt:/app/labels.txt \
-e IMAGE_URL="http://allskypi5.lan/image.jpg" \
-e MQTT_BROKER="192.168.1.250" \
-e DETECT_INTERVAL="60" \
-e MQTT_DISCOVERY_MODE="homeassistant" \
-e DEVICE_ID="clouddetect_001" \
-e VERIFY_SSL="false" \
chvvkumar/simpleclouddetect:latest
services:
simpleclouddetect:
container_name: simple-cloud-detect
image: chvvkumar/simpleclouddetect:latest
network_mode: host
restart: unless-stopped
environment:
- IMAGE_URL=http://localhost/current/resized/image.jpg
- MQTT_BROKER=192.168.1.250
- MQTT_PORT=1883
- MQTT_DISCOVERY_MODE=homeassistant
- DEVICE_ID=clouddetect001
- DEVICE_NAME=AllSkyPi5 Cloud Detector
- MQTT_USERNAME=
- MQTT_PASSWORD=
- DETECT_INTERVAL=60
- VERIFY_SSL=false
- ALPACA_PORT=11111
- ALPACA_UPDATE_INTERVAL=30
volumes:
- /path/to/config:/config
- /home/pi/git/simpleCloudDetect/keras_model.h5:/app/keras_model.h5
- /home/pi/git/simpleCloudDetect/labels.txt:/app/labels.txt
services:
simpleclouddetect:
container_name: simple-cloud-detect
image: chvvkumar/simpleclouddetect:latest
network_mode: host
restart: unless-stopped
environment:
- IMAGE_URL=http://allskypi5.lan/current/resized/image.jpg
- MQTT_BROKER=192.168.1.250
- MQTT_PORT=1883
- MQTT_TOPIC=Astro/SimpleCloudDetect
- MQTT_USERNAME=your_username
- MQTT_PASSWORD=your_password
- DETECT_INTERVAL=60
- VERIFY_SSL=false
volumes:
- /path/to/config:/config
- /home/pi/git/simpleCloudDetect/keras_model.h5:/app/keras_model.h5
- /home/pi/git/simpleCloudDetect/labels.txt:/app/labels.txt
services:
simpleclouddetect:
container_name: simple-cloud-detect
image: chvvkumar/simpleclouddetect:latest
network_mode: host
restart: unless-stopped
environment:
- IMAGE_URL=file:///tmp/image.jpg
- MQTT_BROKER=192.168.1.250
- MQTT_DISCOVERY_MODE=homeassistant
- DEVICE_ID=clouddetect_001
volumes:
- /path/to/config:/config
- /path/to/image.jpg:/tmp/image.jpg
- /home/pi/git/simpleCloudDetect/keras_model.h5:/app/keras_model.h5
- /home/pi/git/simpleCloudDetect/labels.txt:/app/labels.txt
When using MQTT_DISCOVERY_MODE=homeassistant, your device automatically appears in Home Assistant with no YAML configuration needed.
What You Get:
binary_sensor.<device_name>_safe) - ON when the monitor reports safe, OFF when unsafe or when data is stale (see Stale Data Fail-Safes)unavailable while data is staleSetup Steps:

Tip: Use unique
DEVICE_IDvalues if you have multiple AllSky cameras
For custom setups or backward compatibility, configure sensors manually in configuration.yaml:
mqtt:
sensor:
- name: "Cloud Status"
unique_id: cloud_status_sensor_001
icon: mdi:clouds
state_topic: "Astro/SimpleCloudDetect"
value_template: "{{ value_json.class_name }}"
- name: "Cloud Status Confidence"
unique_id: cloud_confidence_sensor_001
icon: mdi:percent
state_topic: "Astro/SimpleCloudDetect"
value_template: "{{ value_json.confidence_score }}"
unit_of_measurement: "%"
- name: "Cloud Detection Time"
unique_id: cloud_detection_time_001
icon: mdi:timer
state_topic: "Astro/SimpleCloudDetect"
value_template: "{{ value_json['Detection Time (Seconds)'] }}"
unit_of_measurement: "s"
The Alpaca server also adds is_safe (true/false) to this payload. It is sent only on successful detection cycles, so treat a missing update for more than 3 minutes as unsafe.
The container includes an ASCOM Alpaca SafetyMonitor service (Interface Version 3) for astronomy automation software.
http://<your-server-ip>:11111/setup/v1/safetymonitor/0/setupIsSafe reports False when the input data can no longer be trusted:
max_image_age_sec (default 600), the monitor commits unsafe immediately. The Unsafe Wait Time does not apply. Recovery requires a changed image followed by the normal Safe Wait Time. Set to 0 to disable, for example when the camera serves a static test image.max(180, 3 x ALPACA_UPDATE_INTERVAL) seconds, for example because the image URL returns errors or times out, IsSafe reports False and the stable state is committed to unsafe. After detections succeed again, the normal Safe Wait Time applies before IsSafe returns True. This limit is not configurable.The Safety History on the setup page records these events with the condition Stale Image or Detection Stale.
Configuration:
max_image_age_sec in alpaca_config.json.MAX_IMAGE_AGE_SEC applies only until alpaca_config.json exists. Startup writes the full configuration to that file, and the file takes precedence afterwards. Once the file exists, use the setup page or edit the JSON key.Notes:
ALPACA_UPDATE_INTERVAL. A lower value forces unsafe between normal image updates.0 if that is not the intended behaviour.max_image_age_sec set to 0, this startup requirement is also disabled.Both checks apply to the ASCOM IsSafe value, the dashboard, and the external REST API.
Home Assistant behaviour while data is stale:
unavailable. They use a second availability topic that reports offline when either check trips and online on the next successful detection.Safe binary sensor stays available and reads OFF. It follows IsSafe each cycle and reads ON only when the monitor reports safe.is_safe in the JSON payload only on successful detection cycles, so a consumer receives nothing while detections fail and must treat a missing update for more than 3 minutes as unsafe.
Full Documentation: See docs/ALPACA_README.md for detailed configuration, API reference, and troubleshooting.
SimpleCloudDetect provides a separate REST API designed for external integrations like dashboards, monitoring systems, and custom scripts.
Get Current Status:
curl http://localhost:11111/api/ext/v1/status
Response:
{
"is_safe": true,
"safety_status": "Safe",
"detection": {
"class_name": "Clear",
"confidence_score": 0.9823,
"timestamp": "2026-02-04T15:30:45.123456"
}
}
/api/ext/v1/system - System information and uptime/api/ext/v1/status - Current safety status and detection/api/ext/v1/config - Configuration settings/api/ext/v1/clients - Connected ASCOM clients/api/ext/v1/history - Safety state transition history/api/ext/v1/image - Latest detection image (JPEG)Full Documentation: See docs/EXTERNAL_API.md for complete API reference, examples, and integration guides.
While an example model is included, training your own model with your camera's images is highly recommended for better accuracy.
keras_model.h5 and labels.txt

For Docker:
docker run -d --name simple-cloud-detect --network=host \
-v /path/to/config:/config \
-v /path/to/your/keras_model.h5:/app/keras_model.h5 \
-v /path/to/your/labels.txt:/app/labels.txt \
# ...other environment variables...
chvvkumar/simpleclouddetect:latest
Note: Docker containers automatically convert the model on startup.
VERIFY_SSL environment variable to disable SSL certificate verification for HTTPS IMAGE_URLs. Defaults to false.For issues, questions, or contributions, please visit the GitHub repository.
Content type
Image
Digest
sha256:a69c0c68c…
Size
373.1 MB
Last updated
19 days ago
docker pull chvvkumar/simpleclouddetect