pimoroni / breakout-garden

Documentation, software, and examples for the Breakout Garden ecosystem.
https://shop.pimoroni.com/products/breakout-garden-hat
MIT License
72 stars 14 forks source link

Weather API #31

Open Richard238 opened 1 year ago

Richard238 commented 1 year ago

For the weather.py example, what's the new suggested, recommended source API?

I ask as it appears the Dark Sky API is no longer available.

Thank you.

helgibbons commented 1 year ago

We've been enjoying using the OpenMeteo API for weather stuff!

I recently updated the Inky pHAT weather example to use OpenMeteo as its data source instead of DarkSky - looks like the weather example in this repo is quite similar if you wanted to modify this one in the same way?

Richard238 commented 1 year ago

I'm probably out of my depth with that kind of thing TBH. I'll give it a try when I get time...

Richard238 commented 1 year ago

NameError: name 'sh1106' is not defined

#!/usr/bin/env python3
## -*- coding: utf-8 -*-

import os
import time
import datetime
import glob
import logging
import json
from demo_opts import get_device
from font_fredoka_one import FredokaOne
from PIL import Image, ImageDraw, ImageFont

try:
    from smbus2 import SMBus
except ImportError:
    from smbus import SMBus
from bme280 import BME280
from sys import exit

try:
    import requests
except ImportError:
    exit("This script requires the requests module\nInstall with: sudo pip install requests")

try:
    import geocoder
except ImportError:
    exit("This script requires the geocoder module\nInstall with: sudo pip install geocoder")

print("""Test""")

CITY = "Sheffield"
COUNTRYCODE = "GB"
WARNING_TEMP = 25.0

# Used to calibrate the sensor
TEMP_OFFSET = 0.0

logging.basicConfig(level=os.environ.get("LOGLEVEL", "WARNING"))

### using BME280 not 680 ###
print("""This Pimoroni Breakout Garden example requires a
BME680 Environmental Sensor Breakout and a 1.12" OLED Breakout.
This example turns your Breakout Garden into a mini weather display
combining indoor temperature and pressure data with a weather icon
indicating the current local weather conditions.
Press Ctrl+C a couple times to exit.
""")

# Convert a city name and country code to latitude and longitude
def get_coords(address):
    g = geocoder.arcgis(address)
    coords = g.latlng
    return coords

# Query OpenMeteo (https://open-meteo.com) to get current weather data
def get_weather(address):
    coords = get_coords(address)
    weather = {}
    res = requests.get("https://api.open-meteo.com/v1/forecast?latitude=" + str(coords[0]) + "&longitude=" + str(coords[1]) + "&current_weather=true")
    if res.status_code == 200:
        j = json.loads(res.text)
        current = j["current_weather"]
        weather["temperature"] = current["temperature"]
        weather["windspeed"] = current["windspeed"]
        weather["weathercode"] = current["weathercode"]
        return weather
    else:
        return weather        

  icon_map = {
    "snow": [71, 73, 75, 77, 85, 86],
    "rain": [51, 53, 55, 56, 57, 61, 63, 65, 66, 67, 80, 81, 82],
    "cloud": [1, 2, 3, 45, 48],
    "sun": [0],
    "storm": [95, 96, 99],
    "wind": []
}

# Pre-load icons into a dictionary with PIL
icons = {}

for icon in glob.glob("icons/*.png"):
    icon_name = icon.split("/")[1].replace(".png", "")
    icon_image = Image.open(icon)
    icons[icon_name] = icon_image

location_string = "{city}, {countrycode}".format(city=CITY,
                                                 countrycode=COUNTRYCODE)
coords = get_coords(location_string)

def get_weather_icon(weather):
    if weather:
        summary = weather["summary"]

        for icon in icon_map:
            if summary in icon_map[icon]:
                logging.info("Weather icon: %s", icon)
                return icons[icon]
        logging.error("Could not determine icon for weather")
        return None
    else:
        logging.error("No weather information provided to get icon")
        return None

# Get initial weather data for the given location
#weather_icon = get_weather_icon(get_weather(coords))

# Get the weather data for the given location
location_string = "{city}, {countrycode}".format(city=CITY, countrycode=COUNTRYCODE)
weather = get_weather(location_string)

# Set up OLED
# Launching from terminal, so don't seem to need this?
oled = sh1106(spi(port=0, device=1, gpio_DC=9), rotate=2, height=128, width=128)

# Initialise the BME280
bus = SMBus(1)
bme280 = BME280(i2c_dev=bus)
temperature = bme280.get_temperature()
pressure = bme280.get_pressure()
humidity = bme280.get_humidity()
print('{:05.2f}*C {:05.2f}hPa {:05.2f}%'.format(temperature, pressure, humidity))

last_checked = time.time()
# device = get_device()
device = oled.display

# Main loop
while True:
    # Limit calls to Dark Sky to 1 per minute
    if time.time() - last_checked > 60:
        weather_icon = get_weather_icon(get_weather(coords))
        last_checked = time.time()
       #device = get_device()

    # Load in the background image
    # Use random image until it works
    background = Image.open("images/runner.png").convert(device)

    # Place the weather icon and draw the background
    if weather_icon:
        background.paste(weather_icon, (10, 46))
    draw = ImageDraw.ImageDraw(background)

    # Gets temp. and press. and keeps track of daily min and max temp
    if sensor.get_sensor_data():
        temp = sensor.data.temperature
        press = sensor.data.pressure
        if datetime.datetime.today().day == curr_date: 
            if temp < low_temp:
                low_temp = temp
            elif temp > high_temp:
                high_temp = temp
        else:
            curr_date = datetime.datetime.today().day
            low_temp = temp
            high_temp = temp

        # Write temp. and press. to image
        draw.text((8, 22), "{0:4.0f}".format(press),
                  fill="white", font=rb_20)
        draw.text((86, 12), u"{0:2.0f}°".format(temp),
                  fill="white", font=rb_20)

        # Write min and max temp. to image
        draw.text((80, 0), u"max: {0:2.0f}°".format(high_temp),
                  fill="white", font=rr_12)
        draw.text((80, 110), u"min: {0:2.0f}°".format(low_temp),
                  fill="white", font=rr_12)

    # Write the 24h time and blink the separator every second
    if int(time.time()) % 2 == 0:
        draw.text((4, 98), datetime.datetime.now().strftime("%H:%M"),
                  fill="white", font=rr_24)
    else:
        draw.text((4, 98), datetime.datetime.now().strftime("%H %M"),
                  fill="white", font=rr_24)

    # These lines display the temp. on the thermometer image
    draw.rectangle([(97, 43), (100, 86)], fill="black")
    temp_offset = 86 - ((86 - 43) * ((temp - 20) / (32 - 20)))
    draw.rectangle([(97, temp_offset), (100, 86)], fill="white")

    # Display the completed image on the OLED
    oled.display(background)

    time.sleep(TEMPERATURE_UPDATE_INTERVAL)
helgibbons commented 1 year ago

Looks like you're missing the imports for the OLED display?

from luma.core.interface.serial import spi
from luma.core.error import DeviceNotFoundError
from luma.oled.device import sh1106