diff --git a/.DS_Store b/.DS_Store
index fc263ad..a883a69 100644
Binary files a/.DS_Store and b/.DS_Store differ
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index f091842..fca6055 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -16,7 +16,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v4
with:
- go-version: "1.21"
+ go-version: "1.22"
- name: Verify dependencies
run: go mod verify
diff --git a/.gitignore b/.gitignore
index 0b59c55..e9d44f5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,7 @@
*.dll
*.so
*.dylib
+*.DS_Store
# Test binary, built with `go test -c`
*.test
@@ -28,3 +29,5 @@ bin/
output*.*
out*.*
out.txt
+
+.idea/
diff --git a/.goreleaser.yaml b/.goreleaser.yaml
index a65edfe..ce9a1a1 100644
--- a/.goreleaser.yaml
+++ b/.goreleaser.yaml
@@ -12,7 +12,8 @@ builds:
goos:
- linux
- windows
- - darwin
+ - darwin
+ main: ./cmd/gogoodwe
archives:
- format: tar.gz
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c3d8e47..a47cd84 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,22 @@
-# CHANGELOG
+# GoGoodwe - CHANGELOG
+
+## v2.0.0 (2024-01-22)
+
+- Major refactoring to cleanup project structure.
+- Simplified package structure.
+- refactored structs to use pointers more efficiently.
+
+## v1.4.0 (2023-08-30)
+
+- Major refactoring to move non-shared code to /internal folder
+- Abstracted core away from main()
+
+## v1.1.0 (2023-08-16)
+
+- refactored code to make errors bubble back to main package and better error reporting/logging
+- refactored main package to include run() method
+- removed staticcheck as there is a bug with Go v1.21
## v1.0.0 (2023-08-10)
+
- initial version 1.0 release
diff --git a/LICENSE b/LICENSE
index 8480972..06c0e27 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2023, Aaron Saikovski
+Copyright (c) 2024, Aaron Saikovski
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/Makefile b/Makefile
index d82dc9a..5e177ad 100644
--- a/Makefile
+++ b/Makefile
@@ -1,68 +1,92 @@
# Define Go command and flags
GO = go
GOFLAGS = -ldflags="-s -w"
-
-#export PATH=$PATH:$HOME/go/bin;
-
-# Define the target executable
TARGET = gogoodwe
+MAINAPPPATH = ./cmd/${TARGET}/main.go
+default: help
+
+.PHONY: help
## help - Display help about make targets for this Makefile
help:
@cat Makefile | grep '^## ' --color=never | cut -c4- | sed -e "`printf 's/ - /\t- /;'`" | column -s "`printf '\t'`" -t
-## localrelease - Builds the project in preparation for (local)release
-localrelease:
- go build $(GOFLAGS) -o bin/${TARGET} main.go
+
+.PHONY: release
+## release - Builds the project in preparation for (local)release
+release: vet lint seccheck
+ go build $(GOFLAGS) -o bin/${TARGET} ${MAINAPPPATH}
file bin/${TARGET}
-## release - Builds the project in preparation for release
-release:
- goreleaser release --snapshot --clean
-
-## debug - Builds the project in preparation for debug
+
+.PHONY: goreleaser
+## goreleaser - Builds the project in preparation for release
+goreleaser:
+ goreleaser release --snapshot --clean
+
+
+.PHONY: docs
+## docs - updates the swagger docs
+docs:
+ swag init
+
+
+.PHONY: build
+## build - Builds the project in preparation for debug
build:
- go build -o bin/${TARGET} main.go
+ go build -o bin/${TARGET} ${MAINAPPPATH}
file bin/${TARGET}
-## buildandrun - builds and runs the program on the target platform
-buildandrun: build
- ./bin/${TARGET}
-## run - runs main.go for testing
-run: dep
- go run main.go
+.PHONY: run
+## run - builds and runs the program on the target platform
+run:
+ go run ${MAINAPPPATH}
+.PHONY: clean
## clean - Remove the old builds and any debug information
clean:
+ go clean -cache
go clean
rm -rf dist
rm bin/${TARGET}
-## test - executes unit test
+
+.PHONY: test
+## test - executes unit tests
test:
- go test ./...
+ go test -v ./test/...
-## dep - fetches any external dependencies
-dep:
+
+.PHONY: deps
+## deps - fetches any external dependencies and updates
+deps:
go mod tidy
go mod download
+ go get -u ./...
+
+.PHONY: vet
## vet - Vet examines Go source code and reports suspicious constructs
vet:
go vet ./...
-## staticcheck - Runs static code analyzer staticcheck
-staticcheck:
- go run honnef.co/go/tools/cmd/staticcheck@latest -checks=all,-ST1000,-U1000 ./...
+.PHONY: staticcheck
+## staticcheck - Runs static code analyzer staticcheck - currently broken
+staticcheck:
+ staticcheck ./...
+
+
+.PHONY: seccheck
## seccheck - Code vulnerability check
seccheck:
- brew install govulncheck
- govulncheck ./...
+ #govulncheck ./...
+
+.PHONY: lint
## lint - format code and tidy modules
lint:
go fmt ./...
- go mod tidy -v
\ No newline at end of file
+ go mod tidy -v
diff --git a/README.md b/README.md
index 491243a..132e902 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,10 @@
+# GoGoodwe V2.0
+=======
-# GoGoodwe
-
-A command line tool and Go packages to query the GOODWE SEMS Portal APIs - written in 100% Go.
+A command line tool and query the GOODWE SEMS Inverter APIs - written in 100% Go.
[![Build Status](https://github.com/AaronSaikovski/gogoodwe/workflows/build/badge.svg)](https://github.com/AaronSaikovski/gogoodwe/actions)
-[![Coverage Status](https://coveralls.io/repos/github/AaronSaikovski/gogoodwe/badge.svg?branch=main)](https://coveralls.io/github/AaronSaikovski/gogoodwe?branch=main)
[![Licence](https://img.shields.io/github/license/AaronSaikovski/gogoodwe)](LICENSE)
@@ -15,17 +14,19 @@ A command line tool and Go packages to query the GOODWE SEMS Portal APIs - writt
The toolchain is mainly driven by the Makefile.
```bash
-* help - Display help about make targets for this Makefile
-* release - Builds the project in preparation for release
-* debug - Builds the project in preparation for debug
-* buildandrun - builds and runs the program on the target platform
-* run - runs main.go for testing
-* clean - Remove the old builds and any debug information
-* test - executes unit test
-* dep - fetches any external dependencies
-* vet - Vet examines Go source code and reports suspicious constructs
-* staticcheck - Runs static code analyzer staticcheck
-* lint - format code and tidy modules
+help - Display help about make targets for this Makefile
+release - Builds the project in preparation for (local)release
+goreleaser - Builds the project in preparation for release
+docs - updates the swagger docs
+build - Builds the project in preparation for debug
+run - builds and runs the program on the target platform
+clean - Remove the old builds and any debug information
+test - executes unit tests
+deps - fetches any external dependencies and updates
+vet - Vet examines Go source code and reports suspicious constructs
+staticcheck - Runs static code analyzer staticcheck - currently broken
+seccheck - Code vulnerability check
+lint - format code and tidy modules
```
To get started type,
@@ -46,10 +47,12 @@ From the command line the usage is pretty simple:
```bash
##Note the use of single quotes ''
-./gogoodwe --account '' --pwd '' --powerstationid ''
+./gogoodwe --account '' --pwd '' --powerstationid '' --dailysummary
# Or
-./gogoodwe -a '' -p '' -i ''
+./gogoodwe -a '' -p '' -i '' -d
+
+##where daily summary provides a shorter daily view of the inverter data
```
To get the help on using the command line tool, type:
diff --git a/TODO.md b/TODO.md
index 3143314..9ffce79 100644
--- a/TODO.md
+++ b/TODO.md
@@ -1,13 +1,23 @@
-# gogoodwe TODO
+# GoGoodwe V2 - TODO
-GoGoodwe backlog
+### ToDo
-### Todo
-
-- [ ] Add ability to output to file with a flag
-- [ ] Add ability to have a smaller output struct of just key reporting data
-- [ ] Add Golang contexts for API calls
+- [ ]
### In Progress
+- [ ] add unit tests
+
### Done ✓
+
+- [x] Add the ability to query the inverter status for Generation today and Status (check if operational).
+
+### Future/Roadmap
+
+- [ ] Format the inverter output to make it more human readable.
+- [ ] Add ability to output inverter data to a file.
+- [ ] Add the ability to query historical data for a single day.
+- [ ] Have the ability to have a realtime logging to the screen or to a file in 5 minute intervals.
+- [ ] Add the ability to produce a daily summary of key data (Generation today, Income today, total generation, total income).
+- [ ] Add goroutines and wait groups for the API calls and maybe channels for success/failed API calls.
+- [ ] Investigate the ability to generate .CSV files as output.
diff --git a/cmd/gogoodwe/app/app.go b/cmd/gogoodwe/app/app.go
new file mode 100644
index 0000000..0117dc6
--- /dev/null
+++ b/cmd/gogoodwe/app/app.go
@@ -0,0 +1,30 @@
+package app
+
+// Main package - This is the main program entry point
+import (
+ "github.com/AaronSaikovski/gogoodwe/cmd/gogoodwe/powerstation"
+ "github.com/AaronSaikovski/gogoodwe/cmd/gogoodwe/utils"
+ "github.com/alexflint/go-arg"
+)
+
+// Run - main program runner
+func Run() error {
+
+ //Get the args input data
+ var args utils.Args
+ p := arg.MustParse(&args)
+
+ //check for valid email address input
+ if !utils.CheckValidEmail(args.Account) {
+ p.Fail("Invalid Email address format - should be: 'user@somedomain.com'.")
+ }
+
+ //check for valid powerstation Id
+ if !utils.CheckValidPowerstationID(args.PowerStationID) {
+ p.Fail("Invalid Powerstation ID format: - should be: 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'.")
+ }
+
+ // Get the data from the API, return any errors. Pass in args as string
+ return powerstation.FetchData(args.Account, args.Password, args.PowerStationID, args.DailySummary)
+
+}
diff --git a/cmd/gogoodwe/interfaces/interfaces.go b/cmd/gogoodwe/interfaces/interfaces.go
new file mode 100644
index 0000000..7e705cb
--- /dev/null
+++ b/cmd/gogoodwe/interfaces/interfaces.go
@@ -0,0 +1,10 @@
+package interfaces
+
+import (
+ "github.com/AaronSaikovski/gogoodwe/cmd/gogoodwe/types"
+)
+
+// Constraints for functions that return data from the API via marshalled structs
+type ISemsDataConstraint interface {
+ types.InverterData | types.DailySummaryData
+}
diff --git a/cmd/gogoodwe/main.go b/cmd/gogoodwe/main.go
new file mode 100644
index 0000000..a418908
--- /dev/null
+++ b/cmd/gogoodwe/main.go
@@ -0,0 +1,19 @@
+/*
+Package main implements a program that authenticates to and queries the SEMS Solar inverter API.
+*/
+package main
+
+import (
+ "github.com/AaronSaikovski/gogoodwe/cmd/gogoodwe/app"
+ "log"
+)
+
+func main() {
+ if err := runApp(); err != nil {
+ log.Fatalf("error: %v", err)
+ }
+}
+
+func runApp() error {
+ return app.Run()
+}
diff --git a/cmd/gogoodwe/powerstation/constants.go b/cmd/gogoodwe/powerstation/constants.go
new file mode 100644
index 0000000..9976090
--- /dev/null
+++ b/cmd/gogoodwe/powerstation/constants.go
@@ -0,0 +1,9 @@
+package powerstation
+
+const (
+ // Powerstation API Url
+ PowerStationURL string = "v2/PowerStation/GetMonitorDetailByPowerstationId"
+
+ // Default timeout value
+ HTTPTimeout int = 20
+)
diff --git a/cmd/gogoodwe/powerstation/fetch.go b/cmd/gogoodwe/powerstation/fetch.go
new file mode 100644
index 0000000..4513b4f
--- /dev/null
+++ b/cmd/gogoodwe/powerstation/fetch.go
@@ -0,0 +1,39 @@
+/*
+# Name: data - fetches data from the goodwe API - and processes it to pass back to caller
+# Author: Aaron Saikovski - asaikovski@outlook.com
+*/
+package powerstation
+
+import (
+ "github.com/AaronSaikovski/gogoodwe/cmd/gogoodwe/semsapi"
+ "github.com/AaronSaikovski/gogoodwe/cmd/gogoodwe/types"
+ "github.com/AaronSaikovski/gogoodwe/cmd/gogoodwe/utils"
+)
+
+func FetchData(Account string, Password string, PowerStationID string, DailySummary bool) error {
+
+ // User account struct
+ creds := &types.LoginCredentials{
+ Account: Account,
+ Password: Password,
+ PowerStationID: PowerStationID,
+ }
+
+ // Do the login..check for errors
+ loginApiResponse, err := semsapi.Login(creds)
+ if err != nil {
+ utils.HandleError(err)
+ return err
+ }
+
+ //fetch data based on
+ if DailySummary {
+ getMonitorSummaryByPowerstationId(creds, loginApiResponse)
+
+ } else {
+ //powerstationData = types.InverterData
+ getMonitorDetailByPowerstationId(creds, loginApiResponse)
+ }
+
+ return nil
+}
diff --git a/cmd/gogoodwe/powerstation/monitordetails.go b/cmd/gogoodwe/powerstation/monitordetails.go
new file mode 100644
index 0000000..2cc99d9
--- /dev/null
+++ b/cmd/gogoodwe/powerstation/monitordetails.go
@@ -0,0 +1,109 @@
+package powerstation
+
+import (
+ "bytes"
+ "errors"
+ "net/http"
+ "time"
+
+ "github.com/AaronSaikovski/gogoodwe/cmd/gogoodwe/interfaces"
+ "github.com/AaronSaikovski/gogoodwe/cmd/gogoodwe/types"
+ "github.com/AaronSaikovski/gogoodwe/cmd/gogoodwe/utils"
+)
+
+// Generic function to retrieve data from the API via an ISemsDataConstraint Interface of defined structs
+func getMonitorData[T interfaces.ISemsDataConstraint](LoginCredentials *types.LoginCredentials, LoginApiResponse *types.LoginResponse, InverterOutput *T) error {
+
+ // get the Token header data
+ apiResponseJsonData, err := dataTokenJSON(LoginApiResponse)
+ if err != nil {
+ return err
+ }
+
+ // get the Powerstation ID header data
+ powerStationIdJsonData, err := powerStationIdJSON(LoginCredentials)
+ if err != nil {
+ return err
+ }
+
+ //Get the url from the Auth API and append the data url part
+ url := (LoginApiResponse.API + PowerStationURL)
+
+ // Create a new http request
+ req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(powerStationIdJsonData))
+ if err != nil {
+ return err
+ }
+
+ //Add headers pass in the pointer to set the headers on the request object
+ setHeaders(req, apiResponseJsonData)
+
+ //make the API Call
+ client := &http.Client{Timeout: time.Duration(HTTPTimeout) * time.Second}
+ resp, err := client.Do(req)
+ if err != nil {
+ return err
+ }
+
+ //cleanup
+ defer resp.Body.Close()
+
+ // Get the response body
+ respBody, err := utils.FetchResponseBody(resp.Body)
+ if err != nil {
+ return err
+ }
+
+ //marshall response to struct pointer
+ inverterDataerr := utils.UnmarshalDataToStruct(respBody, &InverterOutput)
+ if inverterDataerr != nil {
+ return inverterDataerr
+ }
+
+ return nil
+
+}
+
+// Get Monitor Detailed data
+func getMonitorDetailByPowerstationId(LoginCredentials *types.LoginCredentials, LoginApiResponse *types.LoginResponse) {
+ var powerstationData types.InverterData
+
+ err := getMonitorData(LoginCredentials, LoginApiResponse, &powerstationData)
+ if err != nil {
+ utils.HandleError(err)
+ }
+
+ dataOutput, err := getDataJSON(powerstationData)
+ if err != nil {
+ utils.HandleError(errors.New("error: converting powerstation data"))
+ }
+
+ output, err := parseOutput(dataOutput)
+ if err != nil {
+ utils.HandleError(err)
+ }
+ printOutput(output)
+
+}
+
+// Get Monitor sumary data
+func getMonitorSummaryByPowerstationId(LoginCredentials *types.LoginCredentials, LoginApiResponse *types.LoginResponse) {
+
+ var powerstationData types.DailySummaryData
+ err := getMonitorData(LoginCredentials, LoginApiResponse, &powerstationData)
+ if err != nil {
+ utils.HandleError(err)
+ }
+
+ dataOutput, err := getDataJSON(powerstationData)
+ if err != nil {
+ utils.HandleError(errors.New("error: converting powerstation summary data"))
+ }
+
+ output, err := parseOutput(dataOutput)
+ if err != nil {
+ utils.HandleError(err)
+ }
+ printOutput(output)
+
+}
diff --git a/cmd/gogoodwe/powerstation/utils.go b/cmd/gogoodwe/powerstation/utils.go
new file mode 100644
index 0000000..5aa7c76
--- /dev/null
+++ b/cmd/gogoodwe/powerstation/utils.go
@@ -0,0 +1,53 @@
+/*
+# Name: powerstationhelper - helper functions to get the Powerstation Data from the API
+# Author: Aaron Saikovski - asaikovski@outlook.com
+*/
+package powerstation
+
+import (
+ "encoding/json"
+ "net/http"
+ "strconv"
+
+ "github.com/AaronSaikovski/gogoodwe/cmd/gogoodwe/interfaces"
+ "github.com/AaronSaikovski/gogoodwe/cmd/gogoodwe/types"
+ "github.com/AaronSaikovski/gogoodwe/cmd/gogoodwe/utils"
+)
+
+// setHeaders - Set the headers for the SEMS Data API
+func setHeaders(r *http.Request, tokenstring []byte) {
+ r.Header.Add("Content-Type", "application/json")
+ r.Header.Add("Token", string(tokenstring))
+}
+
+// PowerStationIdJSON - Makes a map for the powerStationId to be passed to the Data API header and returns a JSON string
+func powerStationIdJSON(UserLogin *types.LoginCredentials) ([]byte, error) {
+ powerStationMap := make(map[string]string)
+ powerStationMap["powerStationId"] = UserLogin.PowerStationID
+
+ // convert to byte[]
+ jsonStr, err := json.Marshal(powerStationMap)
+ return jsonStr, err
+}
+
+func dataTokenJSON(SemsResponseData *types.LoginResponse) ([]byte, error) {
+ tokenMap := make(map[string]string)
+ tokenMap["version"] = "v2.1.0"
+ tokenMap["client"] = "ios"
+ tokenMap["language"] = "en"
+ tokenMap["timestamp"] = strconv.FormatInt(SemsResponseData.Data.Timestamp, 10)
+ tokenMap["uid"] = SemsResponseData.Data.UID
+ tokenMap["token"] = SemsResponseData.Data.Token
+
+ // convert to byte[]
+ jsonStr, err := json.Marshal(tokenMap)
+ return jsonStr, err
+}
+
+// parse json data
+func getDataJSON[T interfaces.ISemsDataConstraint](data T) ([]byte, error) {
+
+ // Get the response and return any errors
+ resp, err := utils.MarshalStructToJSON(&data)
+ return resp, err
+}
diff --git a/cmd/gogoodwe/semsapi/constants.go b/cmd/gogoodwe/semsapi/constants.go
new file mode 100644
index 0000000..4f618b5
--- /dev/null
+++ b/cmd/gogoodwe/semsapi/constants.go
@@ -0,0 +1,6 @@
+package semsapi
+
+const (
+ AuthLoginURL = "https://www.semsportal.com/api/v2/Common/CrossLogin"
+ HTTPTimeout = 20 // seconds
+)
diff --git a/cmd/gogoodwe/semsapi/login.go b/cmd/gogoodwe/semsapi/login.go
new file mode 100644
index 0000000..8d9fb9b
--- /dev/null
+++ b/cmd/gogoodwe/semsapi/login.go
@@ -0,0 +1,66 @@
+package semsapi
+
+import (
+ "bytes"
+ "net/http"
+ "time"
+
+ "github.com/AaronSaikovski/gogoodwe/cmd/gogoodwe/types"
+ "github.com/AaronSaikovski/gogoodwe/cmd/gogoodwe/utils"
+)
+
+// Login - Login to the SEMS API passing in a LoginCredentials struct and returning a LoginResponse struct.
+func Login(LoginCredentials *types.LoginCredentials) (*types.LoginResponse, error) {
+
+ // API Response struct
+ loginApiResponse := types.LoginResponse{}
+
+ //check if the UserLogin struct is empty
+ if err := checkUserLoginInfo(LoginCredentials); err != nil {
+ return nil, err
+ }
+
+ // User login struct to be converted to JSON
+ loginData, err := utils.MarshalStructToJSON(LoginCredentials)
+ if err != nil {
+ return nil, err
+ }
+
+ // Create a new http request
+ req, err := http.NewRequest(http.MethodPost, AuthLoginURL, bytes.NewBuffer(loginData))
+ if err != nil {
+ return nil, err
+ }
+
+ //Add headers pass in the pointer to set the headers on the request object
+ setHeaders(req)
+
+ //make the API Call
+ client := &http.Client{Timeout: time.Duration(HTTPTimeout) * time.Second}
+ resp, err := client.Do(req)
+ if err != nil {
+ return nil, err
+ }
+
+ defer resp.Body.Close()
+
+ // Get the response body
+ respBody, respErr := utils.FetchResponseBody(resp.Body)
+ if respErr != nil {
+ return nil, respErr
+ }
+
+ //marshall response to loginresponse struct
+ dataErr := utils.UnmarshalDataToStruct(respBody, &loginApiResponse)
+ if dataErr != nil {
+ return nil, dataErr
+ }
+
+ // check for successful login return value..return a login error
+ loginErr := checkUserLoginResponse(loginApiResponse.Msg)
+ if loginErr != nil {
+ return nil, loginErr
+ }
+
+ return &loginApiResponse, nil
+}
diff --git a/cmd/gogoodwe/semsapi/utils.go b/cmd/gogoodwe/semsapi/utils.go
new file mode 100644
index 0000000..17ac244
--- /dev/null
+++ b/cmd/gogoodwe/semsapi/utils.go
@@ -0,0 +1,31 @@
+package semsapi
+
+import (
+ "errors"
+ "net/http"
+ "strings"
+
+ "github.com/AaronSaikovski/gogoodwe/cmd/gogoodwe/types"
+)
+
+// SetHeaders - Set the login headers for the SEMS API login
+func setHeaders(r *http.Request) {
+ r.Header.Add("Content-Type", "application/json")
+ r.Header.Add("Token", "{\"version\":\"v2.1.0\",\"client\":\"ios\",\"language\":\"en\"}")
+}
+
+// CheckUserLoginResponse - check for successful login return value..return a login error
+func checkUserLoginResponse(loginResponse string) error {
+ if strings.Compare(loginResponse, "Successful") != 0 {
+ return errors.New("**API Login Error: " + loginResponse)
+ }
+ return nil
+}
+
+// CheckUserLoginInfo - Check user login struct is valid/not null
+func checkUserLoginInfo(UserLogin *types.LoginCredentials) error {
+ if *UserLogin == (types.LoginCredentials{}) {
+ return errors.New("**Error: User Login details are empty or invalid..**")
+ }
+ return nil
+}
diff --git a/cmd/gogoodwe/types/dailysummarydata.go b/cmd/gogoodwe/types/dailysummarydata.go
new file mode 100644
index 0000000..dd58b16
--- /dev/null
+++ b/cmd/gogoodwe/types/dailysummarydata.go
@@ -0,0 +1,25 @@
+/*
+# Name: DailySummaryData - Struct to hold daily summary data
+*/
+package types
+
+type DailySummaryData struct {
+ Language string `json:"language"`
+ HasError bool `json:"hasError"`
+ Msg string `json:"msg"`
+ Code string `json:"code"`
+ Data struct {
+ Kpi struct {
+ MonthGeneration float64 `json:"month_generation"`
+ Power float64 `json:"power"`
+ TotalPower float64 `json:"total_power"`
+ DayIncome float64 `json:"day_income"`
+ TotalIncome float64 `json:"total_income"`
+ Currency string `json:"currency"`
+ } `json:"kpi"`
+ Inverter []struct {
+ TotalGeneration string `json:"total_generation"`
+ DailyGeneration string `json:"daily_generation"`
+ } `json:"inverter"`
+ } `json:"data"`
+}
diff --git a/types/stationresponsedata.go b/cmd/gogoodwe/types/inverterdata.go
similarity index 72%
rename from types/stationresponsedata.go
rename to cmd/gogoodwe/types/inverterdata.go
index 8239905..81cdbfb 100644
--- a/types/stationresponsedata.go
+++ b/cmd/gogoodwe/types/inverterdata.go
@@ -1,43 +1,18 @@
+/*
+# Name: InverterData - Struct to hold data returned from the Powerstation API
+# Minimised version - removed any sensitive data
+# Author: Aaron Saikovski - asaikovski@outlook.com
+*/
package types
-// StationResponseData - Struct to hold data returned from the Powerstation API
-type StationResponseData struct {
+// InverterData - Struct to hold data returned from the Inverter Powerstation API
+type InverterData struct {
Language string `json:"language"`
Function []string `json:"function"`
HasError bool `json:"hasError"`
Msg string `json:"msg"`
Code string `json:"code"`
Data struct {
- Info struct {
- PowerstationID string `json:"powerstation_id"`
- Time string `json:"time"`
- DateFormat string `json:"date_format"`
- DateFormatYm string `json:"date_format_ym"`
- Stationname string `json:"stationname"`
- Address string `json:"address"`
- OwnerName string `json:"owner_name"`
- OwnerPhone string `json:"owner_phone"`
- OwnerEmail string `json:"owner_email"`
- BatteryCapacity float64 `json:"battery_capacity"`
- TurnonTime string `json:"turnon_time"`
- CreateTime string `json:"create_time"`
- Capacity float64 `json:"capacity"`
- Longitude float64 `json:"longitude"`
- Latitude float64 `json:"latitude"`
- PowerstationType string `json:"powerstation_type"`
- Status int `json:"status"`
- IsStored bool `json:"is_stored"`
- IsPowerflow bool `json:"is_powerflow"`
- ChartsType int `json:"charts_type"`
- HasPv bool `json:"has_pv"`
- HasStatisticsCharts bool `json:"has_statistics_charts"`
- OnlyBps bool `json:"only_bps"`
- OnlyBpu bool `json:"only_bpu"`
- TimeSpan float64 `json:"time_span"`
- PrValue string `json:"pr_value"`
- OrgCode string `json:"org_code"`
- OrgName string `json:"org_name"`
- } `json:"info"`
Kpi struct {
MonthGeneration float64 `json:"month_generation"`
Pac float64 `json:"pac"`
@@ -50,80 +25,18 @@ type StationResponseData struct {
} `json:"kpi"`
PowercontrolStatus int `json:"powercontrol_status"`
Images []any `json:"images"`
- Weather struct {
- HeWeather6 []struct {
- Basic struct {
- Cid any `json:"cid"`
- Location any `json:"location"`
- ParentCity any `json:"parent_city"`
- AdminArea any `json:"admin_area"`
- Cnty any `json:"cnty"`
- Lat any `json:"lat"`
- Lon any `json:"lon"`
- Tz any `json:"tz"`
- } `json:"basic"`
- Update struct {
- Loc any `json:"loc"`
- Utc any `json:"utc"`
- } `json:"update"`
- Status string `json:"status"`
- DailyForecast []struct {
- CondCodeD string `json:"cond_code_d"`
- CondCodeN string `json:"cond_code_n"`
- CondTxtD string `json:"cond_txt_d"`
- CondTxtN string `json:"cond_txt_n"`
- Date string `json:"date"`
- Hum string `json:"hum"`
- Pcpn string `json:"pcpn"`
- Pop string `json:"pop"`
- Pres string `json:"pres"`
- TmpMax string `json:"tmp_max"`
- TmpMin string `json:"tmp_min"`
- UvIndex string `json:"uv_index"`
- Vis string `json:"vis"`
- WindDeg string `json:"wind_deg"`
- WindDir string `json:"wind_dir"`
- WindSc string `json:"wind_sc"`
- WindSpd string `json:"wind_spd"`
- } `json:"daily_forecast"`
- } `json:"HeWeather6"`
- } `json:"weather"`
- Inverter []struct {
- Sn string `json:"sn"`
- Dict struct {
- Left []struct {
- IsHT bool `json:"isHT"`
- IsStoreSkip bool `json:"isStoreSkip"`
- Key string `json:"key"`
- Value string `json:"value"`
- Unit string `json:"unit"`
- IsFaultMsg int `json:"isFaultMsg"`
- FaultMsgCode int `json:"faultMsgCode"`
- } `json:"left"`
- Right []struct {
- IsHT bool `json:"isHT"`
- IsStoreSkip bool `json:"isStoreSkip"`
- Key string `json:"key"`
- Value string `json:"value"`
- Unit string `json:"unit"`
- IsFaultMsg int `json:"isFaultMsg"`
- FaultMsgCode int `json:"faultMsgCode"`
- } `json:"right"`
- } `json:"dict"`
- IsStored bool `json:"is_stored"`
- Name string `json:"name"`
- InPac float64 `json:"in_pac"`
- OutPac float64 `json:"out_pac"`
- Eday float64 `json:"eday"`
- Emonth float64 `json:"emonth"`
- Etotal float64 `json:"etotal"`
- Status int `json:"status"`
- TurnonTime string `json:"turnon_time"`
- ReleationID string `json:"releation_id"`
- Type string `json:"type"`
- Capacity float64 `json:"capacity"`
- D struct {
- PwID string `json:"pw_id"`
+ Inverter []struct {
+ IsStored bool `json:"is_stored"`
+ InPac float64 `json:"in_pac"`
+ OutPac float64 `json:"out_pac"`
+ Eday float64 `json:"eday"`
+ Emonth float64 `json:"emonth"`
+ Etotal float64 `json:"etotal"`
+ Status int `json:"status"`
+ TurnonTime string `json:"turnon_time"`
+ Type string `json:"type"`
+ Capacity float64 `json:"capacity"`
+ D struct {
Capacity string `json:"capacity"`
Model string `json:"model"`
OutputPower string `json:"output_power"`
@@ -197,10 +110,7 @@ type StationResponseData struct {
InvertFull struct {
CtSolutionType int `json:"ct_solution_type"`
Cts any `json:"cts"`
- Sn string `json:"sn"`
CheckCode string `json:"check_code"`
- PowerstationID string `json:"powerstation_id"`
- Name string `json:"name"`
ModelType string `json:"model_type"`
ChangeType int `json:"change_type"`
ChangeTime int `json:"change_time"`
@@ -361,43 +271,29 @@ type StationResponseData struct {
BatteryCharging string `json:"battery_charging"`
LastRefreshTime string `json:"last_refresh_time"`
BmsStatus string `json:"bms_status"`
- PwID string `json:"pw_id"`
FaultMessage string `json:"fault_message"`
WarningCode any `json:"warning_code"`
BatteryPower float64 `json:"battery_power"`
- PointIndex string `json:"point_index"`
- Points []struct {
- TargetIndex int `json:"target_index"`
- TargetName string `json:"target_name"`
- Display string `json:"display"`
- Unit string `json:"unit"`
- TargetKey string `json:"target_key"`
- TextCn string `json:"text_cn"`
- TargetSnSix any `json:"target_sn_six"`
- TargetSnSeven any `json:"target_sn_seven"`
- TargetType any `json:"target_type"`
- StorageName any `json:"storage_name"`
- } `json:"points"`
- BackupPloadS float64 `json:"backup_pload_s"`
- BackupVloadS float64 `json:"backup_vload_s"`
- BackupIloadS float64 `json:"backup_iload_s"`
- BackupPloadT float64 `json:"backup_pload_t"`
- BackupVloadT float64 `json:"backup_vload_t"`
- BackupIloadT float64 `json:"backup_iload_t"`
- EtotalBuy any `json:"etotal_buy"`
- EdayBuy float64 `json:"eday_buy"`
- EbatteryCharge any `json:"ebattery_charge"`
- EchargeDay float64 `json:"echarge_day"`
- EbatteryDischarge any `json:"ebattery_discharge"`
- EdischargeDay float64 `json:"edischarge_day"`
- BattStrings any `json:"batt_strings"`
- MeterConnectStatus any `json:"meter_connect_status"`
- MtactivepowerR float64 `json:"mtactivepower_r"`
- MtactivepowerS float64 `json:"mtactivepower_s"`
- MtactivepowerT float64 `json:"mtactivepower_t"`
- HasTigo bool `json:"has_tigo"`
- CanStartIV bool `json:"canStartIV"`
- BatteryCount any `json:"battery_count"`
+ BackupPloadS float64 `json:"backup_pload_s"`
+ BackupVloadS float64 `json:"backup_vload_s"`
+ BackupIloadS float64 `json:"backup_iload_s"`
+ BackupPloadT float64 `json:"backup_pload_t"`
+ BackupVloadT float64 `json:"backup_vload_t"`
+ BackupIloadT float64 `json:"backup_iload_t"`
+ EtotalBuy any `json:"etotal_buy"`
+ EdayBuy float64 `json:"eday_buy"`
+ EbatteryCharge any `json:"ebattery_charge"`
+ EchargeDay float64 `json:"echarge_day"`
+ EbatteryDischarge any `json:"ebattery_discharge"`
+ EdischargeDay float64 `json:"edischarge_day"`
+ BattStrings any `json:"batt_strings"`
+ MeterConnectStatus any `json:"meter_connect_status"`
+ MtactivepowerR float64 `json:"mtactivepower_r"`
+ MtactivepowerS float64 `json:"mtactivepower_s"`
+ MtactivepowerT float64 `json:"mtactivepower_t"`
+ HasTigo bool `json:"has_tigo"`
+ CanStartIV bool `json:"canStartIV"`
+ BatteryCount any `json:"battery_count"`
} `json:"inverter"`
Hjgx struct {
Co2 float64 `json:"co2"`
@@ -466,7 +362,6 @@ type StationResponseData struct {
Environmental []any `json:"environmental"`
Equipment []struct {
Type string `json:"type"`
- Title string `json:"title"`
Status int `json:"status"`
Model any `json:"model"`
StatusText any `json:"statusText"`
@@ -479,8 +374,6 @@ type StationResponseData struct {
IsStored bool `json:"isStored"`
Soc string `json:"soc"`
IsChange bool `json:"isChange"`
- RelationID string `json:"relationId"`
- Sn string `json:"sn"`
HasTigo bool `json:"has_tigo"`
IsSec bool `json:"is_sec"`
IsSecs bool `json:"is_secs"`
@@ -489,11 +382,4 @@ type StationResponseData struct {
TitleSn any `json:"titleSn"`
} `json:"equipment"`
} `json:"data"`
- Components struct {
- Para string `json:"para"`
- LangVer int `json:"langVer"`
- TimeSpan int `json:"timeSpan"`
- API string `json:"api"`
- MsgSocketAdr any `json:"msgSocketAdr"`
- } `json:"components"`
}
diff --git a/cmd/gogoodwe/types/logincredentials.go b/cmd/gogoodwe/types/logincredentials.go
new file mode 100644
index 0000000..64a12c1
--- /dev/null
+++ b/cmd/gogoodwe/types/logincredentials.go
@@ -0,0 +1,12 @@
+/*
+# Name: LoginCredentials - Struct to hold User login data
+# Author: Aaron Saikovski - asaikovski@outlook.com
+*/
+package types
+
+// LoginCredentials - Struct to hold User login data
+type LoginCredentials struct {
+ Account string `json:"account"`
+ Password string `json:"pwd"`
+ PowerStationID string `json:"powerstationid"`
+}
diff --git a/types/semsresponsedata.go b/cmd/gogoodwe/types/loginresponse.go
similarity index 59%
rename from types/semsresponsedata.go
rename to cmd/gogoodwe/types/loginresponse.go
index 5abcd61..5d11885 100644
--- a/types/semsresponsedata.go
+++ b/cmd/gogoodwe/types/loginresponse.go
@@ -1,15 +1,19 @@
+/*
+# Name: LoginResponse - SEMS API Response Data struct
+# Contains all the JSON Response data returned from the authentication API - "https://www.semsportal.com/api/v2/Common/CrossLogin"
+# Will be unmarshalled to a struct via a pointer
+# Author: Aaron Saikovski - asaikovski@outlook.com
+*/
package types
-// SemsResponseData - SEMS API Response Data struct
-// Contains all the JSON Response data returned from the authentication API - "https://www.semsportal.com/api/v2/Common/CrossLogin"
-// Will be unmarshalled to a struct via a pointer
-type SemsResponseData struct {
+// LoginResponse - SEMS API Response Data struct
+type LoginResponse struct {
HasError bool `json:"hasError"`
Code int32 `json:"code"`
Msg string `json:"msg"`
Data struct {
UID string `json:"uid"`
- Timestamp int `json:"timestamp"`
+ Timestamp int64 `json:"timestamp"`
Token string `json:"token"`
Client string `json:"client"`
Version string `json:"version"`
diff --git a/cmd/gogoodwe/utils/args.go b/cmd/gogoodwe/utils/args.go
new file mode 100644
index 0000000..9292c71
--- /dev/null
+++ b/cmd/gogoodwe/utils/args.go
@@ -0,0 +1,24 @@
+package utils
+
+var (
+ // Version string
+ VersionString string = "gogoodwe v2.0.0"
+)
+
+// Args - struct using go-arg- https://github.com/alexflint/go-arg
+type Args struct {
+ Account string `arg:"required,-a,--account" help:"SEMS Email Account."`
+ Password string `arg:"required,-p,--password" help:"SEMS Account password."`
+ PowerStationID string `arg:"required,-i,--powerstationid" help:"SEMS Powerstation ID."`
+ DailySummary bool `arg:"-d,--dailysummary" help:"Output as a daily summary."`
+}
+
+// Description - App description
+func (Args) Description() string {
+ return "A command line tool to query the GOODWE SEMS Portal APIs and Solar SEMS API."
+}
+
+// Version - Version info
+func (Args) Version() string {
+ return VersionString
+}
diff --git a/utils/errorhandler.go b/cmd/gogoodwe/utils/errorhandler.go
similarity index 99%
rename from utils/errorhandler.go
rename to cmd/gogoodwe/utils/errorhandler.go
index a365476..2beccae 100644
--- a/utils/errorhandler.go
+++ b/cmd/gogoodwe/utils/errorhandler.go
@@ -2,6 +2,7 @@ package utils
import (
"log"
+
"github.com/logrusorgru/aurora"
)
diff --git a/utils/responsehandler.go b/cmd/gogoodwe/utils/jsonutils.go
similarity index 70%
rename from utils/responsehandler.go
rename to cmd/gogoodwe/utils/jsonutils.go
index 7f06c10..c086406 100644
--- a/utils/responsehandler.go
+++ b/cmd/gogoodwe/utils/jsonutils.go
@@ -1,16 +1,13 @@
+/*
+# Name: jsonutils - helper functions to get the Powerstation Data from the API
+# Author: Aaron Saikovski - asaikovski@outlook.com
+*/
package utils
import (
"encoding/json"
- "io"
)
-// FetchResponseBody - Get the response body from a HTTP response
-func FetchResponseBody(resp io.Reader) ([]byte, error) {
- respBody, err := io.ReadAll(resp)
- return respBody, err
-}
-
// UnmarshalDataToStruct - Unmarshall http response to target struct
func UnmarshalDataToStruct(respBody []byte, targetStruct interface{}) error {
resperr := json.Unmarshal(respBody, &targetStruct)
diff --git a/utils/paramcheck.go b/cmd/gogoodwe/utils/paramcheck.go
similarity index 100%
rename from utils/paramcheck.go
rename to cmd/gogoodwe/utils/paramcheck.go
diff --git a/cmd/gogoodwe/utils/response.go b/cmd/gogoodwe/utils/response.go
new file mode 100644
index 0000000..5b93e20
--- /dev/null
+++ b/cmd/gogoodwe/utils/response.go
@@ -0,0 +1,11 @@
+package utils
+
+import (
+ "io"
+)
+
+// FetchResponseBody - Get the response body from a HTTP response
+func FetchResponseBody(resp io.Reader) ([]byte, error) {
+ respBody, err := io.ReadAll(resp)
+ return respBody, err
+}
diff --git a/constants/constants.go b/constants/constants.go
deleted file mode 100644
index cb423b5..0000000
--- a/constants/constants.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package constants
-
-const (
- // Auth Login Url
- AuthLoginUrL string = "https://www.semsportal.com/api/v2/Common/CrossLogin"
-
- // Powerstation API Url
- PowerStationURL string = "v2/PowerStation/GetMonitorDetailByPowerstationId"
-
- // Default timeout value
- HTTPTimeout = 20
-
- // Version string
- VersionString string = "gogoodwe v0.0.7"
-
- //API login success response message
- SemsLoginSuccessResponse string = "Successful"
-)
diff --git a/go.mod b/go.mod
index c0b1f97..736a067 100644
--- a/go.mod
+++ b/go.mod
@@ -1,10 +1,11 @@
module github.com/AaronSaikovski/gogoodwe
-go 1.21
+go 1.22.0
require (
github.com/alexflint/go-arg v1.4.3
github.com/logrusorgru/aurora v2.0.3+incompatible
+ github.com/valyala/fastjson v1.6.4
)
-require github.com/alexflint/go-scalar v1.1.0 // indirect
+require github.com/alexflint/go-scalar v1.2.0 // indirect
diff --git a/go.sum b/go.sum
index d3619e9..cc942b3 100644
--- a/go.sum
+++ b/go.sum
@@ -1,7 +1,8 @@
github.com/alexflint/go-arg v1.4.3 h1:9rwwEBpMXfKQKceuZfYcwuc/7YY7tWJbFsgG5cAU/uo=
github.com/alexflint/go-arg v1.4.3/go.mod h1:3PZ/wp/8HuqRZMUUgu7I+e1qcpUbvmS258mRXkFH4IA=
-github.com/alexflint/go-scalar v1.1.0 h1:aaAouLLzI9TChcPXotr6gUhq+Scr8rl0P9P4PnltbhM=
github.com/alexflint/go-scalar v1.1.0/go.mod h1:LoFvNMqS1CPrMVltza4LvnGKhaSpc3oyLEBUZVhhS2o=
+github.com/alexflint/go-scalar v1.2.0 h1:WR7JPKkeNpnYIOfHRa7ivM21aWAdHD0gEWHCx+WQBRw=
+github.com/alexflint/go-scalar v1.2.0/go.mod h1:LoFvNMqS1CPrMVltza4LvnGKhaSpc3oyLEBUZVhhS2o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -13,6 +14,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ=
+github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/main.go b/main.go
deleted file mode 100644
index 62ad0ad..0000000
--- a/main.go
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
-# Name: main package - Authenticates to and queries the SEMS Solar inverter API
-# Author: Aaron Saikovski - asaikovski@outlook.com
-*/
-package main
-
-// Main package - This is the main program entry point
-import (
- "github.com/AaronSaikovski/gogoodwe/constants"
- "github.com/AaronSaikovski/gogoodwe/pkg/goodwe/fetchdata"
- "github.com/AaronSaikovski/gogoodwe/types"
- "github.com/AaronSaikovski/gogoodwe/utils"
- "github.com/alexflint/go-arg"
-)
-
-// args - srtruct using go-arg- https://github.com/alexflint/go-arg
-type args struct {
- Account string `arg:"required,-a,--account" help:"SEMS Email Account."`
- Pwd string `arg:"required,-p,--pwd" help:"SEMS Account password."`
- PowerStationID string `arg:"required,-i,--powerstationid" help:"SEMS Powerstation ID."`
-}
-
-// Description - App description
-func (args) Description() string {
- return "A command line tool and GoLang package to query the GOODWE SEMS Portal APIs and Solar SEMS API."
-}
-
-// Version - Version info
-func (args) Version() string {
- return constants.VersionString
-}
-
-// main - program main
-func main() {
-
- //Get the args input data
- var args args
- p := arg.MustParse(&args)
-
- //check for valid email address input
- if !utils.CheckValidEmail(args.Account) {
- p.Fail("Invalid Email address format - should be: 'user@somedomain.com'.")
- }
-
- //check for valid powerstation Id
- if !utils.CheckValidPowerstationID(args.PowerStationID) {
- p.Fail("Invalid Powerstation ID format: - should be: 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'.")
- }
-
- // Create a new SemsLoginCreds object via a struct literal
- SemsUserLogin := types.SemsLoginCreds{
- Account: args.Account,
- Pwd: args.Pwd,
- PowerStationID: args.PowerStationID,
- }
-
- // Get the data from the API
- fetchdata.GetData(&SemsUserLogin)
-}
diff --git a/pkg/goodwe/authentication/authentication.go b/pkg/goodwe/authentication/authentication.go
deleted file mode 100644
index 5b52eae..0000000
--- a/pkg/goodwe/authentication/authentication.go
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
-# Name: authentication - authenticates to the goodwe API - https://www.semsportal.com/api/v2/Common/CrossLogin
-# Author: Aaron Saikovski - asaikovski@outlook.com
-*/
-
-package authentication
-
-import (
- "bytes"
- "net/http"
- "time"
-
- "github.com/AaronSaikovski/gogoodwe/constants"
- "github.com/AaronSaikovski/gogoodwe/types"
- "github.com/AaronSaikovski/gogoodwe/utils"
-)
-
-// SetHeaders - Set the login headers for the SEMS API login
-func SetHeaders(r *http.Request) {
- r.Header.Add("Content-Type", "application/json")
- r.Header.Add("Token", "{\"version\":\"v2.1.0\",\"client\":\"ios\",\"language\":\"en\"}")
-}
-
-// DoLogin - Main public login function
-// Logs into the SEMs API
-func DoLogin(SemsResponseData *types.SemsResponseData, UserLogin *types.SemsLoginCreds) error {
-
- //check if the UserLogin struct is empty
- usererr := CheckUserLoginInfo(UserLogin)
- if usererr != nil {
- utils.HandleError(usererr)
- return usererr
- }
-
- // User login struct to be converted to JSON
- jsonData, _ := utils.MarshalStructToJSON(UserLogin)
-
- // Create a new http request
- req, err := http.NewRequest(http.MethodPost, constants.AuthLoginUrL, bytes.NewBuffer(jsonData))
- if err != nil {
- utils.HandleError(err)
- return err
- }
-
- //Add headers pass in the pointer to set the headers on the request object
- SetHeaders(req)
-
- //make the API Call
- client := &http.Client{Timeout: constants.HTTPTimeout * time.Second}
- resp, err := client.Do(req)
- if err != nil {
- utils.HandleError(err)
- return err
- }
-
- //cleanup
- defer resp.Body.Close()
-
- // Get the response body
- respBody, _ := utils.FetchResponseBody(resp.Body)
-
- //marshall response to SemsRespInfo struct
- utils.UnmarshalDataToStruct(respBody, &SemsResponseData)
-
- // check for successful login return value..return a login error
- CheckUserLoginResponse(SemsResponseData.Msg)
-
- return nil
-
-}
diff --git a/pkg/goodwe/authentication/authhelper.go b/pkg/goodwe/authentication/authhelper.go
deleted file mode 100644
index b103423..0000000
--- a/pkg/goodwe/authentication/authhelper.go
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
-# Name: authhelper - auth helper functions
-# Author: Aaron Saikovski - asaikovski@outlook.com
-*/
-
-package authentication
-
-import (
- "errors"
- "strings"
-
- "github.com/AaronSaikovski/gogoodwe/constants"
- "github.com/AaronSaikovski/gogoodwe/types"
- "github.com/AaronSaikovski/gogoodwe/utils"
-)
-
-// CheckUserLoginInfo - Check user login struct is valid/not null
-func CheckUserLoginInfo(UserLogin *types.SemsLoginCreds) error {
- //check if the UserLogin struct is empty
- if (*UserLogin == types.SemsLoginCreds{}) {
- return errors.New("**Error: User Login details are empty or invalid..**")
- } else {
- return nil
- }
-}
-
-// CheckUserLoginResponse - check for successful login return value..return a login error
-func CheckUserLoginResponse(loginResponse string) {
- if strings.Compare(loginResponse, constants.SemsLoginSuccessResponse) != 0 {
- authErr := errors.New("API Login Error: " + loginResponse)
- utils.HandleError(authErr)
- }
-}
diff --git a/pkg/goodwe/fetchdata/fetchdata.go b/pkg/goodwe/fetchdata/fetchdata.go
deleted file mode 100644
index e12d91b..0000000
--- a/pkg/goodwe/fetchdata/fetchdata.go
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
-# Name: fetchdata - fetches data from the goodwe API - and processes it to pass back to caller
-# Author: Aaron Saikovski - asaikovski@outlook.com
-*/
-package fetchdata
-
-import (
- "errors"
- "fmt"
-
- "github.com/AaronSaikovski/gogoodwe/pkg/goodwe/authentication"
- "github.com/AaronSaikovski/gogoodwe/pkg/goodwe/powerstationdata"
-
- "github.com/AaronSaikovski/gogoodwe/types"
- "github.com/AaronSaikovski/gogoodwe/utils"
- "github.com/logrusorgru/aurora"
-)
-
-// doLogin - Login to the API
-func doLogin(SemsUserLogin *types.SemsLoginCreds, SemsResponseData *types.SemsResponseData) error {
-
- // Do the login - update the pointer to the struct SemsResponseData
- autherr := authentication.DoLogin(SemsResponseData, SemsUserLogin)
- if autherr != nil {
- utils.HandleError(autherr)
- return autherr
- } else {
- return nil
- }
-}
-
-// GetData - Main process data function
-func GetData(SemsUserLogin *types.SemsLoginCreds) {
-
- // Data types
- var SemsResponseData types.SemsResponseData
- var PowerstationData types.StationResponseData
-
- // Do the login..check for errors
- err := doLogin(SemsUserLogin, &SemsResponseData)
- if err == nil {
-
- // Fetch the data
- dataerr := powerstationdata.FetchData(&SemsResponseData, SemsUserLogin, &PowerstationData)
- if dataerr != nil {
- utils.HandleError(errors.New("error: fetching powerstation data, check powerstationid is correct"))
- } else {
- // Get output
- dataOutput, jsonerr := powerstationdata.GetDataJSON(&PowerstationData)
- if jsonerr != nil {
- utils.HandleError(errors.New("error: converting powerstation data"))
-
- } else {
- //Display output
- fmt.Println(aurora.BrightYellow(string(dataOutput)))
- }
- }
-
- } else {
- utils.HandleError(err)
- }
-}
diff --git a/pkg/goodwe/powerstationdata/powerstationdata.go b/pkg/goodwe/powerstationdata/powerstationdata.go
deleted file mode 100644
index e4bc859..0000000
--- a/pkg/goodwe/powerstationdata/powerstationdata.go
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
-# Name: powerstationdata - gets data from the goodwe API - "v2/PowerStation/GetMonitorDetailByPowerstationId"
-# Author: Aaron Saikovski - asaikovski@outlook.com
-*/
-package powerstationdata
-
-import (
- "bytes"
- "net/http"
- "time"
-
- "github.com/AaronSaikovski/gogoodwe/constants"
- "github.com/AaronSaikovski/gogoodwe/types"
- "github.com/AaronSaikovski/gogoodwe/utils"
-)
-
-// setHeaders - Set the headers for the SEMS Data API
-func setHeaders(r *http.Request, tokenstring []byte) {
- r.Header.Add("Content-Type", "application/json")
- r.Header.Add("Token", string(tokenstring))
-}
-
-// FetchData - Fetches Data from the specified PowerstationID via tht SEMs API
-func FetchData(SemsResponseData *types.SemsResponseData,
- UserLogin *types.SemsLoginCreds,
- PowerstationOutputData *types.StationResponseData) error {
-
- // get the Token header data
- tokenMapJSONData, _ := DataTokenJSON(SemsResponseData)
-
- // get the Powerstation ID header data
- powerStationMapJSONData, _ := PowerStationIDJSON(UserLogin)
-
- //Get the url from the Auth API and append the data url part
- url := SemsResponseData.API + constants.PowerStationURL
-
- // Create a new http request
- req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(powerStationMapJSONData))
- if err != nil {
- utils.HandleError(err)
- }
-
- //Add headers pass in the pointer to set the headers on the request object
- setHeaders(req, tokenMapJSONData)
-
- //make the API Call
- client := &http.Client{Timeout: constants.HTTPTimeout * time.Second}
- resp, err := client.Do(req)
- if err != nil {
- utils.HandleError(err)
- return err
- }
-
- //cleanup
- defer resp.Body.Close()
-
- // Get the response body
- respBody, _ := utils.FetchResponseBody(resp.Body)
-
- //marshall response to SemsRespInfo struct
- dataerr := utils.UnmarshalDataToStruct(respBody, &PowerstationOutputData)
- if dataerr != nil {
- return dataerr
- }
-
- return nil
-
-}
diff --git a/pkg/goodwe/powerstationdata/powerstationdatahelper.go b/pkg/goodwe/powerstationdata/powerstationdatahelper.go
deleted file mode 100644
index e6a809b..0000000
--- a/pkg/goodwe/powerstationdata/powerstationdatahelper.go
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
-# Name: powerstationdatahelper - helper functions to get the Powerstation Data from the API
-# Author: Aaron Saikovski - asaikovski@outlook.com
-*/
-package powerstationdata
-
-import (
- "encoding/json"
- "strconv"
-
- "github.com/AaronSaikovski/gogoodwe/types"
- "github.com/AaronSaikovski/gogoodwe/utils"
-)
-
-// DataTokenJSON - Makes a map for the token to be passed to the Data API header and returns a JSON string
-func DataTokenJSON(SemsResponseData *types.SemsResponseData) ([]byte, error) {
-
- tokenMap := make(map[string]string)
- tokenMap["version"] = "v2.1.0"
- tokenMap["client"] = "ios"
- tokenMap["language"] = "en"
- tokenMap["timestamp"] = strconv.Itoa(SemsResponseData.Data.Timestamp)
- tokenMap["uid"] = SemsResponseData.Data.UID
- tokenMap["token"] = SemsResponseData.Data.Token
-
- // convert to byte[]
- jsonStr, err := json.Marshal(tokenMap)
- return jsonStr, err
-}
-
-// PowerStationIDJSON - Makes a map for the powerStationId to be passed to the Data API header and returns a JSON string
-func PowerStationIDJSON(UserLogin *types.SemsLoginCreds) ([]byte, error) {
- powerStationMap := make(map[string]string)
- powerStationMap["powerStationId"] = UserLogin.PowerStationID
-
- // convert to byte[]
- jsonStr, err := json.Marshal(powerStationMap)
- return jsonStr, err
-}
-
-// GetDataJSON - Returns the PowerstationOutputData as JSON
-func GetDataJSON(PowerstationOutputData *types.StationResponseData) ([]byte, error) {
-
- // Get the response and return any errors
- resp, err := utils.MarshalStructToJSON(&PowerstationOutputData)
- return resp, err
-}
diff --git a/test/README.md b/test/README.md
new file mode 100644
index 0000000..cdcf65f
--- /dev/null
+++ b/test/README.md
@@ -0,0 +1,9 @@
+# `/test`
+
+Additional external test apps and test data. Feel free to structure the `/test` directory anyway you want. For bigger projects it makes sense to have a data subdirectory. For example, you can have `/test/data` or `/test/testdata` if you need Go to ignore what's in that directory. Note that Go will also ignore directories or files that begin with "." or "_", so you have more flexibility in terms of how you name your test data directory.
+
+Examples:
+
+* https://github.com/openshift/origin/tree/master/test (test data is in the `/testdata` subdirectory)
+
+
diff --git a/test/samplemodule_test.go b/test/samplemodule_test.go
new file mode 100644
index 0000000..c7bdb64
--- /dev/null
+++ b/test/samplemodule_test.go
@@ -0,0 +1,22 @@
+package testing
+
+/*
+A Sample test harness.
+*/
+
+// import (
+// "testing"
+
+// "github.com/AaronSaikovski/gostarter/internal/pkg/samplemodule"
+// )
+
+// // A testing function.
+// func TestSampleFunction(t *testing.T) {
+
+// msg := samplemodule.SampleFunction()
+// expected := "OK"
+
+// if msg != expected {
+// t.Errorf("Module expected '%q' but got '%q'", expected, msg)
+// }
+// }
diff --git a/test/struct_test.go b/test/struct_test.go
new file mode 100644
index 0000000..2add9b8
--- /dev/null
+++ b/test/struct_test.go
@@ -0,0 +1,21 @@
+/*
+A Sample test harness.
+*/
+
+package testing
+
+// import (
+// "github.com/AaronSaikovski/gogoodwe/cmd/gogoodwe/types"
+// "testing"
+// )
+
+// // A testing function.
+// func TestLoginCredentials(t *testing.T) {
+
+// expected := "test data"
+// ateststruct := types.LoginCredentials{SampleString: "test data", SampleInt: 1}
+
+// if ateststruct.SampleString != expected {
+// t.Errorf("struct expected '%s' but got '%s'", expected, ateststruct.SampleString)
+// }
+// }
diff --git a/types/semslogincreds.go b/types/semslogincreds.go
deleted file mode 100644
index b08db27..0000000
--- a/types/semslogincreds.go
+++ /dev/null
@@ -1,8 +0,0 @@
-package types
-
-// SemsLoginCreds - Struct to hold User login data
-type SemsLoginCreds struct {
- Account string `json:"account"`
- Pwd string `json:"pwd"`
- PowerStationID string `json:"powerstationid"`
-}