As mentioned in a previous installment Hans was having some trouble installing the secure version of the requests module for python, it didn't matter what he tried there always seemed to be something that needed a different version of a tool installing or a script that did not want to run correctly. So Hans took some steps back and wrote the code to get the weather instead.

 

Thinking about the reason for using requests in the first place it was to make the code simpler, however the function to call the API was small and self contained so Hans realised he could swap that out with pycurl instead. So he installed the relevant packages.

 

opkg update
opkg install python-openssl
opkg install python-curl







 

And updated the Python script to use pycurl, he coded it so that either version could be used by swapping out call_api. For the pycurl commands to work an old version of the call was needed, c.setopt(c.WRITEFUNCTION, r.write) rather than c.setopt(c.WRITEDATA, r).

 

from StringIO import StringIO
import pycurl
import json, sys, urllib, urlparse

def get_location():
    with open('/etc/WeatherAPI.conf', 'r') as f:
        location_name = f.readline()
    f.close()
    return location_name


def build_url(location):
    url = "https://query.yahooapis.com/v1/public/yql"
    q = "select item.condition from weather.forecast where woeid in (select woeid from geo.places(1) where text = '" + urllib.quote(
        location) + "') and u='c'"
    params = {'q': q, 'format': 'json', 'env': 'store://datatables.org/alltableswithkeys'}

    url_parts = list(urlparse.urlparse(url))
    query = dict(urlparse.parse_qsl(url_parts[4]))
    query.update(params)

    url_parts[4] = urllib.urlencode(query)

    return urlparse.urlunparse(url_parts)

def call_api(url):
    r = StringIO()
    c = pycurl.Curl()
    c.setopt(c.URL, url)
    c.setopt(c.CONNECTTIMEOUT, 10)
    c.setopt(c.TIMEOUT, 60)
    c.setopt(c.WRITEFUNCTION, r.write)
    c.perform()
    c.close()
    return r.getvalue()

def map_code(code):
    # Map condition codes to expected precipitation
    # https://developer.yahoo.com/weather/documentation.html
    # 1 = very sunny, 3 = changeable, 5 = very rainy
    codes = {'0': {'n': 'tornado', 'p': '3'},
            '1': {'n': 'tropical storm', 'p': '5'},
            '2': {'n': 'hurricane', 'p': '5'},
            '3': {'n': 'severe thunderstorms', 'p': '5'},
            '4': {'n': 'thunderstorms', 'p': '5'},
            '5': {'n': 'mixed rain and snow', 'p': '5'},
            '6': {'n': 'mixed rain and sleet', 'p': '5'},
            '7': {'n': 'mixed snow and sleet', 'p': '5'},
            '8': {'n': 'freezing drizzle', 'p': '5'},
            '9': {'n': 'drizzle', 'p': '4'},
            '10': {'n': 'freezing rain', 'p': '5'},
            '11': {'n': 'showers', 'p': '4'},
            '12': {'n': 'showers', 'p': '4'},
            '13': {'n': 'snow flurries', 'p': '4'},
            '14': {'n': 'light snow showers', 'p': '4'},
            '15': {'n': 'blowing snow', 'p': '3'},
            '16': {'n': 'snow', 'p': '4'},
            '17': {'n': 'hail', 'p': '4'},
            '18': {'n': 'sleet', 'p': '4'},
            '19': {'n': 'dust', 'p': '3'},
            '20': {'n': 'foggy', 'p': '4'},
            '21': {'n': 'haze', 'p': '3'},
            '22': {'n': 'smoky', 'p': '3'},
            '23': {'n': 'blustery', 'p': '3'},
            '24': {'n': 'windy', 'p': '3'},
            '25': {'n': 'cold', 'p': '3'},
            '26': {'n': 'cloudy', 'p': '3'},
            '27': {'n': 'mostly cloudy (night)', 'p': '3'},
            '28': {'n': 'mostly cloudy (day)', 'p': '3'},
            '29': {'n': 'partly cloudy (night)', 'p': '3'},
            '30': {'n': 'partly cloudy (day)', 'p': '3'},
            '31': {'n': 'clear (night)', 'p': '1'},
            '32': {'n': 'sunny', 'p': '1'},
            '33': {'n': 'fair (night)', 'p': '2'},
            '34': {'n': 'fair (day)', 'p': '2'},
            '35': {'n': 'mixed rain and hail', 'p': '4'},
            '36': {'n': 'hot', 'p': '1'},
            '37': {'n': 'isolated thunderstorms', 'p': '4'},
            '38': {'n': 'scattered thunderstorms', 'p': '4'},
            '39': {'n': 'scattered thunderstorms', 'p': '4'},
            '40': {'n': 'scattered showers', 'p': '4'},
            '41': {'n': 'heavy snow', 'p': '5'},
            '42': {'n': 'scattered snow showers', 'p': '4'},
            '43': {'n': 'heavy snow', 'p': '5'},
            '44': {'n': 'partly cloudy', 'p': '2'},
            '45': {'n': 'thundershowers', 'p': '5'},
            '46': {'n': 'snow showers', 'p': '5'},
            '47': {'n': 'isolated thundershowers', 'p': '4'}}
    state = codes.get(code)
    if not state:
        raise RuntimeError('Unknown weather code returned')
    return state['p']


def parse_weather(json):
    text = json['query']['results']['channel']['item']['condition']['text'].replace(",", " ")
    code = map_code(json['query']['results']['channel']['item']['condition']['code'])
    temp = json['query']['results']['channel']['item']['condition']['temp']
    return {'text': text, 'code': code, 'temp': float(temp)}


def main():
    try:
        loc = get_location()
        weather_url = build_url(loc)
        r = call_api(weather_url)
        j = json.loads(r)
        weather = parse_weather(j)

        print 'OK!,%s,%s,%4.2f' % (weather['text'], weather['code'], weather['temp'])

        return 0

    except Exception, err:
        print 'Err,%s,0,0.0' % str(err).replace(",", " ")
        return 1


if __name__ == '__main__':
    sys.exit(main())







 

The first run reported an error with not being able to verify the certificate so he re-installed the certificates using a slightly modified script that downloaded the certificates from OpenWRT, as the Yun pakcage repository does not seem to have these.

 

#! /bin/sh
OPENSSL=/usr/bin/openssl
CERTDIR=/etc/ssl/certs

opkg update
#opkg install ca-certificates
wget http://downloads.openwrt.org/barrier_breaker/14.07/ar71xx/generic/packages/base/ca-certificates_20141019_ar71xx.ipk  
opkg install ca-certificates_20141019_ar71xx.ipk

# Install openssl-util if need
[ ! -f ${OPENSSL} ] && opkg update && opkg install openssl-util

for CERTFILE in ${CERTDIR}/*; do
    # create symbolic link from hash
    echo -en "Certificate ${CERTFILE##*/}\n generating hash: "
    HASH=$(${OPENSSL} x509 -hash -noout -in ${CERTFILE})
    echo "$HASH"

    # handle hash collision
    SUFFIX=0
    while [ -h "${CERTDIR}/${HASH}.${SUFFIX}" ]; do
        let "SUFFIX += 1"
    done

    echo "linking ${HASH}.${SUFFIX} -> ${CERTFILE##*/}"
    ln -s ${CERTFILE##*/} ${CERTDIR}/${HASH}.${SUFFIX}
done

rm ca-certificates_20141019_ar71xx.ipk







 

Running it a second time produced a result. Hans had never been so happy to know that it was Mostly Cloudy in Chicago, IL.

MostlyCloudy.png

Next: Enchanted Objects Design Challenge - The Glowing Golem and shiny battery box