Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor common code for http requests and enable logging #25

Merged
merged 1 commit into from
Apr 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions api/http.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package api

import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)

type HTTPHelper struct {
Log *Logger
Client *http.Client
}

// NewHTTPHelper creates http helper for simplified PUT GET logic
func NewHTTPHelper(log *Logger) *HTTPHelper {
r := &HTTPHelper{
Log: log,
Client: &http.Client{},
}
return r
}

// Response codes other than HTTP 200 or 204 are raised as error
func (r *HTTPHelper) readBody(resp *http.Response, err error) ([]byte, error) {
if err != nil {
return []byte{}, err
}
defer resp.Body.Close()

b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return []byte{}, err
}

if r.Log != nil {
r.Log.TRACE.Printf("%s\n%s", resp.Request.URL.String(), string(b))
}

if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent {
return b, fmt.Errorf("unexpected response %d: %s", resp.StatusCode, string(b))
}

return b, nil
}

func (r *HTTPHelper) decodeJSON(resp *http.Response, err error, res interface{}) ([]byte, error) {
b, err := r.readBody(resp, err)
if err == nil {
err = json.Unmarshal(b, &res)
}

return b, err
}

// Get executes HTTP GET request returns the response body
func (r *HTTPHelper) Get(url string) ([]byte, error) {
resp, err := r.Client.Get(url)
return r.readBody(resp, err)
}

// Put executes HTTP PUT request returns the response body
func (r *HTTPHelper) Put(url string, body []byte) ([]byte, error) {
req, err := http.NewRequest(http.MethodPut, url, bytes.NewBuffer(body))
if err != nil {
return []byte{}, err
}

resp, err := r.Client.Do(req)
return r.readBody(resp, err)
}

// RequestJSON executes HTTP request and decodes JSON response
func (r *HTTPHelper) RequestJSON(req *http.Request, res interface{}) ([]byte, error) {
resp, err := r.Client.Do(req)
return r.decodeJSON(resp, err, res)
}

// GetJSON executes HTTP GET request and decodes JSON response
func (r *HTTPHelper) GetJSON(url string, res interface{}) ([]byte, error) {
resp, err := r.Client.Get(url)
return r.decodeJSON(resp, err, res)
}

// PutJSON executes HTTP PUT request and returns the response body
func (r *HTTPHelper) PutJSON(url string, data interface{}) ([]byte, error) {
body, err := json.Marshal(data)
if err != nil {
return []byte{}, err
}

return r.Put(url, body)
}
47 changes: 0 additions & 47 deletions charger/config.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
package charger

import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"

"github.com/andig/evcc/api"
Expand Down Expand Up @@ -36,45 +31,3 @@ func NewFromConfig(log *api.Logger, typ string, other map[string]interface{}) ap

return c
}

func getJSON(url string, result interface{}) (*http.Response, []byte, error) {
resp, err := http.Get(url)
if err != nil {
return resp, []byte{}, err
}
defer resp.Body.Close()

body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return resp, body, err
}

if resp.StatusCode == http.StatusOK {
err = json.Unmarshal(body, &result)
return resp, body, err
}

return resp, body, fmt.Errorf("unexpected status %d", resp.StatusCode)
}

