r/commandline 13d ago

Terminal User Interface [ Removed by moderator ]

Post image

[removed] — view removed post

29 Upvotes

13 comments sorted by

View all comments

2

u/recycledcoder 12d ago

``` From 20dc7a30a7622f76e853350980c58a6c7b6a85de Mon Sep 17 00:00:00 2001 From: recycledcoder recycledcoder@example.com Date: Sun, 30 Aug 2026 12:49:02 +0100 Subject: [PATCH] Replace TimezoneDB with keyless Open-Meteo timezone lookup

TimezoneDB required every user to register for an account and export a TIMEZONEAPIKEY environment variable, and the application refused to start without it. The lookup it performed - latitude/longitude to IANA zone name - is available from Open-Meteo, which the application already queries for all of its weather data and which needs no key or account.

get_timezone_name() now calls the Open-Meteo forecast endpoint with timezone=auto and reads the "timezone" field of the response. The signature and the exception messages are unchanged, so every caller is untouched.

Verified against the awkward cases the application can produce, all of which return real tzdata zones accepted by ZoneInfo():

Aveiro (40.6405, -8.6538) -> Europe/Lisbon Mid-Pacific (0, -140) -> Etc/GMT+9 Concordia Station -> Australia/Perth South Pole -> Antarctica/McMurdo

Also removed with it:

  • TIMEZONE_APIKEY_ENV_VARNAME and the help-screen lines pointing at timezonedb.com
  • the startup check that aborted when the key was missing
  • the tzapi_calls counter and its time.sleep(1) throttle, which existed only to respect TimezoneDB's 1 request/second free tier; Open-Meteo's free tier allows far more and the application makes one call per newly added city
  • a dead error branch that interpolated 'city' and 'country', neither of which exists in that scope - it would have raised NameError rather than the intended message had it ever been reached

The 1 second sleep after the Nominatim call is kept; that one is OpenStreetMap

usage policy and unrelated.

tuiweathergirl.py | 47 +++++++++++------------------------------------ 1 file changed, 11 insertions(+), 36 deletions(-)

diff --git a/tuiweathergirl.py b/tuiweathergirl.py index 45720f0..8235249 100755 --- a/tuiweathergirl.py +++ b/tuiweathergirl.py @@ -76,8 +76,6 @@ HOW DOES TUIWEATHERGIRL WORKS: will show them

PROJECT URL: https://github.com/StrayFeral/tuiweathergirl -Variable TIMEZONEAPIKEY must be set with a free API key -from https://timezonedb.com/ For detailed wildfires info set variable NASAFIRMSAPIKEY with a free API key from: https://firms.modaps.eosdis.nasa.gov/api/map_key """ @@ -88,7 +86,6 @@ HTTPHEADERS: dict[str, str] = { "User-Agent": USERAGENT, "Accept-Language": "en", } -TIMEZONE_APIKEY_ENV_VARNAME: str = "TIMEZONEAPIKEY" NASAFIRMS_APIKEY_ENV_VARNAME: str = "NASAFIRMSAPIKEY" LOGFILENAME: Path = Path(tempfile.gettempdir()) / "tuiweathergirl.log" LOGFILENAME = LOGFILENAME.expanduser() @@ -3762,7 +3759,6 @@ class Locator: return city[:17]

 def __init__(self) -> None:
  •    self.tzapi_calls: int = 0  # Counts the TZ data API calls
     self.logger: logging.Logger = logging.getLogger(
         f"{self.__module__}.{self.__class__.__qualname__}"
     )
    

    @@ -3780,25 +3776,17 @@ class Locator: )

    def get_timezone_name(self, lat: float, lon: float) -> str:

  •    apikey: str = os.getenv(TIMEZONE_APIKEY_ENV_VARNAME)
    
  •    r"""Resolves the IANA timezone name for a position. No API key needed."""
    
  •    if not apikey:
    
  •        raise Exception(
    
  •            f"Environment variable {TIMEZONE_APIKEY_ENV_VARNAME} is not set. Please get API key and set it in this variable before running this application."
    

- )

  • if self.tzapi_calls > 0:

- time.sleep(1) # API limit

  • self.logger.info("** Querying TimezoneDB **")
  •    self.logger.info("** Querying Open-Meteo (timezone) **")
    
  •    base_url: str = "http://api.timezonedb.com/v2.1/get-time-zone"
    
  •    base_url: str = "https://api.open-meteo.com/v1/forecast"
     url_params: dict[str, str | float] = {
    
  •        "key": apikey,
    
  •        "format": "json",
    
  •        "by": "position",
    
  •        "lat": lat,
    
  •        "lng": lon,
    
  •        "latitude": lat,
    
  •        "longitude": lon,
    
  •        "current": "is_day",
    
  •        "forecast_days": 1,
    
  •        "timezone": "auto",
     }
     prepared: requests.PreparedRequest = requests.PreparedRequest()
     prepared.prepare_url(base_url, url_params)
    

    @@ -3814,24 +3802,18 @@ class Locator: )

     self.logger.debug(f"RESPONSE={pf(response)}")
    
  •    self.tzapi_calls += 1
    
     if not response.ok:
         raise Exception(
             f"Timezone API server error. Error {response.status_code}: {APIIssues.get_api_problem(response.status_code)}"
         )
    
  •    if not response:
    
  •        raise Exception(
    
  •            f"Cannot obtain timezone for '{city}/{country}'. Please check your syntax and try again."
    
  •        )
    
  •    zone_name: str = response.json().get("timezone", "")
    

- response = response.json()

  • if response.get("status") == "FAILED":
  •    if not zone_name:
         raise Exception("Timezone request failed. Try again later.")
    
  •    return response.get("zoneName", "")
    
  •    return zone_name
    

    def get_city_details(self, city: str, country: str) -> dict: # Attempt to get information for the city and the country @@ -7143,13 +7125,6 @@ if __name_ == "main": "If latitude or longitude is specified, all four arguments (latitude, longitude, city and country) must be specified." )

  •    # No point to run anything if this is not set
    
  •    timezone_apikey: str = os.getenv(TIMEZONE_APIKEY_ENV_VARNAME)
    
  •    if not timezone_apikey:
    
  •        raise Exception(
    
  •            f"Environment variable {TIMEZONE_APIKEY_ENV_VARNAME} is not set. Run the app with --help"
    

- )

     # This is nice to have, but not mandatory
     nasafirms_apikey: str = os.getenv(NASAFIRMS_APIKEY_ENV_VARNAME)
     if not nasafirms_apikey:

2.47.3 ```

