chore: remove unused code (#31088)

This commit is contained in:
andig 2026-06-21 13:10:12 +02:00 • committed by GitHub
parent 6cd76d78bc
commit be75289a3b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 0 additions and 629 deletions

View file

@ -170,9 +170,6 @@ func ForwarderRules() []ForwarderRule {
return forwarderRules
}
// StartForwarder is a no-op; hooks fire on every charger connection.
func StartForwarder() {}
// actionsRelayedToUpstream lists actions for which upstream is the authoritative
// Central System: evcc's handler is bypassed and upstream's reply relayed back.
var actionsRelayedToUpstream = map[string]bool{

View file

@ -1,51 +0,0 @@
package oauth
import (
"time"
"github.com/evcc-io/evcc/util"
"golang.org/x/oauth2"
)
// Refresh refreshes the token every 5m. If token refresh fails 5 times, it is aborted.
func Refresh(log *util.Logger, token *oauth2.Token, ts oauth2.TokenSource, optMaxTokenLifetime ...time.Duration) {
var failed int
// limit lifetime of initial token
limitTokenLife(token, optMaxTokenLifetime...)
for range time.Tick(5 * time.Minute) {
// get token- either previous or new
t, err := ts.Token()
if err != nil {
// error means refresh failed
failed++
if failed > 5 {
log.ERROR.Printf("token refresh: %v, giving up", err)
return
}
log.ERROR.Printf("token refresh: %v", err)
continue
}
failed = 0
// limit lifetime of new tokens
if t.Expiry != token.Expiry {
token = t
limitTokenLife(token, optMaxTokenLifetime...)
}
}
}
func limitTokenLife(token *oauth2.Token, optMaxTokenLifetime ...time.Duration) {
if len(optMaxTokenLifetime) != 1 {
return
}
maxTokenLifetime := optMaxTokenLifetime[0]
if time.Until(token.Expiry) > maxTokenLifetime {
token.Expiry = time.Now().Add(maxTokenLifetime)
}
}

View file

@ -16,15 +16,6 @@ func Copy(q url.Values) url.Values {
return res
}
// Merge copies multiple from url values into to
func Merge(to url.Values, from ...url.Values) {
for _, vv := range from {
for k, v := range vv {
to[k] = append(to[k], v...)
}
}
}
// Require verifies that url contains the required non-nil values
func Require(q url.Values, keys ...string) error {
for _, k := range keys {

View file

@ -1,132 +0,0 @@
package mb
import (
"context"
"fmt"
"net/http"
"net/http/cookiejar"
"net/url"
"strings"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/request"
"github.com/samber/lo"
"golang.org/x/net/publicsuffix"
"golang.org/x/oauth2"
)
// https://github.com/TA2k/ioBroker.smart-eq
// https://id.mercedes-benz.com/.well-known/openid-configuration
const OAuthURI = "https://id.mercedes-benz.com"
type Identity struct {
*request.Helper
oc *oauth2.Config
oauth2.TokenSource
}
// NewIdentity creates Mercedes Benz identity
func NewIdentity(log *util.Logger, oc *oauth2.Config) *Identity {
return &Identity{
Helper: request.NewHelper(log),
oc: oc,
}
}
func (v *Identity) Login(user, password string) error {
if v.Client.Jar == nil {
v.Client.Jar, _ = cookiejar.New(&cookiejar.Options{
PublicSuffixList: publicsuffix.List,
})
}
var param request.InterceptResult
v.Client.CheckRedirect, param = request.InterceptRedirect("resume", false)
cv := oauth2.GenerateVerifier()
state := lo.RandomString(16, lo.AlphanumericCharset)
uri := v.oc.AuthCodeURL(state, oauth2.AccessTypeOffline, oauth2.S256ChallengeOption(cv))
if _, err := v.Get(uri); err != nil {
return err
}
resume, err := param()
if err != nil {
return err
}
v.Client.CheckRedirect = nil
data := struct {
Username string `json:"username"`
Password string `json:"password,omitempty"`
RememberMe bool `json:"rememberMe,omitempty"`
}{
Username: user,
}
var res struct {
Result, Token string
Errors []struct{ Key string }
}
uri = fmt.Sprintf("%s/ciam/auth/login/user", OAuthURI)
req, err := request.New(http.MethodPost, uri, request.MarshalJSON(data), request.JSONEncoding)
if err == nil {
if err = v.DoJSON(req, &res); err != nil && len(res.Errors) > 0 {
err = fmt.Errorf("%s: %w", res.Errors[0].Key, err)
}
}
if err == nil {
data.Password = password
data.RememberMe = true
uri = fmt.Sprintf("%s/ciam/auth/login/pass", OAuthURI)
req, err = request.New(http.MethodPost, uri, request.MarshalJSON(data), request.JSONEncoding)
if err == nil {
if err = v.DoJSON(req, &res); err != nil && len(res.Errors) > 0 {
err = fmt.Errorf("%s: %w", res.Errors[0].Key, err)
}
}
}
if err == nil && res.Token == "" && res.Result != "" {
err = fmt.Errorf("missing token: %s", res.Result)
}
var code string
if err == nil {
params := url.Values{
"token": {res.Token},
}
var param request.InterceptResult
v.Client.CheckRedirect, param = request.InterceptRedirect("code", true)
uri := OAuthURI + resume
if _, err = v.Post(uri, request.FormContent, strings.NewReader(params.Encode())); err == nil {
code, err = param()
}
v.Client.CheckRedirect = nil
}
var token *oauth2.Token
if err == nil {
ctx, cancel := context.WithTimeout(
context.WithValue(context.Background(), oauth2.HTTPClient, v.Client),
request.Timeout)
defer cancel()
token, err = v.oc.Exchange(ctx, code, oauth2.VerifierOption(cv))
}
if err == nil {
v.TokenSource = v.oc.TokenSource(context.Background(), token)
}
return err
}

View file

@ -1,8 +1,6 @@
package mercedes
import (
"fmt"
"html"
"time"
)
@ -12,19 +10,6 @@ var Regions = map[string]string{
"noram": "North-America",
}
type ErrorInfo struct {
ErrorCode int
ErrorMessage string
ErrorDetails string
}
func (e ErrorInfo) Error() error {
if e.ErrorCode == 0 {
return nil
}
return fmt.Errorf("%s: %s", e.ErrorMessage, html.UnescapeString(e.ErrorDetails))
}
type PinRequest struct {
EmailOrPhoneNumber string `json:"emailOrPhoneNumber"`
CountryCode string `json:"countryCode"`

View file

@ -1,102 +0,0 @@
package smart
import (
"fmt"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/request"
"github.com/evcc-io/evcc/util/transport"
"github.com/evcc-io/evcc/vehicle/mb"
"github.com/samber/lo"
"golang.org/x/oauth2"
)
// https://github.com/TA2k/ioBroker.smart-eq
const ApiURI = "https://oneapp.microservice.smart.mercedes-benz.com/seqc/v0"
var OAuth2Config = &oauth2.Config{
ClientID: "70d89501-938c-4bec-82d0-6abb550b0825",
RedirectURL: "https://oneapp.microservice.smart.mercedes-benz.com",
Endpoint: oauth2.Endpoint{
AuthURL: mb.OAuthURI + "/as/authorization.oauth2",
TokenURL: mb.OAuthURI + "/as/token.oauth2",
},
Scopes: []string{"openid", "profile", "email", "phone", "ciam-uid", "offline_access"},
}
type API struct {
*request.Helper
}
func NewAPI(log *util.Logger, identity oauth2.TokenSource) *API {
v := &API{
Helper: request.NewHelper(log),
}
// replace client transport with authenticated transport
v.Client.Transport = &transport.Decorator{
Base: &oauth2.Transport{
Source: identity,
Base: v.Client.Transport,
},
Decorator: transport.DecorateHeaders(map[string]string{
"accept": "*/*",
"accept-language": "de-DE;q=1.0",
"guid": "280C6B55-F179-4428-88B6-E0CCF5C22A7C",
"x-applicationname": OAuth2Config.ClientID,
}),
}
return v
}
func (v *API) Vehicles() ([]string, error) {
type vehicle struct {
FIN string
}
var res struct {
Authorizations, LicensePlates []vehicle
Error string
ErrorDescription string `json:"error_description"`
}
uri := fmt.Sprintf("%s/users/current", ApiURI)
err := v.GetJSON(uri, &res)
if err != nil && res.Error != "" {
err = fmt.Errorf("%s (%s): %w", res.Error, res.ErrorDescription, err)
}
vehicles := lo.Map(res.LicensePlates, func(v vehicle, _ int) string {
return v.FIN
})
return vehicles, err
}
func (v *API) Status(vin string) (StatusResponse, error) {
var res StatusResponse
uri := fmt.Sprintf("%s/vehicles/%s/init-data?requestedData=BOTH&countryCode=DE&locale=de-DE", ApiURI, vin)
err := v.GetJSON(uri, &res)
if err != nil && res.Error != "" {
err = fmt.Errorf("%s (%s): %w", res.Error, res.ErrorDescription, err)
}
return res, err
}
func (v *API) Refresh(vin string) (StatusResponse, error) {
var res StatusResponse
uri := fmt.Sprintf("%s/vehicles/%s/refresh-data", ApiURI, vin)
err := v.GetJSON(uri, &res)
if err != nil && res.Error != "" {
err = fmt.Errorf("%s (%s): %w", res.Error, res.ErrorDescription, err)
}
return res, err
}

View file

@ -1,99 +0,0 @@
package smart
import (
"fmt"
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/util"
)
// https://github.com/TA2k/ioBroker.smart-eq
type Provider struct {
statusG func() (StatusResponse, error)
expiry time.Duration
}
func NewProvider(log *util.Logger, api *API, vin string, expiry, cache time.Duration) *Provider {
v := &Provider{
expiry: expiry,
}
v.statusG = util.Cached(func() (StatusResponse, error) {
return v.status(
func() (StatusResponse, error) { return api.Status(vin) },
func() (StatusResponse, error) { return api.Refresh(vin) },
)
}, cache)
return v
}
func (v *Provider) status(statusG, refreshG func() (StatusResponse, error)) (StatusResponse, error) {
res, err := statusG()
// if ts := res.Status.Data.Soc.Ts.Time; err == nil && ts.Add(v.expiry).Before(time.Now()) {
// fmt.Println("--------------------------", ts)
// res, err = refreshG()
// ts := res.Status.Data.Soc.Ts.Time
// fmt.Println("--------------------------", ts)
// }
return res, err
}
// Soc implements the api.Vehicle interface
func (v *Provider) Soc() (float64, error) {
res, err := v.statusG()
return res.Status.Data.Soc.Value, err
}
var _ api.ChargeState = (*Provider)(nil)
// Range implements the api.VehicleRange interface
func (v *Provider) Status() (api.ChargeStatus, error) {
res, err := v.statusG()
if err != nil {
return api.StatusNone, err
}
cs := res.PreCond.Data.ChargingStatus
if cs.Status != 0 {
return api.StatusNone, fmt.Errorf("unknown status/value: %d/%d", cs.Status, cs.Value)
}
// confirmed status/value/active combinations (https://github.com/evcc-io/evcc/discussions/5596#discussioncomment-4556035)
// 0/0/active: charging
// 0/2/*: connected
// 0/3/*: disconnected
switch cs.Value {
case 0:
if res.PreCond.Data.ChargingActive.Value {
return api.StatusC, nil
}
return api.StatusB, nil
case 1, 2:
return api.StatusB, nil
case 3:
return api.StatusA, nil
default:
return api.StatusNone, fmt.Errorf("unknown status/value: %d/%d", cs.Status, cs.Value)
}
}
var _ api.VehicleRange = (*Provider)(nil)
// Range implements the api.VehicleRange interface
func (v *Provider) Range() (int64, error) {
res, err := v.statusG()
return int64(res.Status.Data.RangeElectric.Value), err
}
var _ api.VehicleOdometer = (*Provider)(nil)
// Odometer implements the api.VehicleOdometer interface
func (v *Provider) Odometer() (float64, error) {
res, err := v.statusG()
return res.Status.Data.Odo.Value, err
}

View file

@ -1,61 +0,0 @@
package smart
import (
"strconv"
"time"
)
type StatusResponse struct {
PreCond struct {
Data struct {
ChargingPower FloatValue
ChargingActive BoolValue
ChargingStatus IntValue
} `json:"data"`
}
Status struct {
Data struct {
Odo FloatValue
RangeElectric FloatValue
Soc FloatValue
} `json:"data"`
}
Images []string
Error string
ErrorDescription string `json:"error_description"`
}
type BoolValue struct {
Status int
Value bool
Ts TimeSecs
}
type IntValue struct {
Status int
Value int
Ts TimeSecs
}
type FloatValue struct {
Status int
Value float64
Ts TimeSecs
}
// TimeSecs implements JSON unmarshal for Unix timestamps in seconds
type TimeSecs struct {
time.Time
}
// UnmarshalJSON decodes unix timestamps in ms into time.Time
func (ct *TimeSecs) UnmarshalJSON(data []byte) error {
i, err := strconv.ParseInt(string(data), 10, 64)
if err == nil {
t := time.Unix(i, 0)
ct.Time = t
}
return err
}

View file

@ -70,10 +70,6 @@ func NewAPI(log *util.Logger, identity *Identity) *API {
return v
}
func (v *API) IDToken() string {
return v.identity.IDToken()
}
func (v *API) Vehicles() ([]string, error) {
uri := fmt.Sprintf("%s/%s", ApiBaseUrl, VehicleGuidPath)

View file

@ -67,62 +67,3 @@ func (ts *tokenSource) TokenEx() (*Token, error) {
func (ts *tokenSource) mergeToken(t *Token) error {
return mergo.Merge(ts.token, t, mergo.WithOverride)
}
type metaTokenSource struct {
mu sync.Mutex
ts TokenSource
newT func() (*Token, error)
newTS func(*Token) TokenSource
}
// MetaTokenSource creates a token source that is created using the
// `newTS` function or recreated once it fails to return tokens.
// The recreation uses a new bootstrap token provided by the `newT` function.
func MetaTokenSource(newT func() (*Token, error), newTS func(*Token) TokenSource) *metaTokenSource {
return &metaTokenSource{
newT: newT,
newTS: newTS,
}
}
// Token returns an oauth2 token or an error
func (ts *metaTokenSource) Token() (*oauth2.Token, error) {
token, err := ts.TokenEx()
if err != nil {
return nil, err
}
return &token.Token, err
}
// Token returns a vag token or an error
func (ts *metaTokenSource) TokenEx() (*Token, error) {
ts.mu.Lock()
defer ts.mu.Unlock()
// use token source
if ts.ts != nil {
token, err := ts.ts.TokenEx()
if err == nil {
return token, nil
}
}
// create new start token
token, err := ts.newT()
if err != nil {
return nil, err
}
// create token source
ts.ts = ts.newTS(token)
// use token source
token, err = ts.ts.TokenEx()
if err != nil {
// token source doesn't work anymore, reset it
ts.ts = nil
}
return token, err
}

View file

@ -1,89 +0,0 @@
package vwidentity
import (
"context"
"fmt"
"net/http"
"net/url"
"strings"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/request"
"github.com/evcc-io/evcc/util/urlvalues"
"github.com/evcc-io/evcc/vehicle/vag"
"golang.org/x/oauth2"
)
// Login performs VW identity login with optional code challenge
func Oauth2Login(log *util.Logger, oc *oauth2.Config, user, password string) (vag.TokenSource, error) {
oc.Endpoint = Config.NewProvider(context.Background()).Endpoint()
// add code challenge
cv := oauth2.GenerateVerifier()
q := url.Values{
"response_type": {"code id_token"},
"client_id": {oc.ClientID},
"redirect_uri": {oc.RedirectURL},
"code_challenge_method": {"S256"},
"code_challenge": {oauth2.S256ChallengeFromVerifier(cv)},
"scope": {strings.Join(oc.Scopes, " ")},
}
uri := fmt.Sprintf("%s?%s", oc.Endpoint.AuthURL, q.Encode())
vwi := New(log)
q, err := vwi.Login(uri, user, password)
if err != nil {
return nil, err
}
if err := urlvalues.Require(q, "id_token", "code"); err != nil {
return nil, err
}
os := &Oauth2Service{Helper: request.NewHelper(log), Config: oc}
token, err := os.retrieveToken(url.Values{
"client_id": {oc.ClientID},
"client_secret": {oc.ClientSecret},
"redirect_uri": {oc.RedirectURL},
"grant_type": {"authorization_code"},
"code": {q.Get("code")},
"code_verifier": {cv},
})
if err != nil {
return nil, err
}
return os.TokenSource(token), nil
}
type Oauth2Service struct {
*oauth2.Config
*request.Helper
}
func (v *Oauth2Service) retrieveToken(data url.Values) (*vag.Token, error) {
var res vag.Token
req, err := request.New(http.MethodPost, v.Endpoint.TokenURL, strings.NewReader(data.Encode()), request.URLEncoding, request.AcceptJSON)
if err == nil {
err = v.DoJSON(req, &res)
}
return &res, err
}
func (v *Oauth2Service) refresh(token *vag.Token) (*vag.Token, error) {
return v.retrieveToken(url.Values{
"client_id": {v.ClientID},
"client_secret": {v.ClientSecret},
"grant_type": {"refresh_token"},
"refresh_token": {token.RefreshToken},
})
}
// TokenSource creates token source. Token is refreshed automatically.
func (v *Oauth2Service) TokenSource(token *vag.Token) vag.TokenSource {
return vag.RefreshTokenSource(token, v.refresh)
}

View file

@ -28,11 +28,6 @@ var brands = map[string]brand{
"Cupra": {"f85e5b69-e3b2-43aa-9c0d-1b7d0e0b576f@apps_vw-dilab_com", "CUPRA"},
}
// Brands returns the supported brand names
func Brands() []string {
return []string{"Volkswagen", "Audi", "Skoda", "Seat", "Cupra"}
}
// resolveBrand looks up a brand by name, case-insensitively
func resolveBrand(name string) (brand, bool) {
for k, b := range brands {