package isoclient import ( "encoding/json" "errors" "fmt" "io/ioutil" "log" "net/http" "os" "strings" "time" "github.com/amigan/energyd/internal/config" ) type Client interface { GetCurrentLoad() (*FiveMinSystemLoad, error) GetHourlyLoadForecast() (*HourlyLoadForecasts, error) GetGenFuelMixes() (GenFuelMixes, error) } type client struct { user string passwd string Debug bool } func New(cfg *config.Conf) Client { c := &client{ user: cfg.ISOUser, passwd: cfg.ISOPassword, Debug: os.Getenv("DEBUG") == "true", } return c } const BASEURL = "https://webservices.iso-ne.com/api/v1.1" const GETCURRENTLOADEP = "/fiveminutesystemload/current" func (c *client) makeRequest(endpoint string, into interface{}) error { req, err := http.NewRequest(http.MethodGet, BASEURL+endpoint, nil) if err != nil { return err } req.SetBasicAuth(c.user, c.passwd) req.Header.Add("Accept", "application/json") req.Header.Add("Content-Type", "application/json") client := http.Client{} resp, err := client.Do(req) if resp.StatusCode != http.StatusOK { return errors.New("non 2xx response") } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { return err } if c.Debug { log.Println(string(body)) } return json.Unmarshal(body, into) } type FiveMinSystemLoad struct { BeginDate time.Time `json:"BeginDate"` LoadMW float32 `json:"LoadMw"` NativeLoad float32 `json:"NativeLoad"` ArdDemand float32 `json:"ArdDemand"` SystemLoadBtmPv float32 `json:"SystemLoadBtmPv"` NativeLoadBtmPv float32 `json:"NativeLoadBtmPv"` } func (c *client) GetCurrentLoad() (*FiveMinSystemLoad, error) { fms := struct { FiveMinSystemLoad []*FiveMinSystemLoad }{} err := c.makeRequest(GETCURRENTLOADEP, &fms) if err != nil { return nil, err } if len(fms.FiveMinSystemLoad) < 1 { return nil, errors.New("less than one element") } return fms.FiveMinSystemLoad[0], nil } const GETHOURLYFORECASTEP = "/hourlyloadforecast/day/" type HourlyLoadForecast struct { BeginDate time.Time `json:"BeginDate"` CreationDate time.Time `json:"CreationDate"` LoadMW float32 `json:"LoadMw"` LoadAdjustmentMW *float32 `json:"LoadAdjustmentMw"` NetLoadMW float32 `json:"NetLoadMw"` } type HourlyLoadForecasts struct { HourlyLoadForecast [23]HourlyLoadForecast `json:"HourlyLoadForecast"` } func (c *client) GetHourlyLoadForecast() (*HourlyLoadForecasts, error) { hlfc := struct { HourlyLoadForecasts HourlyLoadForecasts }{} year, month, day := time.Now().Date() endpoint := fmt.Sprintf("%s%04d%02d%02d", GETHOURLYFORECASTEP, year, month, day) err := c.makeRequest(endpoint, &hlfc) if err != nil { return nil, err } return &hlfc.HourlyLoadForecasts, nil } type GenFuelMix struct { BeginDate time.Time `json:"BeginDate"` GenMW float32 `json:"GenMW"` FuelCategoryRollup string `json:"FuelCategoryRollup"` FuelCategory string `json:"FuelCategory"` Marginal YNFlag `json:"MarginalFlag"` } type YNFlag bool func (ynf *YNFlag) UnmarshalJSON(b []byte) error { var s string err := json.Unmarshal(b, &s) if err != nil { return err } switch strings.ToLower(s) { case "y": *ynf = true case "n": *ynf = false } return nil } type GenFuelMixes []GenFuelMix const GETGENFUELMIXES = "/genfuelmix/current" func (c *client) GetGenFuelMixes() (GenFuelMixes, error) { var fm struct { GenFuelMixes struct { GenFuelMix GenFuelMixes `json:"GenFuelMix"` } `json:"GenFuelMixes"` } err := c.makeRequest(GETGENFUELMIXES, &fm) if err != nil { return nil, err } return fm.GenFuelMixes.GenFuelMix, nil }