From 1929cc8e4a86a261dbff4483f5eaa32981694d57 Mon Sep 17 00:00:00 2001 From: Kumar Challa Date: Sat, 24 Jan 2026 11:20:23 -0600 Subject: [PATCH 1/3] bug: Fix regression when reading custom conditions from label.txt --- alpaca/config.py | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/alpaca/config.py b/alpaca/config.py index 7c2e119..35af6b5 100644 --- a/alpaca/config.py +++ b/alpaca/config.py @@ -28,8 +28,32 @@ def get_current_time(timezone_str: str = 'UTC') -> datetime: ERROR_NOT_CONNECTED = 0x407 # 1031 ERROR_UNSPECIFIED = 0x500 # 1280 -# Available cloud conditions from ML model -ALL_CLOUD_CONDITIONS = ['Clear', 'Mostly Cloudy', 'Overcast', 'Rain', 'Snow', 'Wisps of clouds'] +# Dynamically load cloud conditions from labels file +def load_labels(): + """Load cloud condition labels from labels.txt file""" + label_path = os.environ.get('LABEL_PATH', 'labels.txt') + try: + if os.path.exists(label_path): + with open(label_path, 'r') as f: + labels = [] + for line in f: + clean_line = line.strip() + # Handle "0 Clear" format if present + if " " in clean_line and clean_line.split(" ", 1)[0].isdigit(): + clean_line = clean_line.split(" ", 1)[1] + if clean_line: + labels.append(clean_line) + logger.info(f"Loaded {len(labels)} classes from {label_path}") + return labels + except Exception as e: + logger.error(f"Failed to load labels from {label_path}: {e}") + + # Fallback to defaults if file missing or error + logger.warning("Using fallback default cloud conditions") + return ['Clear', 'Mostly Cloudy', 'Overcast', 'Rain', 'Snow', 'Wisps of clouds'] + +# Available cloud conditions from ML model (loaded dynamically from labels.txt) +ALL_CLOUD_CONDITIONS = load_labels() @dataclass From 0faed06664cac7c722ecb2b66fb8d046d5ac19ff Mon Sep 17 00:00:00 2001 From: Kumar Challa Date: Sat, 24 Jan 2026 11:34:52 -0600 Subject: [PATCH 2/3] fix: Sanitize loaded settings and remove obsolete conditions from saved persistent configuration --- alpaca/config.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/alpaca/config.py b/alpaca/config.py index 35af6b5..256b63d 100644 --- a/alpaca/config.py +++ b/alpaca/config.py @@ -126,7 +126,33 @@ def load_settings_from_file(cls) -> dict: # Filter dictionary to only include valid fields for this dataclass valid_fields = {f.name for f in cls.__dataclass_fields__.values()} - return {k: v for k, v in config_dict.items() if k in valid_fields} + settings = {k: v for k, v in config_dict.items() if k in valid_fields} + + # Sanitize loaded settings against current labels from labels.txt + # Remove saved 'unsafe_conditions' that are no longer in ALL_CLOUD_CONDITIONS + if 'unsafe_conditions' in settings: + valid_labels = set(ALL_CLOUD_CONDITIONS) + original_count = len(settings['unsafe_conditions']) + settings['unsafe_conditions'] = [ + c for c in settings['unsafe_conditions'] + if c in valid_labels + ] + removed_count = original_count - len(settings['unsafe_conditions']) + if removed_count > 0: + logger.info(f"Removed {removed_count} obsolete unsafe condition(s) from saved configuration") + + # Clean up stale class thresholds + if 'class_thresholds' in settings: + original_count = len(settings['class_thresholds']) + settings['class_thresholds'] = { + k: v for k, v in settings['class_thresholds'].items() + if k in ALL_CLOUD_CONDITIONS + } + removed_count = original_count - len(settings['class_thresholds']) + if removed_count > 0: + logger.info(f"Removed {removed_count} obsolete class threshold(s) from saved configuration") + + return settings except Exception as e: logger.error(f"Failed to load configuration from {filepath}: {e}") return {} From 7071a62702c2ba9de5d242e3ef9417bf50153570 Mon Sep 17 00:00:00 2001 From: Kumar Challa Date: Sat, 24 Jan 2026 11:46:58 -0600 Subject: [PATCH 3/3] feat: MArk all conditions as unsaafe until explicitly set to safe by the user --- alpaca/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alpaca/config.py b/alpaca/config.py index 256b63d..43204cb 100644 --- a/alpaca/config.py +++ b/alpaca/config.py @@ -70,7 +70,7 @@ class AlpacaConfig: update_interval: int = 30 # seconds between cloud detection updates location: str = "AllSky Camera" image_url: str = field(default_factory=lambda: os.environ.get('IMAGE_URL', '')) - unsafe_conditions: list = field(default_factory=lambda: ['Rain', 'Snow', 'Mostly Cloudy', 'Overcast']) + unsafe_conditions: list = field(default_factory=lambda: ALL_CLOUD_CONDITIONS.copy()) # Confidence threshold settings default_threshold: float = 50.0 # Default threshold for any class not explicitly configured