Skip to content

Add SkyKnow - rain & snow precipitation alerts#414

Merged
tavdog merged 2 commits intotronbyt:mainfrom
wurtz:add-skyknow
Mar 20, 2026
Merged

Add SkyKnow - rain & snow precipitation alerts#414
tavdog merged 2 commits intotronbyt:mainfrom
wurtz:add-skyknow

Conversation

@wurtz
Copy link
Contributor

@wurtz wurtz commented Mar 19, 2026

Summary

  • Adds SkyKnow, a weather app that monitors upcoming rain and snow events
  • Fetches forecasts from Open-Meteo (free, no API key required)
  • Displays precipitation type, timing, probability, and accumulation
  • Scrolls through multiple events chronologically
  • Prompts user to set location via schema if not configured

Details

  • Author: wurtz
  • Category: weather
  • Recommended interval: 5 minutes
  • API: Open-Meteo (open source, no key needed)

Test plan

  • App renders correctly with a configured location
  • App displays "set location" prompt when no location is configured
  • Snow and rain events display with correct icons and colors
  • Scrolls through multiple upcoming events

🤖 Generated with Claude Code

Displays upcoming rain and snow events from Open-Meteo forecasts
with timing, probability, and accumulation details.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@wurtz wurtz requested a review from tavdog as a code owner March 19, 2026 10:15
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces SkyKnow, a new weather application designed to provide users with timely alerts for impending rain and snow. The app integrates with the Open-Meteo API to fetch forecast data, which is then processed and presented in an easily digestible format, highlighting key details like precipitation type, timing, and severity. It aims to enhance user awareness of upcoming weather changes, ensuring they are prepared for varying conditions.

Highlights

  • New Application: SkyKnow: Introduced SkyKnow, a new weather application designed to monitor and alert users about upcoming rain and snow events.
  • Open-Meteo API Integration: Integrated with the Open-Meteo API to fetch weather forecasts, notably requiring no API key for usage.
  • Detailed Precipitation Alerts: Displays comprehensive precipitation details including type (rain/snow), timing, probability, and accumulation estimates.
  • Chronological Event Scrolling: Features the ability to scroll through multiple detected weather events in chronological order.
  • Location Configuration Prompt: Includes a user-friendly prompt to set the location via schema if it has not been configured.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new weather app, SkyKnow, for displaying precipitation alerts. The implementation is comprehensive, featuring API integration, data parsing, animations, and various test modes. My review focuses on improving code maintainability and ensuring adherence to the repository's style guide. The main suggestions involve externalizing large data structures and base64-encoded images, simplifying some logic, and removing unused code. Additionally, please note that the required preview images, as specified in the style guide, have not been included in this pull request.

Comment on lines +36 to +145
SNOW_PARTICLES = [
(5, 2, 0.5, 0),
(15, 22, 0.6, 2),
(28, 8, 0.4, 1),
(42, 28, 0.7, 3),
(55, 14, 0.5, 4),
(10, 18, 0.6, 5),
(35, 5, 0.4, 2),
(50, 25, 0.8, 1),
(20, 12, 0.5, 3),
(60, 20, 0.6, 0),
(8, 28, 0.7, 4),
(48, 10, 0.5, 2),
]
RAIN_PARTICLES = [
(3, 1, 1.5, 0),
(12, 9, 1.8, 0),
(22, 17, 1.4, 0),
(30, 5, 2.0, 0),
(38, 25, 1.6, 0),
(46, 13, 1.9, 0),
(54, 21, 1.5, 0),
(8, 29, 1.7, 0),
(18, 7, 2.0, 0),
(33, 15, 1.8, 0),
(48, 3, 1.4, 0),
(58, 23, 1.6, 0),
(26, 11, 1.9, 0),
(41, 19, 1.5, 0),
(10, 25, 1.7, 0),
(52, 8, 2.0, 0),
]

# Interval options: value is hours, forecast_days is API param
INTERVAL_OPTIONS = {
"24": 1,
"48": 2,
"72": 3,
"120": 5,
"168": 7,
}

# ─── Pixel art icons (8x8 base64-encoded PNGs) ───

