Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 54 additions & 4 deletions alpaca/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -46,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
Expand Down Expand Up @@ -102,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 {}
Expand Down
Loading