Messaging: send warnings and errors to ui (#6351)
This commit is contained in:
parent
d35170955e
commit
ff7ae36967
6 changed files with 71 additions and 34 deletions
|
|
@ -11,8 +11,10 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/evcc-io/evcc/cmd/shutdown"
|
||||
"github.com/evcc-io/evcc/push"
|
||||
"github.com/evcc-io/evcc/util"
|
||||
"github.com/spf13/viper"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
// parseLogLevels parses --log area:level[,...] switch into levels per log area
|
||||
|
|
@ -32,6 +34,33 @@ func parseLogLevels() {
|
|||
util.LogLevel(level, levels)
|
||||
}
|
||||
|
||||
// pushErrorEvents forwards error events as push messages
|
||||
func pushErrorEvents(events chan<- push.Event, valueChan <-chan util.Param) {
|
||||
cache := make(map[string]interface{})
|
||||
|
||||
for p := range valueChan {
|
||||
if !slices.Contains([]string{"warn", "error", "fatal"}, p.Key) {
|
||||
continue
|
||||
}
|
||||
|
||||
// deduplicate
|
||||
if v, ok := cache[p.Key]; ok && v == p.Val {
|
||||
continue
|
||||
}
|
||||
cache[p.Key] = p.Val
|
||||
|
||||
// simplify for users
|
||||
if p.Key == "fatal" {
|
||||
p.Key = "error"
|
||||
}
|
||||
|
||||
events <- push.Event{
|
||||
Event: p.Key,
|
||||
Loadpoint: p.Loadpoint,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// unwrap converts a wrapped error into slice of strings
|
||||
func unwrap(err error) (res []string) {
|
||||
for err != nil {
|
||||
|
|
|
|||
21
cmd/root.go
21
cmd/root.go
|
|
@ -135,7 +135,7 @@ func runRoot(cmd *cobra.Command, args []string) {
|
|||
|
||||
// value cache
|
||||
cache := util.NewCache()
|
||||
go cache.Run(pipe.NewDropper(ignoreErrors...).Pipe(tee.Attach()))
|
||||
go cache.Run(tee.Attach())
|
||||
|
||||
// create web server
|
||||
socketHub := server.NewSocketHub()
|
||||
|
|
@ -155,12 +155,21 @@ func runRoot(cmd *cobra.Command, args []string) {
|
|||
go socketHub.Run(tee.Attach(), cache)
|
||||
|
||||
// setup values channel
|
||||
valueChan := make(chan util.Param)
|
||||
valueChan := make(chan util.Param, 1)
|
||||
go tee.Run(valueChan)
|
||||
|
||||
// setup events channel
|
||||
eventChan := make(chan push.Event, 1)
|
||||
go pushErrorEvents(eventChan, tee.Attach())
|
||||
|
||||
// capture log messages for UI
|
||||
util.CaptureLogs(valueChan)
|
||||
|
||||
// setup messaging
|
||||
if err == nil {
|
||||
err = configureMessengers(conf.Messaging, eventChan, valueChan, cache)
|
||||
}
|
||||
|
||||
// setup environment
|
||||
if err == nil {
|
||||
err = configureEnvironment(cmd, conf)
|
||||
|
|
@ -211,12 +220,6 @@ func runRoot(cmd *cobra.Command, args []string) {
|
|||
err = configureHEMS(conf.HEMS, site, httpd)
|
||||
}
|
||||
|
||||
// setup messaging
|
||||
var pushChan chan push.Event
|
||||
if err == nil {
|
||||
pushChan, err = configureMessengers(conf.Messaging, valueChan, cache)
|
||||
}
|
||||
|
||||
// run shutdown functions on stop
|
||||
var once sync.Once
|
||||
stopC := make(chan struct{})
|
||||
|
|
@ -252,7 +255,7 @@ func runRoot(cmd *cobra.Command, args []string) {
|
|||
|
||||
// set channels
|
||||
site.DumpConfig()
|
||||
site.Prepare(valueChan, pushChan)
|
||||
site.Prepare(valueChan, eventChan)
|
||||
|
||||
// show and check version
|
||||
valueChan <- util.Param{Key: "version", Val: server.FormattedVersion()}
|
||||
|
|
|
|||
19
cmd/setup.go
19
cmd/setup.go
|
|
@ -203,25 +203,26 @@ func configureEEBus(conf map[string]interface{}) error {
|
|||
}
|
||||
|
||||
// setup messaging
|
||||
func configureMessengers(conf messagingConfig, valueChan chan util.Param, cache *util.Cache) (chan push.Event, error) {
|
||||
messageChan := make(chan push.Event, 1)
|
||||
func configureMessengers(conf messagingConfig, eventChan <-chan push.Event, valueChan chan util.Param, cache *util.Cache) error {
|
||||
pushHub, err := push.NewHub(conf.Events, cache)
|
||||
|
||||
if pushHub != nil {
|
||||
go pushHub.Run(eventChan, valueChan)
|
||||
}
|
||||
|
||||
messageHub, err := push.NewHub(conf.Events, cache)
|
||||
if err != nil {
|
||||
return messageChan, fmt.Errorf("failed configuring push services: %w", err)
|
||||
return fmt.Errorf("failed configuring push services: %w", err)
|
||||
}
|
||||
|
||||
for _, service := range conf.Services {
|
||||
impl, err := push.NewFromConfig(service.Type, service.Other)
|
||||
if err != nil {
|
||||
return messageChan, fmt.Errorf("failed configuring push service %s: %w", service.Type, err)
|
||||
return fmt.Errorf("failed configuring push service %s: %w", service.Type, err)
|
||||
}
|
||||
messageHub.Add(impl)
|
||||
pushHub.Add(impl)
|
||||
}
|
||||
|
||||
go messageHub.Run(messageChan, valueChan)
|
||||
|
||||
return messageChan, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func configureTariffs(conf tariffConfig) (tariff.Tariffs, error) {
|
||||
|
|
|
|||
|
|
@ -231,6 +231,10 @@ messaging:
|
|||
guest: # vehicle could not be identified
|
||||
title: Unknown vehicle
|
||||
msg: Unknown vehicle, guest connected?
|
||||
error: # errors (deduplicated)
|
||||
title: "Error: {{ .error }}"
|
||||
warn: # warnings (deduplicated)
|
||||
title: "Warning: {{ .warn }}"
|
||||
services:
|
||||
# - type: pushover
|
||||
# app: # app id
|
||||
|
|
|
|||
26
push/hub.go
26
push/hub.go
|
|
@ -27,23 +27,23 @@ type Hub struct {
|
|||
cache *util.Cache
|
||||
}
|
||||
|
||||
// NewHub creates push hub with definitions and receiver
|
||||
// NewHub creates push hub with definitions and receiver. The hub is always created, even on error.
|
||||
func NewHub(cc map[string]EventTemplateConfig, cache *util.Cache) (*Hub, error) {
|
||||
// instantiate all event templates
|
||||
for k, v := range cc {
|
||||
if _, err := template.New("out").Funcs(template.FuncMap(sprig.FuncMap())).Parse(v.Title); err != nil {
|
||||
return nil, fmt.Errorf("invalid event title: %s (%w)", k, err)
|
||||
}
|
||||
if _, err := template.New("out").Funcs(template.FuncMap(sprig.FuncMap())).Parse(v.Msg); err != nil {
|
||||
return nil, fmt.Errorf("invalid event message: %s (%w)", k, err)
|
||||
}
|
||||
}
|
||||
|
||||
h := &Hub{
|
||||
definitions: cc,
|
||||
cache: cache,
|
||||
}
|
||||
|
||||
// instantiate all event templates
|
||||
for k, v := range cc {
|
||||
if _, err := template.New("out").Funcs(template.FuncMap(sprig.FuncMap())).Parse(v.Title); err != nil {
|
||||
return h, fmt.Errorf("invalid event title: %s (%w)", k, err)
|
||||
}
|
||||
if _, err := template.New("out").Funcs(template.FuncMap(sprig.FuncMap())).Parse(v.Msg); err != nil {
|
||||
return h, fmt.Errorf("invalid event message: %s (%w)", k, err)
|
||||
}
|
||||
}
|
||||
|
||||
return h, nil
|
||||
}
|
||||
|
||||
|
|
@ -72,7 +72,7 @@ func (h *Hub) apply(ev Event, tmpl string) (string, error) {
|
|||
}
|
||||
|
||||
// Run is the Hub's main publishing loop
|
||||
func (h *Hub) Run(events <-chan Event, valueChan chan util.Param) {
|
||||
func (h *Hub) Run(events <-chan Event, valueChan chan<- util.Param) {
|
||||
log := util.NewLogger("push")
|
||||
|
||||
for ev := range events {
|
||||
|
|
@ -103,7 +103,7 @@ func (h *Hub) Run(events <-chan Event, valueChan chan util.Param) {
|
|||
}
|
||||
|
||||
for _, sender := range h.sender {
|
||||
if strings.TrimSpace(msg) != "" {
|
||||
if strings.TrimSpace(title)+strings.TrimSpace(msg) != "" {
|
||||
go sender.Send(title, msg)
|
||||
} else {
|
||||
log.DEBUG.Printf("did not send empty message template for %s: %v", ev.Event, err)
|
||||
|
|
|
|||
|
|
@ -146,12 +146,12 @@ func (w *uiWriter) Write(p []byte) (n int, err error) {
|
|||
|
||||
// CaptureLogs appends uiWriter to relevant log levels for
|
||||
// loggers created before uiChan is initialized
|
||||
func CaptureLogs(c chan<- Param) {
|
||||
func CaptureLogs(valueChan chan<- Param) {
|
||||
if uiChan != nil {
|
||||
return
|
||||
panic("log capturing already initialized")
|
||||
}
|
||||
|
||||
uiChan = c
|
||||
uiChan = valueChan
|
||||
|
||||
for _, l := range loggers {
|
||||
captureLogger(l)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue