Using DOT3K for weather forecast - few issues


#1

I’ve been trying to put together a Python script that collects the weather forecast then displays the results on the Display-o-Tron 3000 and I’ve almost got it figured out. I forked a project that used Dark Sky’s API and based the code on the initial example, but I’m struggling to get the updated weather forecast displayed correctly.

At the moment, everything works but the updated forecast isn’t being displayed on the DOT3K, so was hoping somebody here might be able to help out. I suspect it’s down to how the functions are created and how the variables are passed. Here’s my code so far:

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

from dot3k import joystick as nav
from dot3k import lcd
from dot3k import backlight as backlight
backlight.use_rbg() # Required for early-batch DOT3K's as the RGB LEDs are RBG.
from time import sleep
import os
from ConfigParser import ConfigParser

# Import details from config file to save typing
config = ConfigParser()
config.read('config/config.ini')
api_key = config.get('darksky', 'key')
latitude = config.get('darksky', 'latitude')
longitude = config.get('darksky', 'longitude')

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

# Get the forecast details from Dark Sky
forecast = forecastio.load_forecast(api_key,latitude,longitude)
current = forecast.currently()

# Get the temperature, humidity and chance of rain then
# convert to strings to display on DOT3K.
temp = current.temperature
temp = str(temp)
humidity = current.humidity*100
humidity = str(humidity)
rainInt = current.precipProbability*100
rain = str(rainInt)

def updateWeather():
    forecast.update()
    return current

def rainWarning():
    if rainInt <= 33:
        backlight.rgb(165, 255, 0)    # Green (good)
    elif (rainInt > 34) and (rainInt <= 74):
        backlight.rgb(226, 111, 11) # Orange (warning)
    else:
        backlight.rgb(0, 225, 255)  # Blue (you'll get wet!)

def graph():
    if rainInt ==0:
        backlight.set_graph(0)
    elif (rainInt >1) and (rainInt <=9):
        backlight.set_graph(0.1)
    elif (rainInt >= 10) and (rainInt <=19):
        backlight.set_graph(0.2)
    elif (rainInt >= 20) and (rainInt <=29):
        backlight.set_graph(0.3)
    elif (rainInt >= 30) and (rainInt <=39):
        backlight.set_graph(0.4)
    elif (rainInt >= 40) and (rainInt <=49):
        backlight.set_graph(0.5)
    elif (rainInt >= 50) and (rainInt <=59):
        backlight.set_graph(0.6)
    elif (rainInt >= 60) and (rainInt <=69):
        backlight.set_graph(0.7)
    elif (rainInt >= 70) and (rainInt <=79):
        backlight.set_graph(0.8)
    elif (rainInt >= 80) and (rainInt <=89):
        backlight.set_graph(0.9)
    else:
        backlight.set_graph(1.0)

# Press the button on the joystick to exit
@nav.on(nav.BUTTON)
def handle_button(pin):
    lcd.clear()
    backlight.rgb(0, 0, 0)
    backlight.set_graph(0)
    os._exit(1)

def display():
    try:
        current = updateWeather()
        lcd.clear()
        lcd.set_cursor_position(0, 0)
        print("Temperture: "+temp+" C")
        lcd.write("Temp: "+temp+" C")
        lcd.set_cursor_position(0, 1)
        print("Humidity: "+humidity+"%")
        lcd.write("Humidity: "+humidity+"%")
        lcd.set_cursor_position(0, 2)
        print("Rain: "+rain+"%")
        lcd.write("Rain: "+rain+"%")
    except:
        lcd.write("Connection Error")

while 1:
    updateWeather()
    display()
    rainWarning()
    graph()
    sleep(120)

My full repo is available here.

Basically, the weather data is pulled from Dark Sky and the current temperature, humidity and chance of rain are displayed.

If the chance of rain is low, the backlight is green and the bar graph is off. If there is a medium chance of rain, the backlight is orange and the bar graph lights up depending on the percentage and if the rain chance is high, the backlight is blue and the bar graph shows more lights.


#2

At the moment you’re only setting temp, humidity and rain once, at startup.

You’re also relying on global variables in some functions - such as rainWarning- which might be better structured with an argument to pass in the information they need since globals can get ugly quickly…


#3

Ahh, thanks @gadgetoid. I’ll do some digging on how to handle these variables better then - I was looking into using a class to keep everything together - I did try arguments, but it was a bit above my level of understanding so will go away and read up to see what I can figure out as I know you guys are busy!


