Weather cli app



  • So I've been trying to get better at development and have a deeper understanding of Go. I wrote a small utility to interface with openweathermap.org's API. I had an initial setup that didn't utilize interfaces or methods but I went back and refactored it so that I could add another weather service later if I wanted to. Interfaces allow you to call methods over multiple types. The app just takes two arguments, the city and country where you want the weather.

    I'll preface the code with the fact that this is most likely horrible and I'm sure a lot is done incorrectly, so feel free to tear this apart if you actually know what you're doing.

    main.go

    package main
    
    import (
            "os"
    )
    
    type Location struct {
            City    string
            Country string
    }
    
    type Getter interface {
            GetWeather()
    }
    
    func Weather(g Getter) {
            g.GetWeather()
    }
    
    func main() {
            city := os.Args[1]
            country := os.Args[2]
    
            place := Location{}
            place.City = city
            place.Country = country
    
            Weather(place)
    
    }
    

    openweathermap.go

    package main
    
    import (
            "encoding/json"
            "fmt"
            "log"
            "net/http"
            "os"
    
            ini "gopkg.in/ini.v1"
    )
    
    type OpenWeather struct {
            Coord struct {
                    Lon float64 `json:"lon"`
                    Lat float64 `json:"lat"`
            } `json:"coord"`
            Weather []struct {
                    ID          int    `json:"id"`
                    Main        string `json:"main"`
                    Description string `json:"description"`
                    Icon        string `json:"icon"`
            } `json:"weather"`
            Base string `json:"base"`
            Main struct {
                    Temp     float64 `json:"temp"`
                    Pressure int     `json:"pressure"`
                    Humidity int     `json:"humidity"`
                    TempMin  float64 `json:"temp_min"`
                    TempMax  float64 `json:"temp_max"`
            } `json:"main"`
            Visibility int `json:"visibility"`
            Wind       struct {
                    Speed float64 `json:"speed"`
                    Deg   int     `json:"deg"`
                    Gust  float64 `json:"gust"`
            } `json:"wind"`
            Rain struct {
                    OneH float64 `json:"1h"`
            } `json:"rain"`
            Snow struct {
                    OneH float64 `json:"1h"`
            } `json:"snow"`
            Clouds struct {
                    All int `json:"all"`
            } `json:"clouds"`
            Dt  int `json:"dt"`
            Sys struct {
                    Type    int     `json:"type"`
                    ID      int     `json:"id"`
                    Message float64 `json:"message"`
                    Country string  `json:"country"`
                    Sunrise int     `json:"sunrise"`
                    Sunset  int     `json:"sunset"`
            } `json:"sys"`
            ID   int    `json:"id"`
            Name string `json:"name"`
            Cod  int    `json:"cod"`
    }
    
    func (l Location) GetWeather() {
            config, err := ini.Load(".weather")
            if err != nil {
                    fmt.Printf("Failed to load config file %v", err)
                    os.Exit(1)
            }
    
            apiKey := config.Section("").Key("openweather").String()
    
            url := fmt.Sprintf("https://api.openweathermap.org/data/2.5/weather?q=%s,%s&units=imperial&appid=%s", l.City, l.Country, apiKey)
    
            req, err := http.NewRequest("GET", url, nil)
            if err != nil {
                    log.Fatal("NewRequest: ", err)
            }
    
            client := http.Client{}
    
            resp, err := client.Do(req)
            if err != nil {
                    log.Fatal("Do: ", err)
            }
    
            defer resp.Body.Close()
    
            var weather OpenWeather
    
            if err := json.NewDecoder(resp.Body).Decode(&weather); err != nil {
                    log.Println(err)
            }
    
            fmt.Println("Temperature: ", weather.Main.Temp)
            fmt.Println("Description: ", weather.Weather[0].Description)
            fmt.Println("Minimum Temperature: ", weather.Main.TempMin)
            fmt.Println("Maximum Temperature: ", weather.Main.TempMax)
            fmt.Println("Wind Speed: ", weather.Wind.Speed, " MPH")
    }
    

    It's looking for an ini file named .weather in the same directory as the application. The content should be

    openweather = <your_api_key>
    

    I only have a couple parts of the json returned, mostly because I'm lazy. I also have the imperial units hard coded because again, I'm lazy.

    weatherapp.png



  • To build this, you can just create those two files and do a go build -o weather main.go openweathermap.go and it will generate your executable.

    To build it for a different platform (Windows vs Linux) pass the build environment variable in like this:

    GOOS=windows go build -o weather.exe main.go openweathermap.go

Log in to reply