func putJSON(url string, request interface{}) (*http.Response, []byte, error) {
data, err := json.Marshal(request)
if err != nil {
return nil, []byte{}, err
}

client := &http.Client{}
req, err := http.NewRequest(http.MethodPut, url, bytes.NewBuffer(data))
if err != nil {
return nil, []byte{}, err
}

resp, err := client.Do(req)
if err != nil {
return resp, []byte{}, err
}
defer resp.Body.Close()

body, err := ioutil.ReadAll(resp.Body)
return resp, body, err
}
54 changes: 29 additions & 25 deletions charger/go-e.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ type goeStatusResponse struct {

// GoE charger implementation
type GoE struct {
log *api.Logger
*api.HTTPHelper
URI string
}

Expand All @@ -41,11 +41,11 @@ func NewGoEFromConfig(log *api.Logger, other map[string]interface{}) api.Charger
// NewGoE creates GoE charger
func NewGoE(URI string) *GoE {
c := &GoE{
URI: URI,
log: api.NewLogger("go-e"),
HTTPHelper: api.NewHTTPHelper(api.NewLogger("go-e")),
URI: URI,
}

c.log.WARN.Println("-- experimental --")
c.HTTPHelper.Log.WARN.Println("-- experimental --")

return c
}
Expand All @@ -55,51 +55,55 @@ func (c *GoE) apiURL(api apiFunction) string {
}

func (c *GoE) getJSON(url string, result interface{}) error {
resp, body, err := getJSON(url, result)
c.log.TRACE.Printf("GET %s: %s", url, string(body))

if err != nil && len(body) == 0 {
return err
b, err := c.GetJSON(url, result)
if err != nil && len(b) > 0 {
var error goeStatusResponse
if err := json.Unmarshal(b, &error); err != nil {
return err
}

return fmt.Errorf("response code: %d", error.Err)
}

var error goeStatusResponse
_ = json.Unmarshal(body, &error)

return fmt.Errorf("api %d: %d", resp.StatusCode, error.Err)
return err
}

// Status implements the Charger.Status interface
func (c *GoE) Status() (api.ChargeStatus, error) {
var status goeStatusResponse
err := c.getJSON(c.apiURL(goeStatus), status)
if err := c.getJSON(c.apiURL(goeStatus), status); err != nil {
return api.StatusNone, err
}

switch status.Car {
case 1:
return api.StatusA, err
return api.StatusA, nil
case 2:
return api.StatusC, err
return api.StatusC, nil
case 3:
return api.StatusB, err
return api.StatusB, nil
case 4:
return api.StatusB, err
return api.StatusB, nil
default:
return api.StatusNone, fmt.Errorf("unknown result %d", status.Car)
}

return api.StatusNone, fmt.Errorf("unknown result %d", status.Car)
}

// Enabled implements the Charger.Enabled interface
func (c *GoE) Enabled() (bool, error) {
var status goeStatusResponse
err := c.getJSON(c.apiURL(goeStatus), status)
if err := c.getJSON(c.apiURL(goeStatus), status); err != nil {
return false, err
}

switch status.Alw {
case 0:
return false, err
return false, nil
case 1:
return true, err
return true, nil
default:
return false, fmt.Errorf("unknown result %d", status.Alw)
}

return false, fmt.Errorf("unknown result %d", status.Alw)
}

// Enable implements the Charger.Enable interface
Expand Down
43 changes: 19 additions & 24 deletions charger/nrgkick.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package charger
import (
"encoding/json"
"fmt"
"net/http"

"github.com/andig/evcc/api"
)
Expand All @@ -24,7 +23,7 @@ type NRGMeasurements struct {
ChargingPower float64
}

// NRGSettings is the /api/setings request/response
// NRGSettings is the /api/settings request/response
type NRGSettings struct {
Info NRGInfo `json:"omitempty"`
Values NRGValues
Expand Down Expand Up @@ -59,7 +58,7 @@ type NRGDeviceMetadata struct {

// NRGKick charger implementation
type NRGKick struct {
log *api.Logger
*api.HTTPHelper
IP string
MacAddress string
Password string
Expand All @@ -76,13 +75,13 @@ func NewNRGKickFromConfig(log *api.Logger, other map[string]interface{}) api.Cha
// NewNRGKick creates NRGKick charger
func NewNRGKick(IP, MacAddress, Password string) *NRGKick {
nrg := &NRGKick{
HTTPHelper: api.NewHTTPHelper(api.NewLogger("kick")),
IP: IP,
MacAddress: MacAddress,
Password: Password,
log: api.NewLogger("kick"),
}

nrg.log.WARN.Println("-- experimental --")
nrg.HTTPHelper.Log.WARN.Println("-- experimental --")

return nrg
}
Expand All @@ -92,35 +91,31 @@ func (nrg *NRGKick) apiURL(api apiFunction) string {
}

func (nrg *NRGKick) getJSON(url string, result interface{}) error {
resp, body, err := getJSON(url, result)
nrg.log.TRACE.Printf("GET %s: %s", url, string(body))

if err != nil && len(body) == 0 {
return err
b, err := nrg.GetJSON(url, result)
if err != nil && len(b) > 0 {
var error NRGResponse
if err := json.Unmarshal(b, &error); err != nil {
return err
}

return fmt.Errorf("response: %s", error.Message)
}

var error NRGResponse
_ = json.Unmarshal(body, &error)

return fmt.Errorf("api %d: %s", resp.StatusCode, error.Message)
return err
}

func (nrg *NRGKick) putJSON(url string, request interface{}) error {
resp, body, err := putJSON(url, request)
nrg.log.TRACE.Printf("PUT %v: %s", resp, string(body))

if err != nil && len(body) == 0 {
b, err := nrg.PutJSON(url, request)
if err != nil && len(b) == 0 {
return err
}

if resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusNoContent {
return nil
}

var error NRGResponse
_ = json.Unmarshal(body, &error)
if err := json.Unmarshal(b, &error); err != nil {
return err
}

return fmt.Errorf("api %d: %s", resp.StatusCode, error.Message)
return fmt.Errorf("response: %s", error.Message)
}

// Status implements the Charger.Status interface
Expand Down
Loading