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

Push: add custom messenger (BC) #17211

Merged
merged 6 commits into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
80 changes: 80 additions & 0 deletions push/push.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package push

import (
"context"
"encoding/json"
"strings"

"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/provider"
"github.com/evcc-io/evcc/util"
)

func init() {
registry.AddCtx(api.Custom, NewConfigurableFromConfig)
}

// NewConfigurableFromConfig creates Messenger from config
func NewConfigurableFromConfig(ctx context.Context, other map[string]interface{}) (Messenger, error) {
var cc struct {
Send provider.Config
Encoding string
}

if err := util.DecodeOther(other, &cc); err != nil {
return nil, err
}

send, err := provider.NewStringSetterFromConfig(ctx, "send", cc.Send)
if err != nil {
return nil, err
}

return NewConfigurable(send, cc.Encoding)
}

// NewConfigurable creates a new meter
andig marked this conversation as resolved.
Show resolved Hide resolved
func NewConfigurable(send func(string) error, encoding string) (*Push, error) {
m := &Push{
log: util.NewLogger("push"),
send: send,
encoding: strings.ToLower(encoding),
}
return m, nil
}

// Push is a configurable Messenger implementation
type Push struct {
log *util.Logger
send func(string) error
encoding string
}

// Send implements the Messenger interface
func (m *Push) Send(title, msg string) {
var res string

switch m.encoding {
case "json":
b, _ := json.Marshal(struct {
Title string `json:"title"`
andig marked this conversation as resolved.
Show resolved Hide resolved
Msg string `json:"msg"`
andig marked this conversation as resolved.
Show resolved Hide resolved
}{
Title: title,
Msg: msg,
})
res = string(b)
case "csv":
res = title + "," + msg
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is too simplistic, especially when title and message can contain commas (to tabs for tsv). Do we really need csv and tsv ? I can't see any good use case that can not be solved with a stronger typed representation (like json).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it hurt? Since users do control the message format this can be handled by crafting the right kind of message. Happy to improve this by doing a better encoding.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nvm. We can just use the cvswriter for this as well.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it hurt? Since users do control the message format this can be handled by crafting the right kind of message. Happy to improve this by doing a better encoding.

Well, I've been in the open-source software business for too long, I guess. I've learned over the long run to respect YAGNI and better leave out things than anticipate anything the people might use at some point in time. It's easy to add these features later anyway if needed, but if nobody uses them, you will carry that baggage with you forever. Ideally, you add some tests for it (especially for corner cases and strange escape rules), and you need some documentation. I became a fanboy of less is more most of the time.

Or let me ask the other way around: Which use case do you think needs CSV, TSV, or title—only for sending out to an endpoint? Couldn't you just do it all yourself when defining the msg, even with the proper templating support?

In the end, it's your call anyway, as I'm like many other contributors: Here for some time to help, but likely not forever. Just sharing some stuff.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or let me ask the other way around: Which use case do you think needs CSV, TSV, or title—only for sending out to an endpoint?

Anybody using the script messenger right now.

Copy link
Contributor

@rhuss rhuss Nov 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so.

The script provider does not implement SetStringProvider (so it wouldn't work anyway as is). I would keep it simplistic and ignore the title.

Empty titles will lead to strange CSV and TSV representations (like a leading , or leading tab), too. Still, we should allow for empty titles for this kind of integration, as this would be the most natural way to define no title and a JSON msg when using a custom provider (you can always add a title: in that msg that the user defines if you like)

Copy link
Contributor

@rhuss rhuss Nov 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using tsv with

cmdline: script ${send}

Getting title and message as two separate arguments will not work when $msg (or $title) contains spaces.

So I think that most of the time, one would use cmdline: 'script "${send}"' to get the whole input as a single argument to the script. I would always leave out the title for custom providers.

case "tsv":
res = title + "\t" + msg
case "title":
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this type does not "fit" into the list: json, csv, tsv are formats (so kind of an encoding), "title" and "message" (the default) are more filters.

Wdyt to restrict encoding to "json" and "string" (or "raw") and have some second options as to whether to include the title or not ? (usually if you only use a single service endpoint and you don't need the title, you just don't define it in events: ...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this type does not "fit" into the list: json, csv, tsv are formats (so kind of an encoding), "title" and "message" (the default) are more filters.

It doesn't, but it allows returning the title instead of the message for simple command line scripts. It doesn't hurt anyway.

Copy link
Contributor

@rhuss rhuss Nov 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don't buy the argument it doesn't hurt anyway, but your mileage may vary :-)

res = title
default:
res = msg
}

if err := m.send(res); err != nil {
m.log.ERROR.Printf("send: %v", err)
}
}
85 changes: 0 additions & 85 deletions push/script.go

This file was deleted.