logo
Kurashizu Blogwhere ideas flow
  • Home
  • News
  • Blog
  • About
  • Home
  • News
  • Blog
  • About

© 2026 Kurashizu. All rights reserved.

Admin·New Post·Service Status
Powered by Cloudflare·31.10.26
← Back to news

A Planet Position Widget

Original article
leancrew.com|11 points|1 comments|by speckx|Aug 12, 2026

A Planet Position Widget

August 12, 2026 at 12:16 PM by Drang

After discovering how to utilize TerminalWidget for creating Mac widgets and leveraging Astropy to calculate the coordinates of celestial bodies, I decided to merge these two tools. The result is a custom widget designed to track the positions of the planets.

🌌 Scope and Definitions

For this project, I have adopted an outdated ancient definition of "planets." This means the list includes the Sun and the Moon, but excludes any celestial bodies located beyond Saturn.

BodyIncluded?
Sun✅
Moon✅
Mercury✅
Venus✅
Mars✅
Jupiter✅
Saturn✅
Uranus/Neptune❌

The widget displays two primary values: Azimuth and Altitude, rounded to the nearest degree.

Note: In one specific screenshot taken shortly after 10:00 PM, every object was listed as being below the horizon, rendering a trip outside unnecessary.

While most people don't need a widget to find the Sun, and other bodies are generally invisible during the day, I included the Sun for two reasons:

  1. To ensure the data set was complete.
  2. Because the visibility of other planets often depends on their angular separation from the Sun.

🛠️ The Technical Implementation

The core of the widget is a Python script titled planets.

The Logic Flow

The Source Code

import astropy.units as u 
from astropy.time import Time 
from astropy.coordinates import get_body, AltAz, EarthLocation 
from subprocess import run 

def direction(az): 
    'Return a string indication of the azimuth (given in degrees).' 
    dirs = 'N NNE NE ENE E ESE SE SSE S SSW SW WSW W WNW NW NNW'.split() 
    i = int(((az + 11.25) % 360) / 22.5) 
    return dirs[i] 

# Current time in UTC.
# (Time definition omitted for brevity in original snippet)

home = EarthLocation(lat=41.81433*u.deg, lon=-88.07093*u.deg, height=208*u.m) 

# Bodies of interest.
planets = 'Moon Sun Mercury Venus Mars Jupiter Saturn'.split() 

# Current positions of all the bodies.
pos = {} 
const = {} 
for p in planets: 
    pos[p] = get_body(p, ut).transform_to(AltAz(obstime=ut, location=home)) 
    const[p] = pos[p].get_constellation() 

# Assemble the results.
output = [] 
for p in planets: 
    output.append(f'{p: 8s}: {pos[p].az.value:3.0f} {direction(pos[p].az.value):3s} {pos[p].alt.value:3.0f} {const[p]}') 

# Pipe the results through TerminalWidget.
tw = '/Applications/TerminalWidget.app/Contents/MacOS/TerminalWidget --target planets --font Menlo --bg eeeeee --fg 000000 --text -'.split() 
run(tw, input='\n'.join(output).encode()) 

# print('\n'.join(output)) 

Breakdown of the Script:

  • Compass Conversion: The direction function converts a numerical azimuth (e.g., 223∘223^\circ223∘) into a human-readable compass point (e.g., "Southwest"). I wrote this manually because it's more intuitive than raw degrees.
  • The Math: The function utilizes a 16-point compass. Since a circle is 360∘360^\circ360∘, each point covers: 360∘16=22.5∘\frac{360^\circ}{16} = 22.5^\circ16360∘​=22.5∘ The formula ((az + 11.25) % 360) / 22.5 accounts for the fact that North is centered at 0∘0^\circ0∘ (ranging from 348.75∘348.75^\circ348.75∘ to 11.25∘11.25^\circ11.25∘).
  • Location: The coordinates in the code above point to the Morton Arboretum, though my personal version uses my home's specific latitude and longitude.
  • Data Retrieval: get_body fetches the position, which is then transformed into AltAz coordinates and used by get_constellation to identify the current constellation.
  • Output: The final strings are passed to the TerminalWidget executable via subprocess.run.
  • Debugging: Line 40 is a print statement I used during development to verify output in the Terminal before piping it to the widget.

⏱️ Automation via launchd

To keep the data fresh, the script runs every 30 minutes. This is handled by a launchd agent named com.leancrew.planets.plist.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.leancrew.planets</string>
    <key>ProgramArguments</key>
    <array>
        <string>/path/to/python</string>
        <string>/path/to/planets</string>
    </array>
    <key>StartCalendarInterval</key>
    <array>
        <dict>
            <key>Minute</key>
            <integer>0</integer>
        </dict>
        <dict>
            <key>Minute</key>
            <integer>30</integer>
        </dict>
    </array>
</dict>
</plist>

Key Configuration Details:

  • Executable: The first argument is the full path to Python (removing the need for a shebang line in the script).
  • Script: The second argument is the path to the planets script.
  • Schedule: The StartCalendarInterval ensures execution at :00 and :30 of every hour.

🌑 Final Thoughts: The Eclipse

Recently, the Chicago area experienced a partial solar eclipse where only 1% of the Sun was obscured. Unfortunately, 100% of the Sun was obscured by clouds, making it impossible to see the event visually. However, my widget confirmed that the eclipse was indeed occurring—just safely tucked away above the cloud layer.

Visuals

Planet position widget The widget in action.

Compass rose from Wikipedia The 16-point compass logic.

Planets widget near the solar eclipse time Tracking the solar eclipse.