80 lines
2.5 KiB
Go
80 lines
2.5 KiB
Go
package plugin
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/evcc-io/evcc/api"
|
|
"github.com/evcc-io/evcc/util"
|
|
)
|
|
|
|
// setFormattedValue formats a message template or returns the value formatted as %v if the message template is empty
|
|
func setFormattedValue(message, param string, v any) (string, error) {
|
|
if message == "" {
|
|
return fmt.Sprintf("%v", v), nil
|
|
}
|
|
|
|
return util.ReplaceFormatted(message, map[string]any{
|
|
param: v,
|
|
})
|
|
}
|
|
|
|
// knownErrors maps string responses to known error codes
|
|
func knownErrors(b []byte) error {
|
|
switch string(b) {
|
|
case "ErrAsleep":
|
|
return api.ErrAsleep
|
|
case "ErrMustRetry":
|
|
return api.ErrMustRetry
|
|
case "ErrNotAvailable":
|
|
return api.ErrNotAvailable
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// parseFloat rejects NaN and Inf values
|
|
func parseFloat(payload string) (float64, error) {
|
|
f, err := strconv.ParseFloat(payload, 64)
|
|
if err == nil && (math.IsNaN(f) || math.IsInf(f, 0)) {
|
|
return 0, fmt.Errorf("invalid float value: %s", payload)
|
|
}
|
|
return f, err
|
|
}
|
|
|
|
// unixThreshold is the Unix timestamp for 2026-01-01 00:00:00 UTC, used to distinguish
|
|
// Unix timestamps from duration-in-seconds when parsing numeric finish times.
|
|
const unixThreshold = 1767225600
|
|
|
|
// parseRelativeTime parses a string into an absolute time.Time.
|
|
// Supported formats:
|
|
// - RFC3339 timestamp (e.g. "2026-05-03T14:00:00Z") → interpreted as absolute time
|
|
// - Go duration string (e.g. "1h30m") → interpreted as remaining duration, added to time.Now()
|
|
// - Numeric string ≥ 1767225600 (e.g. "1767225600") → interpreted as Unix timestamp (absolute time)
|
|
// - Numeric string < 1767225600 (e.g. "5400") → interpreted as remaining seconds, added to time.Now()
|
|
//
|
|
// For relative formats, time.Now() is evaluated at the time of each call, providing a
|
|
// fresh estimate. This matches the behavior of hardcoded charger/vehicle implementations.
|
|
func parseRelativeTime(s string) (time.Time, error) {
|
|
// Try RFC3339 timestamp first (absolute time)
|
|
if t, err := time.Parse(time.RFC3339, s); err == nil {
|
|
return t, nil
|
|
}
|
|
|
|
// Try Go duration string (relative)
|
|
if d, err := time.ParseDuration(s); err == nil {
|
|
return time.Now().Add(d), nil
|
|
}
|
|
|
|
// Try numeric: Unix timestamp (absolute) or duration in seconds (relative)
|
|
if f, err := strconv.ParseFloat(s, 64); err == nil {
|
|
if f >= unixThreshold {
|
|
return time.Unix(int64(f), 0), nil
|
|
}
|
|
return time.Now().Add(time.Duration(f * float64(time.Second))), nil
|
|
}
|
|
|
|
return time.Time{}, fmt.Errorf("invalid finish time: %s", s)
|
|
}
|