#4

It’s not so much me being busy, as me being keen to- if I may- provide the right amount of guidance to let you have a revelation of your own, without actually providing a straight answer ;)

The set up you’re using at the moment isn’t so bad, but you need to be converting the temperature, humidity and rain and stringifying them every time you update and display the weather information.

Using function arguments would then make it easier to pass that converted data into things like your graph function:

def graph(rainInt):
    # do stuff

while 1:
    # calculate rainInt
    graph(rainInt)

#5

Ha, thanks. Love the approach by the way! I’ll give it a go and hopefully get over the line. Cheers Phil!


#6

I’m not the best writerer-of-tutorials, but you might find this useful if you haven’t already read it: https://learn.pimoroni.com/tutorial/python/python-arguments


#7

Thanks for the pointers @gadgetoid, but I’m still struggling a little.

Based on what you’ve said - about arguments etc., - is this something along the right lines:

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

from dot3k import joystick as nav
from dot3k import lcd
from dot3k import backlight as backlight
backlight.use_rbg() # Required for early-batch DOT3K's as the RGB LEDs are RBG.
from time import sleep
import os
from ConfigParser import ConfigParser

# Import details from config file to save typing
config = ConfigParser()
config.read('config/config.ini')
api_key = config.get('darksky', 'key')
latitude = config.get('darksky', 'latitude')
longitude = config.get('darksky', 'longitude')

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

# Get the forecast details from Dark Sky
forecast = forecastio.load_forecast(api_key,latitude,longitude)
current = forecast.currently()

# Get the temperature, humidity and chance of rain then
# convert to strings to display on DOT3K.
temp = current.temperature
temp = str(temp)
humidity = current.humidity*100
humidity = str(humidity)
rainInt = current.precipProbability*100
rain = str(rainInt)

# New, may not work - need some help!
def updateWeather():
    forecast = forecastio.load_forecast(api_key,latitude,longitude)
    current = forecast.currently()
    temp = current.temperature
    temp = str(temp)
    humidity = current.humidity*100
    humidity = str(humidity)
    rainInt = current.precipProbability*100
    rain = str(rainInt)

def rainWarning(rainInt):
    if rainInt <= 33:
        backlight.rgb(165, 255, 0)    # Green (good)
    elif (rainInt > 34) and (rainInt <= 74):
        backlight.rgb(226, 111, 11) # Orange (warning)
    else:
        backlight.rgb(0, 225, 255)  # Blue (you'll get wet!)

def graph(rainInt):
    if rainInt ==0:
        backlight.set_graph(0)
    elif (rainInt >1) and (rainInt <=9):
        backlight.set_graph(0.1)
    elif (rainInt >= 10) and (rainInt <=19):
        backlight.set_graph(0.2)
    elif (rainInt >= 20) and (rainInt <=29):
        backlight.set_graph(0.3)
    elif (rainInt >= 30) and (rainInt <=39):
        backlight.set_graph(0.4)
    elif (rainInt >= 40) and (rainInt <=49):
        backlight.set_graph(0.5)
    elif (rainInt >= 50) and (rainInt <=59):
        backlight.set_graph(0.6)
    elif (rainInt >= 60) and (rainInt <=69):
        backlight.set_graph(0.7)
    elif (rainInt >= 70) and (rainInt <=79):
        backlight.set_graph(0.8)
    elif (rainInt >= 80) and (rainInt <=89):
        backlight.set_graph(0.9)
    else:
        backlight.set_graph(1.0)

# Press the button on the joystick to exit
@nav.on(nav.BUTTON)
def handle_button(pin):
    lcd.clear()
    backlight.rgb(0, 0, 0)
    backlight.set_graph(0)
    os._exit(1)

def display(temp, humidity, rain):
    try:
        current = updateWeather()
        lcd.clear()
        lcd.set_cursor_position(0, 0)
        print("Temperture: "+temp+" C")
        lcd.write("Temp: "+temp+" C")
        lcd.set_cursor_position(0, 1)
        print("Humidity: "+humidity+"%")
        lcd.write("Humidity: "+humidity+"%")
        lcd.set_cursor_position(0, 2)
        print("Rain: "+rain+"%")
        lcd.write("Rain: "+rain+"%")
    except:
        lcd.write("Connection Error")

