diff --git a/README.md b/README.md index 6445f2e..0425788 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,7 @@ As soon as you navigate to the container's exposed port, you will see the admin - **HomeAssistant's base URL**: the base URL which you use to access HomeAssistant on your server. It should be something like `http://INTERNAL_IP_ADDRESS:8123/` or `https://homeassistant.youdomain.com/`. - **HomeAssistant's API Key:** Get it by going into your HomeAssistant profile settings (at `http://HOMEASSISTANT-BASE-URL/profile`) -> Create Long Lived Access Token (at the very bottom of the page) -> Insert a name -> Copy the string it gives you +- **Installation Date**: Select the date of the first day in which your server's consumption was logged in its entirety. Users won't be able to see consumption data before this date. - **Polled Smart Energy Summation entity ID:** After your plug is added in HomeAssistant, get it in Overview -> look for an entity called like "[Name of your plug] Polledsmartenergysummation" -> Settings -> Copy the Entity ID. Check that the unit of measurement in the "Info" tab is kWh. - **CO2 signal Grid fossil fuel percentage entity ID**: Get it in Settings -> Devices and Integrations -> Add Integration -> CO2 Signal -> Get your token from the website -> CO2 signal Grid fossil fuel percentage -> Settings -> Copy the Entity ID. Check that the unit of measurement in the "Info" tab is %. - **Admin username and password** don't need to be the credentials to HomeAssistant! They are the credentials to log into the admin panel. diff --git a/api.go b/api.go index d2ae11c..877a4e8 100644 --- a/api.go +++ b/api.go @@ -106,7 +106,7 @@ func (config Config) historyAverageAndConvertToGreen(entityID string, startTime, days[key] = day } - days = fillMissing(days) + days = fillMissing(days, startTime, endTime) return days, nil @@ -157,13 +157,13 @@ func (config Config) historyDelta(entityID string, startTime, endTime time.Time) days[key] = day } - days = fillMissing(days) + days = fillMissing(days, startTime, endTime) return days, nil } -func fillMissing(days []DayData) []DayData { +func fillMissing(days []DayData, startTime, endTime time.Time) []DayData { var ( previousDay time.Time @@ -173,6 +173,8 @@ func fillMissing(days []DayData) []DayData { currentTime = time.Now() ) + expectedDaysDiff := int(math.Trunc(endTime.Sub(startTime).Hours()/24) + 1) + for key, day := range days { if key != 0 { @@ -214,8 +216,8 @@ func fillMissing(days []DayData) []DayData { } } - if len(ret) < 8 { - shouldAdd := 8 - len(ret) + if len(ret) < expectedDaysDiff { + shouldAdd := expectedDaysDiff - len(ret) startDay := currentTime.Add(-time.Duration(24*len(ret)) * time.Hour) for i := 0; i < shouldAdd; i++ { fakeTime := startDay.Add(-time.Duration(24*i) * time.Hour) @@ -229,7 +231,7 @@ func fillMissing(days []DayData) []DayData { } } - if len(ret) != 8 { + if len(ret) != expectedDaysDiff { // oh shit log.Panicln("You've found a logic bug! Open a bug report ASAP.") } diff --git a/cache.go b/cache.go index b6c899d..82cb705 100644 --- a/cache.go +++ b/cache.go @@ -16,6 +16,11 @@ type CacheFile []CacheEntry func (config Config) updateCache() { + // in order to avoid querying and storing each day's data from 0001-01-01 in future versions + if config.HomeAssistant.InstallationDate.IsZero() { + return + } + now := time.Now() h, m, s := now.Clock() start := now.AddDate(0, 0, -7).Add(-(time.Duration(h)*time.Hour + time.Duration(m)*time.Minute + time.Duration(s)*time.Second)) diff --git a/config.go b/config.go index 186e7ff..94d439e 100644 --- a/config.go +++ b/config.go @@ -9,6 +9,7 @@ import ( "os" "regexp" "strings" + "time" ) type Config struct { @@ -19,8 +20,9 @@ type Config struct { } type HomeAssistant struct { - BaseURL string `json:"base_url"` - ApiKey string `json:"api_key"` + BaseURL string `json:"base_url"` + ApiKey string `json:"api_key"` + InstallationDate time.Time `json:"installation_date"` } type Sensors struct { PolledSmartEnergySummation string `json:"polled_smart_energy_summation"` diff --git a/http.go b/http.go index 2966753..b7bdbd2 100644 --- a/http.go +++ b/http.go @@ -27,7 +27,7 @@ type Warning struct { IsSuccess bool } -func (config Config) getTemplateDefaults() map[string]interface{} { +func (config Config) getTemplateDefaults() fiber.Map { return fiber.Map{ "DashboardName": config.Dashboard.Name, "HeaderLinks": config.Dashboard.HeaderLinks, @@ -35,6 +35,12 @@ func (config Config) getTemplateDefaults() map[string]interface{} { } } +func (config Config) templateDefaultsMap() fiber.Map { + return fiber.Map{ + "Default": config.getTemplateDefaults(), + } +} + func (config Config) adminEndpoint(c *fiber.Ctx) error { if c.Method() == "POST" { @@ -68,7 +74,7 @@ func (config Config) adminEndpoint(c *fiber.Ctx) error { if config.isAuthorized(c) { return config.renderAdminPanel(c) } - return c.Render("login", fiber.Map{"Defaults": config.getTemplateDefaults()}, "base") + return c.Render("login", config.templateDefaultsMap(), "base") } @@ -99,15 +105,20 @@ func (config Config) renderAdminPanel(c *fiber.Ctx, warning ...Warning) error { func (config Config) saveAdminForm(c *fiber.Ctx) error { - requiredFields := []string{"base_url", "api_key", "polled_smart_energy_summation", "fossil_percentage", "username", "theme", "name"} + requiredFields := []string{"base_url", "api_key", "polled_smart_energy_summation", "fossil_percentage", "username", "theme", "name", "installation_date"} for _, requiredField := range requiredFields { if c.FormValue(requiredField) == "" { return errors.New("Required field is missing: " + requiredField) } } + parsedTime, err := time.Parse("2006-01-02", c.FormValue("installation_date")) + if err != nil { + return err + } + form := Config{ - HomeAssistant: HomeAssistant{ /*BaseURL to be filled later*/ ApiKey: c.FormValue("api_key")}, + HomeAssistant: HomeAssistant{ /*BaseURL to be filled later*/ ApiKey: c.FormValue("api_key"), InstallationDate: parsedTime}, 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*/}, Dashboard: Dashboard{Theme: c.FormValue("theme"), Name: c.FormValue("name"), HeaderLinks: config.Dashboard.HeaderLinks, FooterLinks: config.Dashboard.FooterLinks}, @@ -148,7 +159,7 @@ func (config Config) saveAdminForm(c *fiber.Ctx) error { } -func sevenDaysAverageExcludingCurrentDay(data []float32) float32 { +func averageExcludingCurrentDay(data []float32) float32 { if len(data) == 0 { return 0 } @@ -163,6 +174,13 @@ func sevenDaysAverageExcludingCurrentDay(data []float32) float32 { func (config Config) renderIndex(c *fiber.Ctx) error { + if config.HomeAssistant.InstallationDate.IsZero() { + return c.Render("config-error", fiber.Map{ + "Defaults": config.getTemplateDefaults(), + "Error": "The installation date is not set! This is normal if you've just updated from v0.1 to v0.2.", + }, "base") + } + data, err := readCache() if err != nil { return err @@ -182,14 +200,14 @@ func (config Config) renderIndex(c *fiber.Ctx) error { energyConsumptions = append(energyConsumptions, datum.PolledSmartEnergySummation) } - perDayUsage := sevenDaysAverageExcludingCurrentDay(energyConsumptions) + perDayUsage := averageExcludingCurrentDay(energyConsumptions) return c.Render("index", fiber.Map{ "Defaults": config.getTemplateDefaults(), "Labels": labels, "GreenEnergyPercents": greenEnergyConsumptionAbsolute, "EnergyConsumptions": energyConsumptions, - "GreenEnergyPercent": sevenDaysAverageExcludingCurrentDay(greenEnergyPercents), + "GreenEnergyPercent": averageExcludingCurrentDay(greenEnergyPercents), "PerDayUsage": perDayUsage, }, "base") @@ -208,3 +226,10 @@ func templateDivide(num1, num2 float32) template.HTML { return template.HTML(fmt.Sprintf("%s * 10%d", strconv.FormatFloat(math.Round(preComma*100)/100, 'f', -1, 64), powerOfTen)) } + +func templateHTMLDateFormat(date time.Time) template.HTML { + if date.IsZero() { + return "" + } + return template.HTML(date.Format("2006-01-02")) +} diff --git a/main.go b/main.go index 381ea3d..7be94bf 100644 --- a/main.go +++ b/main.go @@ -28,6 +28,7 @@ func main() { engine := html.New("./templates/"+config.Dashboard.Theme, ".html") engine.AddFunc("divide", templateDivide) + engine.AddFunc("HTMLDateFormat", templateHTMLDateFormat) app := fiber.New(fiber.Config{ Views: engine, @@ -45,9 +46,7 @@ func main() { }) app.Get("/accuracy-notice", func(c *fiber.Ctx) error { - return c.Render("accuracy-notice", fiber.Map{ - "Defaults": config.getTemplateDefaults(), - }, "base") + return c.Render("accuracy-notice", config.templateDefaultsMap(), "base") }) app.All("/admin", config.adminEndpoint) @@ -58,9 +57,7 @@ func main() { time.Sleep(time.Second) os.Exit(1) }() - return c.Render("restart", fiber.Map{ - "Defaults": config.getTemplateDefaults(), - }, "base") + return c.Render("restart", config.templateDefaultsMap(), "base") } return c.Redirect("./", 307) }) diff --git a/templates/default/admin.html b/templates/default/admin.html index e655834..e1bfa1e 100644 --- a/templates/default/admin.html +++ b/templates/default/admin.html @@ -17,6 +17,7 @@

HomeAssistant

+ Installation date

Sensors

diff --git a/templates/default/config-error.html b/templates/default/config-error.html new file mode 100644 index 0000000..5948454 --- /dev/null +++ b/templates/default/config-error.html @@ -0,0 +1,7 @@ +

Configuration error

+ +

We've detected an issue with your configuration that prevents EcoDash from working. Please check it below, and open an issue if this problem persists.

+ +
{{.Error}}
+ +Admin panel \ No newline at end of file