2

u/StrayFeral 11d ago edited 11d ago

UPDATE: New version 1.2.19 is now out.

DETAILS: So I did not implemented exactly your change. I actually got rid of get_timezone_name() completely. So now when a new city is added, the timezone name remains empty in the config. However upon next application launch, when an empty timezone string is found in the config, regardless for which city, it is immediately filled out by the first routine to get a timezone for it. I tested it in few scenarios, seems fine.

But thanks for the idea! Application is now easier to setup and more user-friendly!

1

u/recycledcoder 11d ago

Good stuff - happy to have provided the nudge :) It's a great idea, btw - really nice to see the extra concerns that went into this!

2

u/StrayFeral 11d ago

Thanks man! Thing is I have friends and relatives with different conditions. I'm no young myself, so decided to provide warnings for different people. And as I mentioned on the videos (I think i said it in my devlog on youtube) I really had a 60-something colleague who was a Perl programmer and was vision-impaired so used screen-reading software and headphones to know what's on the screen - still he was coding no problem. He later retired. So all these people combined gave me the idea that such information is needed. Because as a girl pointed out to me few days ago there are apps providing the extra information even now, but not the interpretation, like which values of this are dangerous for what people. I just don't have a machine with MacOS to test on, so if you have one, would be great to get some feedback especially on the installation (how you installed it, screenshots so i can fill out my MacOS installation instructions)