ICON_SNOW_LIGHT = """iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAGklEQVR4nGNgQAL/gYABF/iPBPAqwik5mAEA2w4T7X2e/9EAAAAASUVORK5CYII="""

ICON_SNOW_MEDIUM = """iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAJklEQVR4nGNgQAL/gYABF/iPBPAqwimJF6DrROGj243VLXhNIAQAAqw7xYlDpPsAAAAASUVORK5CYII="""

ICON_SNOW_HEAVY = """iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAKklEQVR4nGNggIJp06b9R8YMyABdEkURLkmsJuEE/4GAEBu7AC5BkhQCAMFiUXPU/Pu7AAAAAElFTkSuQmCC"""

ICON_RAIN_LIGHT = """iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAIklEQVR4nGNgQAIpq/7/Z8AFQJIuHf//41REUAFBKwYQAACc3Bfd0qQO/gAAAABJRU5ErkJggg=="""

ICON_RAIN_MEDIUM = """iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAJUlEQVR4nGNgQAIuHf//M+ACIEkYxqmIaIBiCrqRGFZRz15cAABYzx6vpJUFKQAAAABJRU5ErkJggg=="""

ICON_RAIN_HEAVY = """iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAKUlEQVR4nGNggIJp06b9R8YMyABdEkURLkmsJuEELh3//2OjiVdAkSkApxZTC1LzE2gAAAAASUVORK5CYII="""

# ─── Frog sprite (13x11 pixels) for All Clear screen ───

FROG_COLORS = {
"D": "#2B7A2B", # Dark green (body, head, outline)
"G": "#45A845", # Green (lighter body sides)
"Y": "#E2BB38", # Yellow (belly)
"K": "#1E5E1E", # Eyelid (heavy drooping lids)
"E": "#CCCC55", # Eye (exposed eye slit)
"P": "#111111", # Pupil dot
"T": "#3D9E3D", # Toe pads
}

# Frog sprite states (13 chars wide × 11 rows)
FROG_IDLE = [
"000DDDDDDD000",
"0KDDDDDDDDDK0",
"0KKDDDDDDDKK0",
"0EPEDDDDDEPE0",
"0DDDDDDDDDDD0",
"0DDDDDDDDDDD0",
"DGGYYYYYYYGGD",
"DGGYYYYYYYGGD",
"DGGYYYYYYYGGD",
"0DDDDDDDDDDD0",
"0TT00DDD00TT0",
]

FROG_BLINK = [
"000DDDDDDD000",
"0KDDDDDDDDDK0",
"0KKDDDDDDDKK0",
"0KKDDDDDDDKK0", # Lids shut
"0DDDDDDDDDDD0",
"0DDDDDDDDDDD0",
"DGGYYYYYYYGGD",
"DGGYYYYYYYGGD",
"DGGYYYYYYYGGD",
"0DDDDDDDDDDD0",
"0TT00DDD00TT0",
]

FROG_PUFF = [
"000DDDDDDD000",
"0KDDDDDDDDDK0",
"0KKDDDDDDDKK0",
"0EPEDDDDDEPE0",
"0DDDDDDDDDDD0",
"0DDYYYYYYYDD0", # Yellow creeps up
"DGGYYYYYYYGGD",
"DGGYYYYYYYGGD",
"DGGYYYYYYYGGD",
"0DDDDDDDDDDD0",
"0TT00DDD00TT0",
]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

These large data structures (SNOW_PARTICLES, RAIN_PARTICLES, FROG_COLORS, FROG_IDLE, etc.) decrease the readability of the main application logic. To improve maintainability, consider moving them into a separate helper file (e.g., sky_know_data.star) and loading them as needed.

References
  1. To improve readability and maintainability, move large data structures into a separate file and load them into the main application logic.

Comment on lines +80 to +90
ICON_SNOW_LIGHT = """iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAGklEQVR4nGNgQAL/gYABF/iPBPAqwik5mAEA2w4T7X2e/9EAAAAASUVORK5CYII="""

ICON_SNOW_MEDIUM = """iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAJklEQVR4nGNgQAL/gYABF/iPBPAqwimJF6DrROGj243VLXhNIAQAAqw7xYlDpPsAAAAASUVORK5CYII="""

