From c31148f7f108e355bc04f63d202902ebaabd9ea3 Mon Sep 17 00:00:00 2001 From: James <91348155+FrogAi@users.noreply.github.com> Date: Mon, 1 Dec 2025 12:00:00 -0700 Subject: [PATCH] Random Themes --- frogpilot/assets/theme_manager.py | 83 ++++++++++++++++++++++++- frogpilot/common/frogpilot_variables.py | 7 ++- frogpilot/frogpilot_process.py | 21 +++++-- 3 files changed, 103 insertions(+), 8 deletions(-) diff --git a/frogpilot/assets/theme_manager.py b/frogpilot/assets/theme_manager.py index 83599696f..52a5ebb7d 100644 --- a/frogpilot/assets/theme_manager.py +++ b/frogpilot/assets/theme_manager.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 import glob +import random import requests import shutil @@ -371,7 +372,62 @@ class ThemeManager: start_of_week = target_date - timedelta(days=target_date.weekday()) return start_of_week <= current_date < target_date - def update_active_theme(self, time_validated, frogpilot_toggles, boot_run=False): + @staticmethod + def randomize_distance_icons(available_themes, selected_theme): + theme_packs_path = THEME_SAVE_PATH / "theme_packs" + if not theme_packs_path.exists(): + return "stock" + + candidates = [] + for theme_pack in theme_packs_path.iterdir(): + if not theme_pack.is_dir(): + continue + + distance_icons_dir = theme_pack / "distance_icons" + if not distance_icons_dir.is_dir(): + continue + + icon_name = theme_pack.name.lower() + + theme_association = [theme for theme in available_themes if theme.replace("-animated", "") in icon_name] + if theme_association and selected_theme not in icon_name: + continue + + weight = 5 if selected_theme in icon_name else 1 + candidates.extend([theme_pack.name] * weight) + + return random.choice(candidates) if candidates else "stock" + + @staticmethod + def randomize_theme_asset(available_themes): + if not available_themes: + return "stock" + + return random.choice(available_themes) + + @staticmethod + def randomize_wheel_image(available_themes, selected_theme): + steering_wheels_path = THEME_SAVE_PATH / "steering_wheels" + if not steering_wheels_path.exists(): + return "stock" + + candidates = [] + for wheel_file in steering_wheels_path.iterdir(): + if not wheel_file.is_file(): + continue + + name = wheel_file.stem.lower() + + theme_association = [theme for theme in available_themes if theme.replace("-animated", "") in name] + if theme_association and selected_theme not in name: + continue + + weight = 5 if selected_theme in name else 1 + candidates.extend([wheel_file.stem] * weight) + + return random.choice(candidates) if candidates else "stock" + + def update_active_theme(self, time_validated, frogpilot_toggles, boot_run=False, randomize_theme=False): if time_validated and frogpilot_toggles.holiday_themes: self.holiday_theme = self.update_holiday() else: @@ -386,6 +442,31 @@ class ThemeManager: "turn_signal_pack": ("signals", self.holiday_theme), "wheel_image": ("wheel_image", self.holiday_theme) } + elif (boot_run or randomize_theme) and frogpilot_toggles.random_themes: + available_themes = self.get_full_themes() + + if frogpilot_toggles.random_themes_holidays: + available_themes.extend(HOLIDAY_SLUGS.keys()) + + selected_theme = self.randomize_theme_asset(available_themes) + + asset_mappings = { + "color_scheme": ("colors", selected_theme.replace("-animated", "")), + "distance_icons": ("distance_icons", self.randomize_distance_icons(available_themes, selected_theme.replace("-animated", ""))), + "icon_pack": ("icons", selected_theme), + "sound_pack": ("sounds", selected_theme.replace("-animated", "")), + "turn_signal_pack": ("signals", selected_theme.replace("-animated", "")), + "wheel_image": ("wheel_image", self.randomize_wheel_image(available_themes, selected_theme.replace("-animated", ""))) + } + elif not frogpilot_toggles.random_themes: + asset_mappings = { + "color_scheme": ("colors", frogpilot_toggles.color_scheme), + "distance_icons": ("distance_icons", frogpilot_toggles.distance_icons), + "icon_pack": ("icons", frogpilot_toggles.icon_pack), + "sound_pack": ("sounds", frogpilot_toggles.sound_pack), + "turn_signal_pack": ("signals", frogpilot_toggles.signal_icons), + "wheel_image": ("wheel_image", frogpilot_toggles.wheel_image) + } else: return diff --git a/frogpilot/common/frogpilot_variables.py b/frogpilot/common/frogpilot_variables.py index 2ab44d23e..a7abd092a 100644 --- a/frogpilot/common/frogpilot_variables.py +++ b/frogpilot/common/frogpilot_variables.py @@ -495,7 +495,12 @@ class FrogPilotVariables: toggle.icon_pack = self.get_value("IconPack", cast=None, condition=custom_themes, default="stock") toggle.signal_icons = self.get_value("SignalAnimation", cast=None, condition=custom_themes, default="stock") toggle.sound_pack = self.get_value("SoundPack", cast=None, condition=custom_themes, default="stock") - toggle.wheel_image = self.get_value("WheelIcon", cast=None, condition=custom_themes, default="stock") + toggle.random_themes = self.get_value("RandomThemes", condition=custom_themes) + toggle.random_themes_holidays = self.get_value("RandomThemesHolidays", condition=toggle.random_themes) + if toggle.random_themes: + toggle.wheel_image = random.choice([file.stem for file in (THEME_SAVE_PATH / "steering_wheels").iterdir() if file.is_file()] or ["stock"]) if (THEME_SAVE_PATH / "steering_wheels").exists() else "stock" + else: + toggle.wheel_image = self.get_value("WheelIcon", cast=None, condition=custom_themes, default="stock") custom_ui = self.get_value("CustomUI") toggle.acceleration_path = toggle.openpilot_longitudinal and (self.get_value("AccelerationPath", condition=custom_ui) or toggle.debug_mode) diff --git a/frogpilot/frogpilot_process.py b/frogpilot/frogpilot_process.py index 4e4330285..6fe11a12e 100644 --- a/frogpilot/frogpilot_process.py +++ b/frogpilot/frogpilot_process.py @@ -28,12 +28,15 @@ def check_assets(theme_manager, thread_manager, params_memory, frogpilot_toggles if params_memory.get_bool("FlashPanda"): thread_manager.run_with_lock(flash_panda, (params_memory)) -def transition_offroad(frogpilot_planner, thread_manager, time_validated, sm, params, frogpilot_toggles): +def transition_offroad(frogpilot_planner, theme_manager, thread_manager, time_validated, sm, params, frogpilot_toggles): params.put("LastGPSPosition", json.dumps(frogpilot_planner.gps_position)) if frogpilot_toggles.lock_doors_timer != 0: thread_manager.run_with_lock(lock_doors, (frogpilot_toggles.lock_doors_timer, sm, params), report=False) + if frogpilot_toggles.random_themes: + theme_manager.update_active_theme(time_validated, frogpilot_toggles, randomize_theme=True) + if time_validated: thread_manager.run_with_lock(send_stats, (params, frogpilot_toggles)) @@ -54,12 +57,18 @@ def update_checks(now, theme_manager, thread_manager, params, params_memory, fro time.sleep(1) -def update_toggles(frogpilot_variables, started, theme_manager, thread_manager, time_validated, params): +def update_toggles(frogpilot_variables, started, theme_manager, thread_manager, time_validated, params, frogpilot_toggles): + previous_holiday_themes = frogpilot_toggles.holiday_themes + previous_random_themes = frogpilot_toggles.random_themes + frogpilot_variables.update(theme_manager.holiday_theme, started) frogpilot_toggles = frogpilot_variables.frogpilot_toggles + randomize_theme = frogpilot_toggles.holiday_themes != previous_holiday_themes + randomize_theme |= frogpilot_toggles.random_themes != previous_random_themes + theme_manager.theme_updated = False - theme_manager.update_active_theme(time_validated, frogpilot_toggles) + theme_manager.update_active_theme(time_validated, frogpilot_toggles, randomize_theme=randomize_theme) if time_validated: thread_manager.run_with_lock(backup_toggles, (params)) @@ -103,8 +112,8 @@ def frogpilot_thread(): started = sm["deviceState"].started if not started and started_previously: - frogpilot_toggles = update_toggles(frogpilot_variables, started, theme_manager, thread_manager, time_validated, params) - transition_offroad(frogpilot_planner, thread_manager, time_validated, sm, params, frogpilot_toggles) + frogpilot_toggles = update_toggles(frogpilot_variables, started, theme_manager, thread_manager, time_validated, params, frogpilot_toggles) + transition_offroad(frogpilot_planner, theme_manager, thread_manager, time_validated, sm, params, frogpilot_toggles) run_update_checks = True elif started and not started_previously: @@ -130,7 +139,7 @@ def frogpilot_thread(): check_assets(theme_manager, thread_manager, params_memory, frogpilot_toggles) if params_memory.get_bool("FrogPilotTogglesUpdated") or theme_manager.theme_updated: - frogpilot_toggles = update_toggles(frogpilot_variables, started, theme_manager, thread_manager, time_validated, params) + frogpilot_toggles = update_toggles(frogpilot_variables, started, theme_manager, thread_manager, time_validated, params, frogpilot_toggles) run_update_checks |= params_memory.get_bool("ManualUpdateInitiated") run_update_checks |= now.second == 0 and (now.minute % 60 == 0 or (now.minute % 5 == 0 and frogpilot_variables.frogs_go_moo))