while 1:
    updateWeather()
    display(temp, humidity, rain)
    rainWarning(rainInt)
    graph(rainInt)
    sleep(120)

Basically, I’ve added the arguments temp, humidity, rain in the display() function and rainInt as an argument in the rainWarning() and graph() functions. Not tested yet as my Pi is at home, powered off (Nooooo!) but will try over the weekend if possible.


#8

Ok, so I couldn’t figure that all out so I’ve attempted another version, which now seems to work as expected. It’s probably hideous and hacky, plus I know it calls the Dark Sky API twice instead of just the once, which isn’t ideal, but I doubt the weather will change drastically in the short space of time anyway.

I’ve also tweaked the code so it now changes the colour of the DOT3K backlight according to the UV Index (green = low, yellow = moderate, orange = high, red = very high, violet = extreme) and then used the graph bar lights to show the chance of rain - the more lights, the higher the chance:

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

from dot3k import joystick as nav
from dot3k import lcd
from dot3k import backlight as backlight
backlight.use_rbg() # Required for early-batch DOT3K's as the RGB LEDs are RBG.
from time import sleep
import os
from ConfigParser import ConfigParser

# Import details from config file to save typing
config = ConfigParser()
config.read('config/config.ini')
api_key = config.get('darksky', 'key')
latitude = config.get('darksky', 'latitude')
longitude = config.get('darksky', 'longitude')

try:
    import forecastio
except ImportError:
    exit("This script requires the forecastio module\nInstall with: sudo pip install forecastio")
    
def rainWarning():
    forecast = forecastio.load_forecast(api_key,latitude,longitude)
    current = forecast.currently()
    rain = current.precipProbability*100
    if rain ==0:
        backlight.set_graph(0)
    elif (rain >1) and (rain <=9):
        backlight.set_graph(0.1)
    elif (rain >= 10) and (rain <=19):
        backlight.set_graph(0.2)
    elif (rain >= 20) and (rain <=29):
        backlight.set_graph(0.3)
    elif (rain >= 30) and (rain <=39):
        backlight.set_graph(0.4)
    elif (rain >= 40) and (rain <=49):
        backlight.set_graph(0.5)
    elif (rain >= 50) and (rain <=59):
        backlight.set_graph(0.6)
    elif (rain >= 60) and (rain <=69):
        backlight.set_graph(0.7)
    elif (rain >= 70) and (rain <=79):
        backlight.set_graph(0.8)
    elif (rain >= 80) and (rain <=89):
        backlight.set_graph(0.9)
    else:
        backlight.set_graph(1.0)

def display():
    forecast = forecastio.load_forecast(api_key,latitude,longitude)
    current = forecast.currently()
    temp = current.temperature
    temp = str(temp)
    humidity = current.humidity*100
    humidity = str(humidity)
    rain = current.precipProbability*100
    rain = str(rain)
    uvIndex = current.uvIndex
    uv = str(uvIndex)
    if uvIndex <=2.9:
        backlight.rgb(90, 148, 35)  # Green (low)
    elif (uvIndex >=3) and (uvIndex <=5.9):
        backlight.rgb(241, 227, 54) # Yellow (moderate)
    elif (uvIndex >=6) and (uvIndex <=7.9):
        backlight.rgb(217, 90, 18)  # Orange (high)
    elif (uvIndex >=8) and (uvIndex <=10.9):
        backlight.rgb(185, 2, 34)   # Red (very high)
    else:
        backlight.rgb(99, 74, 195)  # Violet (extreme)
    try:
        lcd.clear()
        lcd.set_cursor_position(0, 0)
        print("Temperture: "+temp+" C")
        lcd.write("Temp: "+temp+"\xf2C")    # Displays the degree symbol + C
        lcd.set_cursor_position(0, 1)
        print("Humidity: "+humidity+"%")
        lcd.write("Humidity: "+humidity+"%")
        lcd.set_cursor_position(0, 2)
        print("Rain: "+rain+"%")
        lcd.write("Rain: "+rain+"%")
        print("UV Index: "+uv+"")
    except:
        lcd.write("Connection Error")
    
    # Press the button on the joystick to exit
    @nav.on(nav.BUTTON)
    def handle_button(pin):
        lcd.clear()
        backlight.rgb(0, 0, 0)
        backlight.set_graph(0)
        os._exit(1)

while True:
    rainWarning()
    display()
    sleep(300)    # 5 minutes to prevent maxing the 1,000 API limit