ICON_SNOW_HEAVY = """iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAKklEQVR4nGNggIJp06b9R8YMyABdEkURLkmsJuEE/4GAEBu7AC5BkhQCAMFiUXPU/Pu7AAAAAElFTkSuQmCC"""

ICON_RAIN_LIGHT = """iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAIklEQVR4nGNgQAIpq/7/Z8AFQJIuHf//41REUAFBKwYQAACc3Bfd0qQO/gAAAABJRU5ErkJggg=="""

ICON_RAIN_MEDIUM = """iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAJUlEQVR4nGNgQAIuHf//M+ACIEkYxqmIaIBiCrqRGFZRz15cAABYzx6vpJUFKQAAAABJRU5ErkJggg=="""

ICON_RAIN_HEAVY = """iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAKUlEQVR4nGNggIJp06b9R8YMyABdEkURLkmsJuEELh3//2OjiVdAkSkApxZTC1LzE2gAAAAASUVORK5CYII="""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Embedding images as base64 strings is strongly discouraged by the repository's style guide as it increases file size and pollutes the context. These icons should be saved as external .png or .gif files and loaded using render.Image.

References
  1. It is strongly recommended to not embed images as base64-encoded strings inside the Starlark file. This is because the base64 overhead results in a roughly 33% size increase and the long strings pollute the context for AI agents. Apps should load images from external files instead. (link)

Comment on lines +167 to +168
interval_hours = int(config.get("interval", "48"))
forecast_days = INTERVAL_OPTIONS.get(str(interval_hours), 2)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The value from config.get("interval") is retrieved, converted to an integer, and then immediately converted back to a string for the dictionary lookup. This can be made more efficient and readable by storing the string value from the configuration and reusing it.

For example:
interval_str = config.get("interval", "48")
interval_hours = int(interval_str)
forecast_days = INTERVAL_OPTIONS.get(interval_str, 2)

interval_str = config.get("interval", "48")
interval_hours = int(interval_str)
forecast_days = INTERVAL_OPTIONS.get(interval_str, 2)

Comment on lines +410 to +417
url = (
"https://api.open-meteo.com/v1/forecast" +
"?latitude=%s&longitude=%s" % (lat, lng) +
"&hourly=snowfall,rain,precipitation_probability,weathercode" +
"&temperature_unit=fahrenheit" +
"&timezone=%s" % tz +
"&forecast_days=%d" % forecast_days
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The URL construction is complex due to multiple string concatenations and formatting operations. This can be simplified into a single, more readable formatting operation.

For example:
url = (
"https://api.open-meteo.com/v1/forecast"
"?latitude=%s&longitude=%s"
"&hourly=snowfall,rain,precipitation_probability,weathercode"
"&temperature_unit=fahrenheit"
"&timezone=%s"
"&forecast_days=%d"
) % (lat, lng, tz, forecast_days)

url = (
    "https://api.open-meteo.com/v1/forecast"
    "?latitude=%s&longitude=%s"
    "&hourly=snowfall,rain,precipitation_probability,weathercode"
    "&temperature_unit=fahrenheit"
    "&timezone=%s"
    "&forecast_days=%d"
) % (lat, lng, tz, forecast_days)

Comment on lines +833 to +848
def format_time_window(start_str, end_str):
start_parts = start_str.split("T")
end_parts = end_str.split("T")

start_date = start_parts[0]
start_hour = int(start_parts[1].split(":")[0])
end_date = end_parts[0]
end_hour = int(end_parts[1].split(":")[0])

start_day = get_day_abbr(start_date)
end_day = get_day_abbr(end_date)

start_fmt = "%s %s" % (start_day, format_hour(start_hour))
end_fmt = "%s %s" % (end_day, format_hour(end_hour))

return "%s-%s" % (start_fmt, end_fmt)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The function format_time_window is defined but never called within the script. Unused code should be removed to improve code clarity and maintainability.

- Extract 6 base64 icons to images/ as PNG files per style guide
- Remove unused format_time_window function
- Simplify interval config: avoid redundant int→str conversion

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@tavdog tavdog merged commit f3dd76b into tronbyt:main Mar 20, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants