Compare commits

...

7 commits

5 changed files with 259 additions and 181 deletions

View file

@ -7,6 +7,24 @@ Which is based on [Keep A Changelog](http://keepachangelog.com/)
## Unreleased
## v1.0.2 - 2019-08-25
### Fixed
- remove memory leak
## v1.0.1 - 2019-08-22
### Added
- goreport notation
### Fixed
- too many open files close with influxdb connection
- add license in code file
- golint check passed
## v1.0.0 - 2019-08-14
### Added

View file

@ -1,6 +1,7 @@
# Weather
[![Version](https://img.shields.io/badge/latest_version-1.0.0-green.svg)](https://git.yaegashi.fr/nishiki/weather/releases)
[![Version](https://img.shields.io/badge/latest_version-1.0.2-green.svg)](https://git.yaegashi.fr/nishiki/weather/releases)
[![GoReport](https://goreportcard.com/badge/git.yaegashi.fr/nishiki/weather)](https://goreportcard.com/report/git.yaegashi.fr/nishiki/weather)
[![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](https://git.yaegashi.fr/nishiki/weather/src/branch/master/LICENSE)
weather is a small program that fetch weather informations, and store them to influxdb

118
config.go
View file

@ -1,74 +1,94 @@
/*
Copyright 2019 Adrien Waksberg
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"fmt"
"io/ioutil"
"fmt"
"io/ioutil"
yaml "gopkg.in/yaml.v2"
yaml "gopkg.in/yaml.v2"
)
// Config struct contains all options
type Config struct {
InfluxDB struct {
URL string `yaml:"url"`
Database string `yaml:"database"`
Measurement string `yaml:"measurement"`
Username string `yaml:"username"`
Password string `yaml:"password"`
} `yaml:"influxdb"`
OpenWeatherMap struct {
ApiKey string `yaml:"api_key"`
Units string `yaml:"units"`
} `yaml:"openweathermap"`
Cities []string `yaml:"cities"`
Interval int64 `yaml:"interval"`
InfluxDB struct {
URL string `yaml:"url"`
Database string `yaml:"database"`
Measurement string `yaml:"measurement"`
Username string `yaml:"username"`
Password string `yaml:"password"`
} `yaml:"influxdb"`
OpenWeatherMap struct {
APIKey string `yaml:"api_key"`
Units string `yaml:"units"`
} `yaml:"openweathermap"`
Cities []string `yaml:"cities"`
Interval int64 `yaml:"interval"`
}
// Load the config from a file
func (c *Config) Load(path string) error {
data, err := ioutil.ReadFile(path)
if err != nil {
return err
}
data, err := ioutil.ReadFile(path)
if err != nil {
return err
}
err = yaml.Unmarshal(data, &c)
if err != nil {
return err
}
err = yaml.Unmarshal(data, &c)
if err != nil {
return err
}
c.Defaults()
err = c.Check()
if err != nil {
return err
}
c.Defaults()
err = c.Check()
if err != nil {
return err
}
return nil
return nil
}
// Defaults set options with default value
func (c *Config) Defaults() {
if c.OpenWeatherMap.Units == "" {
c.OpenWeatherMap.Units = "metric"
}
if c.OpenWeatherMap.Units == "" {
c.OpenWeatherMap.Units = "metric"
}
if c.Interval == 0 {
c.Interval = 600
}
if c.Interval == 0 {
c.Interval = 600
}
if c.InfluxDB.URL == "" {
c.InfluxDB.URL = "http://127.0.0.1:8086"
}
if c.InfluxDB.URL == "" {
c.InfluxDB.URL = "http://127.0.0.1:8086"
}
if c.InfluxDB.Measurement == "" {
c.InfluxDB.Measurement = "weather"
}
if c.InfluxDB.Measurement == "" {
c.InfluxDB.Measurement = "weather"
}
}
// Check if the options are good
func (c *Config) Check() error {
if c.InfluxDB.Database == "" {
return fmt.Errorf("you must specify influxdb.database in config file")
}
if c.InfluxDB.Database == "" {
return fmt.Errorf("you must specify influxdb.database in config file")
}
if c.OpenWeatherMap.ApiKey == "" {
return fmt.Errorf("you must specify api_key in config file")
}
if c.OpenWeatherMap.APIKey == "" {
return fmt.Errorf("you must specify api_key in config file")
}
return nil
return nil
}

101
main.go
View file

@ -1,62 +1,79 @@
/*
Copyright 2019 Adrien Waksberg
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"fmt"
"flag"
"os"
"time"
"flag"
"fmt"
"os"
"time"
)
// Options and OpenWeatherMap URI
var (
URI = "https://api.openweathermap.org/data/2.5"
CONFIG = flag.String("config", "", "config file path")
DAEMON = flag.Bool("daemon", false, "run in daemon mode")
HELP = flag.Bool("help", false, "print this help message")
URI = "https://api.openweathermap.org/data/2.5"
CONFIG = flag.String("config", "", "config file path")
DAEMON = flag.Bool("daemon", false, "run in daemon mode")
HELP = flag.Bool("help", false, "print this help message")
)
func init() {
flag.Parse()
flag.Parse()
}
func main() {
var w Weather
var w Weather
if *HELP {
flag.PrintDefaults()
os.Exit(1)
}
if *HELP {
flag.PrintDefaults()
os.Exit(1)
}
if *CONFIG == "" {
fmt.Println("you must specify a config file with -config option")
os.Exit(1)
}
if *CONFIG == "" {
fmt.Println("you must specify a config file with -config option")
os.Exit(1)
}
err := w.Config.Load(*CONFIG)
if err != nil {
fmt.Printf("%s\n", err)
os.Exit(2)
}
err := w.Config.Load(*CONFIG)
if err != nil {
fmt.Printf("%s\n", err)
os.Exit(2)
}
for {
startTime := time.Now().Unix()
for {
startTime := time.Now().Unix()
w.FetchData()
err = w.SendToInfluxDB()
if err != nil {
fmt.Printf("%s\n", err)
}
w.FetchData()
err = w.SendToInfluxDB()
if err != nil {
fmt.Printf("%s\n", err)
}
if *DAEMON == false {
if err == nil {
os.Exit(0)
} else {
os.Exit(2)
}
}
if *DAEMON == false {
if err == nil {
os.Exit(0)
} else {
os.Exit(2)
}
}
sleepTime := time.Now().Unix() - startTime + w.Config.Interval
if sleepTime > 0 {
time.Sleep(time.Duration(sleepTime) * time.Second)
}
}
sleepTime := time.Now().Unix() - startTime + w.Config.Interval
if sleepTime > 0 {
time.Sleep(time.Duration(sleepTime) * time.Second)
}
}
}

View file

@ -1,113 +1,135 @@
/*
Copyright 2019 Adrien Waksberg
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"fmt"
"net/http"
"encoding/json"
"io/ioutil"
"time"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"time"
influx "github.com/influxdata/influxdb1-client/v2"
influx "github.com/influxdata/influxdb1-client/v2"
)
// Weather is the main struct
type Weather struct {
Config Config
WeatherDatas []WeatherData
Config Config
WeatherDatas []WeatherData
}
// WeatherData contains weather data from openweathermap
type WeatherData struct {
City string `json:"name"`
Main struct {
Humidity int `json:"humidity"`
Pressure int `json:"pressure"`
Temperature float32 `json:"temp"`
} `json:"main"`
Clouds struct {
All int `json:"all"`
} `json:"clouds"`
Rain struct {
OneHour float32 `json:"1h"`
TreeHours float32 `json:"3h"`
} `json:"rain"`
Snow struct {
OneHour float32 `json:"1h"`
TreeHours float32 `json:"3h"`
} `json:"snow"`
Wind struct {
Speed float32 `json:"speed"`
Direction int `json:"deg"`
} `json:"Wind"`
Sys struct {
Country string `json:"country"`
} `json:"sys"`
City string `json:"name"`
Main struct {
Humidity int `json:"humidity"`
Pressure int `json:"pressure"`
Temperature float32 `json:"temp"`
} `json:"main"`
Clouds struct {
All int `json:"all"`
} `json:"clouds"`
Rain struct {
OneHour float32 `json:"1h"`
TreeHours float32 `json:"3h"`
} `json:"rain"`
Snow struct {
OneHour float32 `json:"1h"`
TreeHours float32 `json:"3h"`
} `json:"snow"`
Wind struct {
Speed float32 `json:"speed"`
Direction int `json:"deg"`
} `json:"Wind"`
Sys struct {
Country string `json:"country"`
} `json:"sys"`
}
// FetchData from OpenWeatherMap API
func (w *Weather) FetchData() {
for _, city := range w.Config.Cities {
resp, err := http.Get(
fmt.Sprintf(
"%s/weather?q=%s&appid=%s&units=%s",
URI,
city,
w.Config.OpenWeatherMap.ApiKey,
w.Config.OpenWeatherMap.Units,
),
)
if err != nil {
fmt.Printf("%s", err)
}
defer resp.Body.Close()
w.WeatherDatas = []WeatherData{}
for _, city := range w.Config.Cities {
resp, err := http.Get(
fmt.Sprintf(
"%s/weather?q=%s&appid=%s&units=%s",
URI,
city,
w.Config.OpenWeatherMap.APIKey,
w.Config.OpenWeatherMap.Units,
),
)
if err != nil {
fmt.Printf("%s", err)
}
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Printf("%s", err)
}
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Printf("%s", err)
}
weatherData := WeatherData{}
json.Unmarshal(data, &weatherData)
w.WeatherDatas = append(w.WeatherDatas, weatherData)
weatherData := WeatherData{}
json.Unmarshal(data, &weatherData)
w.WeatherDatas = append(w.WeatherDatas, weatherData)
time.Sleep(500 * time.Millisecond)
}
time.Sleep(500 * time.Millisecond)
}
}
// SendToInfluxDB send data in influxdb
func (w *Weather) SendToInfluxDB() error {
conn, err := influx.NewHTTPClient(influx.HTTPConfig{
Addr: w.Config.InfluxDB.URL,
Username: w.Config.InfluxDB.Username,
Password: w.Config.InfluxDB.Password,
})
if err != nil {
return err
}
conn, err := influx.NewHTTPClient(influx.HTTPConfig{
Addr: w.Config.InfluxDB.URL,
Username: w.Config.InfluxDB.Username,
Password: w.Config.InfluxDB.Password,
})
if err != nil {
return err
}
defer conn.Close()
bps, _ := influx.NewBatchPoints(
influx.BatchPointsConfig{
Database: w.Config.InfluxDB.Database,
})
bps, _ := influx.NewBatchPoints(
influx.BatchPointsConfig{
Database: w.Config.InfluxDB.Database,
})
for _, weather := range w.WeatherDatas {
tags := map[string]string{ "city": weather.City, "country": weather.Sys.Country }
fields := map[string]interface{}{
"temperature": weather.Main.Temperature,
"humidity": weather.Main.Humidity,
"pressure": weather.Main.Pressure,
"clouds": weather.Clouds.All,
"wind_speed": weather.Wind.Speed,
"wind_direction": weather.Wind.Direction,
"rain_1h": weather.Rain.OneHour,
"rain_3h": weather.Rain.TreeHours,
"snow_1h": weather.Snow.OneHour,
"snow_3h": weather.Snow.TreeHours,
}
point, _ := influx.NewPoint(w.Config.InfluxDB.Measurement, tags, fields, time.Now())
bps.AddPoint(point)
}
for _, weather := range w.WeatherDatas {
tags := map[string]string{"city": weather.City, "country": weather.Sys.Country}
fields := map[string]interface{}{
"temperature": weather.Main.Temperature,
"humidity": weather.Main.Humidity,
"pressure": weather.Main.Pressure,
"clouds": weather.Clouds.All,
"wind_speed": weather.Wind.Speed,
"wind_direction": weather.Wind.Direction,
"rain_1h": weather.Rain.OneHour,
"rain_3h": weather.Rain.TreeHours,
"snow_1h": weather.Snow.OneHour,
"snow_3h": weather.Snow.TreeHours,
}
point, _ := influx.NewPoint(w.Config.InfluxDB.Measurement, tags, fields, time.Now())
bps.AddPoint(point)
}
err = conn.Write(bps)
if err != nil {
return err
}
err = conn.Write(bps)
if err != nil {
return err
}
return nil
return nil
}