Skip to main content

Let's Build a Wttr Client in Golang

·1136 words·6 mins·
Table of Contents
Building a Wttr client in Go - This article is part of a series.
Part 1: This Article

Introduction
#

In this series, we’re going to build a simple wttr client in golang. Wttr is a simple API that returns the weather as a pretty string. For example calling curl https://wttr.in/honolulu will return something like the following

Weather report: honolulu

     \  /       Partly cloudy
   _ /"".-.     +23(25) °C
     \_(   ).   ↑ 11 km/h
     /(___(__)  16 km
                0.1 mm
                                                       ┌─────────────┐
┌──────────────────────────────┬───────────────────────┤  Fri 14 Feb ├───────────────────────┬──────────────────────────────┐
│            Morning           │             Noon      └──────┬──────┘     Evening           │             Night            │
├──────────────────────────────┼──────────────────────────────┼──────────────────────────────┼──────────────────────────────┤
│  _`/"".-.     Patchy rain ne…│  _`/"".-.     Patchy rain ne…│  _`/"".-.     Patchy rain ne…│     \   /     Clear          │
│   ,\_(   ).   +24(26) °C     │   ,\_(   ).   +25(26) °C     │   ,\_(   ).   +24(26) °C     │      .-.      +23(25) °C     │
│    /(___(__)  ↑ 12-16 km/h   │    /(___(__)  ↑ 11-13 km/h   │    /(___(__)  ← 3 km/h       │   ― (   ) ―   ↙ 9-14 km/h    │
│      ‘ ‘ ‘ ‘  10 km          │      ‘ ‘ ‘ ‘  10 km          │      ‘ ‘ ‘ ‘  10 km          │      `-’      10 km          │
│     ‘ ‘ ‘ ‘   0.0 mm | 72%   │     ‘ ‘ ‘ ‘   0.0 mm | 72%   │     ‘ ‘ ‘ ‘   0.1 mm | 100%  │     /   \     0.0 mm | 0%    │
└──────────────────────────────┴──────────────────────────────┴──────────────────────────────┴──────────────────────────────┘
                                                       ┌─────────────┐
┌──────────────────────────────┬───────────────────────┤  Sat 15 Feb ├───────────────────────┬──────────────────────────────┐
│            Morning           │             Noon      └──────┬──────┘     Evening           │             Night            │
├──────────────────────────────┼──────────────────────────────┼──────────────────────────────┼──────────────────────────────┤
│     \   /     Sunny          │     \   /     Sunny          │     \   /     Sunny          │     \   /     Clear          │
│      .-.      +24(25) °C     │      .-.      +25(26) °C     │      .-.      +24(26) °C     │      .-.      +23(25) °C     │
│   ― (   ) ―   ↙ 19-24 km/h   │   ― (   ) ―   ← 22-25 km/h   │   ― (   ) ―   ← 18-24 km/h   │   ― (   ) ―   ↙ 11-16 km/h   │
│      `-’      10 km          │      `-’      10 km          │      `-’      10 km          │      `-’      10 km          │
│     /   \     0.0 mm | 0%    │     /   \     0.0 mm | 0%    │     /   \     0.0 mm | 0%    │     /   \     0.0 mm | 0%    │
└──────────────────────────────┴──────────────────────────────┴──────────────────────────────┴──────────────────────────────┘
                                                       ┌─────────────┐
┌──────────────────────────────┬───────────────────────┤  Sun 16 Feb ├───────────────────────┬──────────────────────────────┐
│            Morning           │             Noon      └──────┬──────┘     Evening           │             Night            │
├──────────────────────────────┼──────────────────────────────┼──────────────────────────────┼──────────────────────────────┤
│     \   /     Sunny          │  _`/"".-.     Patchy rain ne…│  _`/"".-.     Light rain sho…│  _`/"".-.     Patchy light d…│
│      .-.      +24(25) °C     │   ,\_(   ).   +25(27) °C     │   ,\_(   ).   +24(26) °C     │   ,\_(   ).   +24(26) °C     │
│   ― (   ) ―   ← 2-3 km/h     │    /(___(__)  ↑ 6-7 km/h     │    /(___(__)  ↑ 3-4 km/h     │    /(___(__)  ↖ 2-3 km/h     │
│      `-’      10 km          │      ‘ ‘ ‘ ‘  10 km          │      ‘ ‘ ‘ ‘  10 km          │      ‘ ‘ ‘ ‘  5 km           │
│     /   \     0.0 mm | 0%    │     ‘ ‘ ‘ ‘   0.0 mm | 68%   │     ‘ ‘ ‘ ‘   0.6 mm | 100%  │     ‘ ‘ ‘ ‘   0.3 mm | 100%  │
└──────────────────────────────┴──────────────────────────────┴──────────────────────────────┴──────────────────────────────┘
Location: Honolulu, Honolulu County, Hawaii, United States of America [21.304547,-157.8556763]

Follow @igor_chubin for wttr.in updates

To see all possible options call the help url with curl https://wttr.in/:help. At the time of writing the following options are available:

Usage:

    $ curl wttr.in          # current location
    $ curl wttr.in/muc      # weather in the Munich airport

Supported location types:

    /paris                  # city name
    /~Eiffel+tower          # any location (+ for spaces)
    /Москва                 # Unicode name of any location in any language
    /muc                    # airport code (3 letters)
    /@stackoverflow.com     # domain name
    /94107                  # area codes
    /-78.46,106.79          # GPS coordinates

Moon phase information:

    /moon                   # Moon phase (add ,+US or ,+France for these cities)
    /moon@2016-10-25        # Moon phase for the date (@2016-10-25)

Units:

    m                       # metric (SI) (used by default everywhere except US)
    u                       # USCS (used by default in US)
    M                       # show wind speed in m/s

View options:

    0                       # only current weather
    1                       # current weather + today's forecast
    2                       # current weather + today's + tomorrow's forecast
    A                       # ignore User-Agent and force ANSI output format (terminal)
    d                       # restrict output to standard console font glyphs
    F                       # do not show the "Follow" line
    n                       # narrow version (only day and night)
    q                       # quiet version (no "Weather report" text)
    Q                       # superquiet version (no "Weather report", no city name)
    T                       # switch terminal sequences off (no colors)

PNG options:

    /paris.png              # generate a PNG file
    p                       # add frame around the output
    t                       # transparency 150
    transparency=...        # transparency from 0 to 255 (255 = not transparent)
    background=...          # background color in form RRGGBB, e.g. 00aaaa

Options can be combined:

    /Paris?0pq
    /Paris?0pq&lang=fr
    /Paris_0pq.png          # in PNG the file mode are specified after _
    /Rome_0pq_lang=it.png   # long options are separated with underscore

Localization:

    $ curl fr.wttr.in/Paris
    $ curl wttr.in/paris?lang=fr
    $ curl -H "Accept-Language: fr" wttr.in/paris

Supported languages:

    am ar af be bn ca da de el et fr fa gl hi hu ia id it lt mg nb nl oc pl pt-br ro ru ta tr th uk vi zh-cn zh-tw (supported)
    az bg bs cy cs eo es eu fi ga hi hr hy is ja jv ka kk ko ky lv mk ml mr nl fy nn pt pt-br sk sl sr sr-lat sv sw te uz zh zu he (in progress)

Special URLs:

    /:help                  # show this page
    /:bash.function         # show recommended bash function wttr()
    /:translation           # show the information about the translators

In this post, I’ll go by the steps I took. The result should be available on GitHub.

First draft
#

main.go
#

Let’s keep things simple and output to the user what we actually know at the moment, absolutely nothing!

package main

import (
  "fmt"
)

func main() {
  fmt.Println("I have no idea")
}

main_test.go
#

Of course, we have to test that I have no idea is returned:

package main

import (
  "bytes"
  "github.com/stretchr/testify/assert"
  "io"
  "os"
  "testing"
)

func TestCmdOutput(t *testing.T) {
  oldStdout := os.Stdout

  r, w, _ := os.Pipe()
  os.Stdout = w

  main()

  _ = w.Close()

  os.Stdout = oldStdout

  var buf bytes.Buffer
  _, _ = io.Copy(&buf, r)
  got := buf.String()

  want := "I have no idea\n"

  assert.Equal(t, want, got)
}

First, we use the os package to get stout data from the command. When we’re done we set os.Stdout to the old one to prevent weirdness.

oldStdout := os.Stdout

r, w, _ := os.Pipe()
os.Stdout = w

main()

_ = w.Close()

os.Stdout = oldStdout

Afterward, we convert the output to a string we can do a comparison with

var buf bytes.Buffer
_, _ = io.Copy(&buf, r)
got := buf.String()

For unit tests, I like to use testify as I personally don’t like rewriting the same assertion functions over and over again. I understand that there is some contention about this in the Go community, personally I choose not to care.

want := "I have no idea\n"

assert.Equal(t, want, got)

Source code
#

This version of the code can be found here.

alrayyes/gwttr

Proof of concept go wttr client

Go
0
0
Building a Wttr client in Go - This article is part of a series.
Part 1: This Article