forked from massivebox/ecodash
Refactoring
This commit is contained in:
parent
9bd3f35cc7
commit
345adcf479
|
@ -1,7 +1,7 @@
|
||||||
# options for analysis running
|
# options for analysis running
|
||||||
run:
|
run:
|
||||||
# timeout for analysis, e.g. 30s, 5m, default is 1m
|
# timeout for analysis, e.g. 30s, 5m, default is 1m
|
||||||
timeout: 5m
|
timeout: 10m
|
||||||
|
|
||||||
# output configuration options
|
# output configuration options
|
||||||
output:
|
output:
|
||||||
|
|
|
@ -5,7 +5,10 @@ COPY --from=0 /usr/bin/golangci-lint /usr/bin/golangci-lint
|
||||||
RUN apk add --no-cache gcc libc-dev
|
RUN apk add --no-cache gcc libc-dev
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY src /app/
|
COPY src /app/src
|
||||||
|
COPY go.mod /app/
|
||||||
|
COPY go.sum /app/
|
||||||
|
COPY .golangci.yml /app/
|
||||||
|
|
||||||
RUN golangci-lint run
|
RUN golangci-lint run
|
||||||
RUN go test ./src/...
|
RUN go test ./src/...
|
||||||
|
|
|
@ -31,18 +31,21 @@ func (config *Config) queryHistory(entityID string, startTime, endTime time.Time
|
||||||
req, err := http.NewRequestWithContext(
|
req, err := http.NewRequestWithContext(
|
||||||
ctx,
|
ctx,
|
||||||
http.MethodGet,
|
http.MethodGet,
|
||||||
config.HomeAssistant.BaseURL+
|
fmt.Sprintf(
|
||||||
"/api/history/period/"+url.QueryEscape(startTime.Format(time.RFC3339))+
|
"%s/api/history/period/%s?filter_entity_id=%s&end_time=%s",
|
||||||
"?filter_entity_id="+entityID+
|
config.HomeAssistant.BaseURL,
|
||||||
"&end_time="+url.QueryEscape(endTime.Format(time.RFC3339)),
|
url.QueryEscape(startTime.Format(time.RFC3339)),
|
||||||
nil,
|
entityID,
|
||||||
|
url.QueryEscape(endTime.Format(time.RFC3339)),
|
||||||
|
),
|
||||||
|
http.NoBody,
|
||||||
)
|
)
|
||||||
cancel()
|
cancel()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return HistoryResult{}, err
|
return HistoryResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Add("Authorization", "Bearer "+config.HomeAssistant.ApiKey)
|
req.Header.Add("Authorization", "Bearer "+config.HomeAssistant.APIKey)
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
|
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
|
@ -239,7 +242,7 @@ func fillMissing(days []DayData, startTime, endTime time.Time) []DayData {
|
||||||
previousDay time.Time
|
previousDay time.Time
|
||||||
defaultDay time.Time
|
defaultDay time.Time
|
||||||
previousValue float32
|
previousValue float32
|
||||||
ret []DayData
|
ret = make([]DayData, 0, len(days))
|
||||||
currentTime = time.Now()
|
currentTime = time.Now()
|
||||||
)
|
)
|
||||||
|
|
|
@ -27,7 +27,7 @@ type Config struct {
|
||||||
type HomeAssistant struct {
|
type HomeAssistant struct {
|
||||||
InstallationDate time.Time `json:"installation_date"`
|
InstallationDate time.Time `json:"installation_date"`
|
||||||
BaseURL string `json:"base_url"`
|
BaseURL string `json:"base_url"`
|
||||||
ApiKey string `json:"api_key"`
|
APIKey string `json:"api_key"`
|
||||||
}
|
}
|
||||||
type Sensors struct {
|
type Sensors struct {
|
||||||
PolledSmartEnergySummation string `json:"polled_smart_energy_summation"`
|
PolledSmartEnergySummation string `json:"polled_smart_energy_summation"`
|
||||||
|
@ -54,7 +54,7 @@ func formatURL(url string) (string, error) {
|
||||||
}
|
}
|
||||||
url = strings.TrimSuffix(url, "/")
|
url = strings.TrimSuffix(url, "/")
|
||||||
|
|
||||||
test := regexp.MustCompile(`(?m)https?:\/\/[^/]*`).ReplaceAllString(url, "")
|
test := regexp.MustCompile(`(?m)https?://[^/]*`).ReplaceAllString(url, "")
|
||||||
if test != "" {
|
if test != "" {
|
||||||
return "", errBadHAFormat
|
return "", errBadHAFormat
|
||||||
}
|
}
|
||||||
|
@ -128,9 +128,9 @@ func (config *Config) isAuthorized(c *fiber.Ctx) bool {
|
||||||
return c.Cookies("admin_username") == config.Administrator.Username && c.Cookies("admin_password_hash") == config.Administrator.PasswordHash
|
return c.Cookies("admin_username") == config.Administrator.Username && c.Cookies("admin_password_hash") == config.Administrator.PasswordHash
|
||||||
}
|
}
|
||||||
|
|
||||||
func (config *Config) equals(new *Config) bool {
|
func (config *Config) Equals(c *Config) bool {
|
||||||
return reflect.DeepEqual(new.HomeAssistant, config.HomeAssistant) &&
|
return reflect.DeepEqual(c.HomeAssistant, config.HomeAssistant) &&
|
||||||
reflect.DeepEqual(new.Sensors, config.Sensors) &&
|
reflect.DeepEqual(c.Sensors, config.Sensors) &&
|
||||||
reflect.DeepEqual(new.Administrator, config.Administrator) &&
|
reflect.DeepEqual(c.Administrator, config.Administrator) &&
|
||||||
reflect.DeepEqual(new.Dashboard, config.Dashboard)
|
reflect.DeepEqual(c.Dashboard, config.Dashboard)
|
||||||
}
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
@ -62,18 +63,26 @@ func (config *Config) refreshCacheFromPast(pastTime time.Time) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for key, day := range greenEnergyPercentage {
|
stmtReplace, err := config.db.Prepare("INSERT OR REPLACE INTO cache(time, green_energy_percentage, energy_consumption) values(?,?,?)")
|
||||||
var action2 string
|
|
||||||
if greenEnergyPercentage[key].Value != 0 && historyPolledSmartEnergySummation[key].Value != 0 {
|
|
||||||
action2 = "REPLACE"
|
|
||||||
} else {
|
|
||||||
action2 = "IGNORE"
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt, err := config.db.Prepare("INSERT OR " + action2 + " INTO cache(time, green_energy_percentage, energy_consumption) values(?,?,?)")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
defer stmtReplace.Close()
|
||||||
|
|
||||||
|
stmtIgnore, err := config.db.Prepare("INSERT OR IGNORE INTO cache(time, green_energy_percentage, energy_consumption) values(?,?,?)")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer stmtIgnore.Close()
|
||||||
|
|
||||||
|
for key, day := range greenEnergyPercentage {
|
||||||
|
var stmt *sql.Stmt
|
||||||
|
if greenEnergyPercentage[key].Value != 0 && historyPolledSmartEnergySummation[key].Value != 0 {
|
||||||
|
stmt = stmtReplace
|
||||||
|
} else {
|
||||||
|
stmt = stmtIgnore
|
||||||
|
}
|
||||||
|
|
||||||
_, err = stmt.Exec(day.DayTime.Unix(), greenEnergyPercentage[key].Value, historyPolledSmartEnergySummation[key].Value)
|
_, err = stmt.Exec(day.DayTime.Unix(), greenEnergyPercentage[key].Value, historyPolledSmartEnergySummation[key].Value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -101,10 +110,13 @@ func (config *Config) readHistory() (History, error) {
|
||||||
)
|
)
|
||||||
err = rows.Scan(&date, &greenEnergyPercentage, &polledSmartEnergyConsumption)
|
err = rows.Scan(&date, &greenEnergyPercentage, &polledSmartEnergyConsumption)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return History{}, err
|
||||||
}
|
}
|
||||||
ret = append(ret, HistoryEntry{date, greenEnergyPercentage, polledSmartEnergyConsumption})
|
ret = append(ret, HistoryEntry{date, greenEnergyPercentage, polledSmartEnergyConsumption})
|
||||||
}
|
}
|
||||||
|
if rows.Err() != nil {
|
||||||
|
return History{}, rows.Err()
|
||||||
|
}
|
||||||
|
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"html"
|
||||||
"html/template"
|
"html/template"
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
|
@ -48,7 +49,7 @@ func (config *Config) adminEndpoint(c *fiber.Ctx) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return config.renderAdminPanel(c, Warning{
|
return config.renderAdminPanel(c, Warning{
|
||||||
Header: "An error occurred!",
|
Header: "An error occurred!",
|
||||||
Body: err.Error(),
|
Body: html.EscapeString(err.Error()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return config.renderAdminPanel(c, Warning{
|
return config.renderAdminPanel(c, Warning{
|
||||||
|
@ -81,6 +82,7 @@ func (config *Config) renderAdminPanel(c *fiber.Ctx, warning ...Warning) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(warning) > 0 {
|
if len(warning) > 0 {
|
||||||
|
// #nosec // TODO this is dangerous, even if we're escaping the only place where we're passing a non-literal
|
||||||
warning[0].BodyHTML = template.HTML(warning[0].Body)
|
warning[0].BodyHTML = template.HTML(warning[0].Body)
|
||||||
return c.Render("admin", fiber.Map{
|
return c.Render("admin", fiber.Map{
|
||||||
"Defaults": config.getTemplateDefaults(),
|
"Defaults": config.getTemplateDefaults(),
|
||||||
|
@ -116,7 +118,7 @@ func (config *Config) saveAdminForm(c *fiber.Ctx) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
form := &Config{
|
form := &Config{
|
||||||
HomeAssistant: HomeAssistant{ /*BaseURL to be filled later*/ ApiKey: c.FormValue("api_key"), InstallationDate: dayStart(parsedTime)},
|
HomeAssistant: HomeAssistant{ /*BaseURL to be filled later*/ APIKey: c.FormValue("api_key"), InstallationDate: dayStart(parsedTime)},
|
||||||
Sensors: Sensors{PolledSmartEnergySummation: c.FormValue("polled_smart_energy_summation"), FossilPercentage: c.FormValue("fossil_percentage")},
|
Sensors: Sensors{PolledSmartEnergySummation: c.FormValue("polled_smart_energy_summation"), FossilPercentage: c.FormValue("fossil_percentage")},
|
||||||
Administrator: Administrator{Username: c.FormValue("username") /*PasswordHash to be filled later*/},
|
Administrator: Administrator{Username: c.FormValue("username") /*PasswordHash to be filled later*/},
|
||||||
Dashboard: Dashboard{Theme: c.FormValue("theme"), Name: c.FormValue("name"), HeaderLinks: config.Dashboard.HeaderLinks, FooterLinks: config.Dashboard.FooterLinks},
|
Dashboard: Dashboard{Theme: c.FormValue("theme"), Name: c.FormValue("name"), HeaderLinks: config.Dashboard.HeaderLinks, FooterLinks: config.Dashboard.FooterLinks},
|
||||||
|
@ -134,7 +136,7 @@ func (config *Config) saveAdminForm(c *fiber.Ctx) error {
|
||||||
}
|
}
|
||||||
form.HomeAssistant.BaseURL = fmtURL
|
form.HomeAssistant.BaseURL = fmtURL
|
||||||
|
|
||||||
if form.equals(config) {
|
if form.Equals(config) {
|
||||||
return errNoChanges
|
return errNoChanges
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,7 +151,7 @@ func (config *Config) saveAdminForm(c *fiber.Ctx) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return os.WriteFile("config.json", js, 0o666)
|
return os.WriteFile("config.json", js, 0o600)
|
||||||
}
|
}
|
||||||
|
|
||||||
func averageExcludingCurrentDay(data []float32) float32 {
|
func averageExcludingCurrentDay(data []float32) float32 {
|
||||||
|
@ -207,10 +209,12 @@ func templateDivide(num1, num2 float32) template.HTML {
|
||||||
|
|
||||||
powerOfTen := int(math.Floor(math.Log10(division)))
|
powerOfTen := int(math.Floor(math.Log10(division)))
|
||||||
if powerOfTen >= -2 && powerOfTen <= 2 {
|
if powerOfTen >= -2 && powerOfTen <= 2 {
|
||||||
|
// #nosec G203 // We're only printing floats
|
||||||
return template.HTML(strconv.FormatFloat(math.Round(division*100)/100, 'f', -1, 64))
|
return template.HTML(strconv.FormatFloat(math.Round(division*100)/100, 'f', -1, 64))
|
||||||
}
|
}
|
||||||
|
|
||||||
preComma := division / math.Pow10(powerOfTen)
|
preComma := division / math.Pow10(powerOfTen)
|
||||||
|
// #nosec G203 // We're only printing floats
|
||||||
return template.HTML(fmt.Sprintf("%s * 10<sup>%d</sup>", strconv.FormatFloat(math.Round(preComma*100)/100, 'f', -1, 64), powerOfTen))
|
return template.HTML(fmt.Sprintf("%s * 10<sup>%d</sup>", strconv.FormatFloat(math.Round(preComma*100)/100, 'f', -1, 64), powerOfTen))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,5 +222,6 @@ func templateHTMLDateFormat(date time.Time) template.HTML {
|
||||||
if date.IsZero() {
|
if date.IsZero() {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
// #nosec G203 // We're only printing a date
|
||||||
return template.HTML(date.Format("2006-01-02"))
|
return template.HTML(date.Format("2006-01-02"))
|
||||||
}
|
}
|
Loading…
Reference in a new issue