chore: fix linter (#11028)

This commit is contained in:
andig 2023-12-06 18:29:32 +01:00 • committed by GitHub
parent 098a0fc2b5
commit 58e2421a5b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
91 changed files with 374 additions and 280 deletions

View file

@ -1,10 +1,55 @@
run:
go: "1.21"
issues:
exclude:
- .regActualCurrent. is unused
- "`routeLogger` is unused"
linters-settings:
staticcheck:
checks:
- "-SA1019"
linters:
disable-all: true
enable:
- dogsled
- durationcheck
- exportloopref
- gci
- gofmt
- gofumpt
- goimports
- goprintffuncname
- govet
- importas
- ineffassign
- makezero
- misspell
- nolintlint
- rowserrcheck
- sqlclosecheck
- staticcheck
- tparallel
- typecheck
- unconvert
- unused
- wastedassign
- whitespace
# fixme
# - bodyclose
# - exhaustive
# - gocritic
# - godot
# - gomoddirectives
# - nakedret
# - nilerr
# - noctx
# - prealloc
# - predeclared
# - revive
# - stylecheck
# - unparam
# - dupl

View file

@ -93,7 +93,7 @@ func NewAlphatec(uri, device, comset string, baudrate int, proto modbus.Protocol
func (wb *Alphatec) setCurrent(current uint16) error {
b := make([]byte, 2)
binary.BigEndian.PutUint16(b, uint16(current))
binary.BigEndian.PutUint16(b, current)
_, err := wb.conn.WriteMultipleRegisters(alphatecRegAmpsConfig, 1, b)

View file

@ -356,7 +356,7 @@ func (c *Easee) ProductUpdate(i json.RawMessage) {
}
// OpMode changed FROM >1 ("car connected") TO 1/disconnected - stop ticker if channel exists
// channel may not exist regulary if the car was connected but charging never started
// channel may not exist regularly if the car was connected but charging never started
if c.opMode != easee.ModeDisconnected && opMode == easee.ModeDisconnected && c.stopTicker != nil {
close(c.stopTicker)
c.stopTicker = nil

View file

@ -73,7 +73,7 @@ const (
LOCAL_PRE_AUTHORIZE_ENABLED ObservationID = 15 // Preauthorize with whitelist enabled. Readback on setting [event] [Boolean]
LOCAL_AUTHORIZE_OFFLINE_ENABLED ObservationID = 16 // Allow offline charging for whitelisted RFID token. Readback on setting [event] [Boolean]
ALLOW_OFFLINE_TX_FOR_UNKNOWN_ID ObservationID = 17 // Allow offline charging for all RFID tokens. Readback on setting [event] [Boolean]
ERRATIC_EVMAX_TOGGLES ObservationID = 18 // 0 == erratic checking disabled, otherwise the number of toggles between states Charging and Charging Complate that will trigger an error [Integer]
ERRATIC_EVMAX_TOGGLES ObservationID = 18 // 0 == erratic checking disabled, otherwise the number of toggles between states Charging and Charging Complete that will trigger an error [Integer]
BACKPLATE_TYPE ObservationID = 19 // Readback on backplate type [Integer]
SITE_STRUCTURE ObservationID = 20 // Site Structure [boot] [String]
DETECTED_POWER_GRID_TYPE ObservationID = 21 // Detected power grid type according to PowerGridType table [boot] [Integer]
@ -94,7 +94,7 @@ const (
FORCED_THREE_PHASE_ON_ITWITH_GND_FAULT ObservationID = 39 // Default disabled. Must be set manually if grid type is indeed three phase IT [Boolean]
LED_STRIP_BRIGHTNESS ObservationID = 40 // LED strip brightness, 0-100% [Integer]
LOCAL_AUTHORIZATION_REQUIRED ObservationID = 41 // Local RFID authorization is required for charging [user options] [event] [Boolean]
AUTHORIZATION_REQUIRED ObservationID = 42 // Authorization is requried for charging [Boolean]
AUTHORIZATION_REQUIRED ObservationID = 42 // Authorization is required for charging [Boolean]
REMOTE_START_REQUIRED ObservationID = 43 // Remote start required flag [event] [Boolean]
SMART_BUTTON_ENABLED ObservationID = 44 // Smart button is enabled [Boolean]
OFFLINE_CHARGING_MODE ObservationID = 45 // Charger behavior when offline [Integer]

View file

@ -36,12 +36,10 @@ func NewWattpilotFromConfig(other map[string]interface{}) (api.Charger, error) {
}
return NewWattpilot(cc.URI, cc.Password, cc.Cache)
}
// NewWattpilot creates Wattpilot charger
func NewWattpilot(uri, password string, cache time.Duration) (api.Charger, error) {
c := &Wattpilot{
api: wattpilot.New(uri, password),
}

View file

@ -66,7 +66,7 @@ func (c *LocalAPI) upgradeV2() {
err := c.response("api/status?filter=alw", &res)
if err == nil {
c.uri = c.uri + "/api"
c.uri += "/api"
} else {
c.v2 = false
}

View file

@ -119,7 +119,7 @@ func (mcc *MobileConnect) fetchToken(request *http.Request) error {
}
// login as the home user with the given password
func (mcc *MobileConnect) login(password string) error {
func (mcc *MobileConnect) login() error {
uri := fmt.Sprintf("%s/%s", mcc.uri, mccAPILogin)
data := url.Values{
@ -170,7 +170,7 @@ func (mcc *MobileConnect) request(method, uri string) (*http.Request, error) {
// do we need to login?
if mcc.token == "" {
if err := mcc.login(mcc.password); err != nil {
if err := mcc.login(); err != nil {
return nil, err
}
}

View file

@ -31,7 +31,7 @@ type apiResponse struct {
// NewTestClient returns *http.Client with Transport replaced to avoid making real calls
func NewTestClient(fn roundTripFunc) *http.Client {
return &http.Client{
Transport: roundTripFunc(fn),
Transport: fn,
}
}
@ -50,13 +50,13 @@ func NewTestMobileConnect(t *testing.T, responses []apiResponse) *MobileConnect
// response string for the currently invoked call
var responseString string
for _, s := range responses {
if strings.Contains("/"+string(s.apiCall), req.URL.Path) {
if strings.Contains("/"+s.apiCall, req.URL.Path) {
responseString = s.apiResponse
}
}
return &http.Response{
StatusCode: 200,
StatusCode: http.StatusOK,
// Send response to be tested
Body: io.NopCloser(bytes.NewBufferString(responseString)),
// Must be set to non-nil value or it panics
@ -83,7 +83,7 @@ func TestMobileConnectLogin(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
mcc := NewTestMobileConnect(t, tc.responses)
if err := mcc.login(tc.password); (err != nil) != tc.wantErr {
if err := mcc.login(); (err != nil) != tc.wantErr {
t.Errorf("MobileConnect.login() error = %v, wantErr %v", err, tc.wantErr)
}
})
@ -263,13 +263,15 @@ func TestMobileConnectCurrentPower(t *testing.T) {
"no data response",
[]apiResponse{
{mccAPIEnergy, "\"\"\n"},
}, 0, false,
},
0, false,
},
{
"home plug - error response",
[]apiResponse{
{mccAPIEnergy, "\"{\\n \\\"L1\\\": {\\n \\\"Ampere\\\": 0,\\n \\\"Power\\\": 0,\\n \\\"Volts\\\": 246.60000000000002\\n },\\n \\\"L2\\\": {\\n \\\"Ampere\\\": 0,\\n \\\"Power\\\": 0,\\n \\\"Volts\\\": 16.800000000000001\\n },\\n \\\"L3\\\": {\\n \\\"Ampere\\\": 0,\\n \\\"Power\\\": 0,\\n \\\"Volts\\\": 16.300000000000001\\n }\\n}\\n\""},
}, 0, false,
},
0, false,
},
}
for _, tc := range tests {
@ -300,19 +302,22 @@ func TestMobileConnectChargedEnergy(t *testing.T) {
"valid response",
[]apiResponse{
{mccAPICurrentSession, "\"{\\n \\\"account\\\": \\\"PRIVATE\\\",\\n \\\"chargingRate\\\": 0,\\n \\\"chargingType\\\": \\\"AC\\\",\\n \\\"clockSrc\\\": \\\"NTP\\\",\\n \\\"costs\\\": 0,\\n \\\"currency\\\": \\\"\\\",\\n \\\"departTime\\\": \\\"\\\",\\n \\\"duration\\\": 30789,\\n \\\"endOfChargeTime\\\": \\\"\\\",\\n \\\"endSoc\\\": 0,\\n \\\"endTime\\\": \\\"\\\",\\n \\\"energySumKwh\\\": 18.832000000000001,\\n \\\"evChargingRatekW\\\": 0,\\n \\\"evTargetSoc\\\": -1,\\n \\\"evVasAvailability\\\": false,\\n \\\"pcid\\\": \\\"\\\",\\n \\\"powerRange\\\": 0,\\n \\\"selfEnergy\\\": 0,\\n \\\"sessionId\\\": 13,\\n \\\"soc\\\": -1,\\n \\\"solarEnergyShare\\\": 0,\\n \\\"startSoc\\\": 0,\\n \\\"startTime\\\": \\\"2020-04-15T10:07:22+02:00\\\",\\n \\\"totalRange\\\": 0,\\n \\\"vehicleBrand\\\": \\\"\\\",\\n \\\"vehicleModel\\\": \\\"\\\",\\n \\\"whitelist\\\": false\\n}\\n\""},
}, 18.832000000000001, false,
},
18.832000000000001, false,
},
{
"no data response",
[]apiResponse{
{mccAPICurrentSession, "\"\"\n"},
}, 0, false,
},
0, false,
},
{
"error response",
[]apiResponse{
{mccAPICurrentSession, "invalidjson"},
}, 0, true,
},
0, true,
},
}
for _, tc := range tests {
@ -342,19 +347,22 @@ func TestMobileConnectChargingTime(t *testing.T) {
"valid response",
[]apiResponse{
{mccAPICurrentSession, "\"{\\n \\\"account\\\": \\\"PRIVATE\\\",\\n \\\"chargingRate\\\": 0,\\n \\\"chargingType\\\": \\\"AC\\\",\\n \\\"clockSrc\\\": \\\"NTP\\\",\\n \\\"costs\\\": 0,\\n \\\"currency\\\": \\\"\\\",\\n \\\"departTime\\\": \\\"\\\",\\n \\\"duration\\\": 30789,\\n \\\"endOfChargeTime\\\": \\\"\\\",\\n \\\"endSoc\\\": 0,\\n \\\"endTime\\\": \\\"\\\",\\n \\\"energySumKwh\\\": 18.832000000000001,\\n \\\"evChargingRatekW\\\": 0,\\n \\\"evTargetSoc\\\": -1,\\n \\\"evVasAvailability\\\": false,\\n \\\"pcid\\\": \\\"\\\",\\n \\\"powerRange\\\": 0,\\n \\\"selfEnergy\\\": 0,\\n \\\"sessionId\\\": 13,\\n \\\"soc\\\": -1,\\n \\\"solarEnergyShare\\\": 0,\\n \\\"startSoc\\\": 0,\\n \\\"startTime\\\": \\\"2020-04-15T10:07:22+02:00\\\",\\n \\\"totalRange\\\": 0,\\n \\\"vehicleBrand\\\": \\\"\\\",\\n \\\"vehicleModel\\\": \\\"\\\",\\n \\\"whitelist\\\": false\\n}\\n\""},
}, 30789 * time.Second, false,
},
30789 * time.Second, false,
},
{
"no data response",
[]apiResponse{
{mccAPICurrentSession, "\"\"\n"},
}, 0, false,
},
0, false,
},
{
"error response",
[]apiResponse{
{mccAPICurrentSession, "invalidjson"},
}, 0, true,
},
0, true,
},
}
for _, tc := range tests {
@ -399,13 +407,15 @@ func TestMobileConnectCurrents(t *testing.T) {
"no data response",
[]apiResponse{
{mccAPIEnergy, "\"\"\n"},
}, 0, 0, 0, false,
},
0, 0, 0, false,
},
{
"home plug - error response",
[]apiResponse{
{mccAPIEnergy, "\"{\\n \\\"L1\\\": {\\n \\\"Ampere\\\": 0,\\n \\\"Power\\\": 0,\\n \\\"Volts\\\": 246.60000000000002\\n },\\n \\\"L2\\\": {\\n \\\"Ampere\\\": 0,\\n \\\"Power\\\": 0,\\n \\\"Volts\\\": 16.800000000000001\\n },\\n \\\"L3\\\": {\\n \\\"Ampere\\\": 0,\\n \\\"Power\\\": 0,\\n \\\"Volts\\\": 16.300000000000001\\n }\\n}\\n\""},
}, 0, 0, 0, false,
},
0, 0, 0, false,
},
}
for _, tc := range tests {

View file

@ -109,16 +109,16 @@ func (suite *ocppTestSuite) TestConnect() {
// status
_, err = c1.Status()
suite.NoError(err)
suite.Require().NoError(err)
// power
f, err := c1.currentPower()
suite.NoError(err)
suite.Require().NoError(err)
suite.Equal(1e3, f)
// energy
f, err = c1.totalEnergy()
suite.NoError(err)
suite.Require().NoError(err)
suite.Equal(1.2, f)
}
@ -148,7 +148,7 @@ func (suite *ocppTestSuite) TestConnect() {
res, err := cp1.StopTransaction(0, types.NewDateTime(suite.clock.Now()), expectedTxn)
suite.Require().NoError(err)
suite.Equal(res.IdTagInfo.Status, types.AuthorizationStatusAccepted)
suite.Equal(types.AuthorizationStatusAccepted, res.IdTagInfo.Status)
}
// 2nd charge point - remote
@ -166,7 +166,7 @@ func (suite *ocppTestSuite) TestConnect() {
// status
_, err = c2.Status()
suite.NoError(err)
suite.Require().NoError(err)
}
// error on unconfigured 2nd charge point

View file

@ -2,6 +2,7 @@ package charger
import (
"fmt"
"strconv"
"strings"
"github.com/evcc-io/evcc/api"
@ -69,7 +70,7 @@ func (wb *Pantabox) Enabled() (bool, error) {
// Enable implements the api.Charger interface
func (wb *Pantabox) Enable(enable bool) error {
resp, err := wb.Post(wb.uri+"/charger/enable", request.PlainContent, strings.NewReader(fmt.Sprintf("%t", enable)))
resp, err := wb.Post(wb.uri+"/charger/enable", request.PlainContent, strings.NewReader(strconv.FormatBool(enable)))
if err == nil {
defer resp.Body.Close()
@ -83,7 +84,7 @@ func (wb *Pantabox) Enable(enable bool) error {
// MaxCurrent implements the api.Charger interface
func (wb *Pantabox) MaxCurrent(current int64) error {
resp, err := wb.Post(wb.uri+"/charger/current", request.PlainContent, strings.NewReader(fmt.Sprintf("%d", current)))
resp, err := wb.Post(wb.uri+"/charger/current", request.PlainContent, strings.NewReader(strconv.FormatInt(current, 10)))
if err == nil {
defer resp.Body.Close()

View file

@ -54,7 +54,7 @@ type SendParameter struct {
Values []Value `json:"values"`
}
// part of Paramter Send structure
// part of Parameter Send structure
type Value struct {
Timestamp string `json:"timestamp"`
ChannelId string `json:"channelId"`

View file

@ -34,6 +34,7 @@ var acceptable = []string{
func TestTemplates(t *testing.T) {
templates.TestClass(t, templates.Charger, func(t *testing.T, values map[string]any) {
t.Helper()
if _, err := NewFromConfig("template", values); err != nil && !test.Acceptable(err, acceptable) {
t.Log(values)
t.Error(err)

View file

@ -163,7 +163,6 @@ func (c *CmdConfigure) configureLinkedTemplate(templateItem templates.Template,
if c.askConfigFailureNextStep() {
continue
}
} else {
c.configuration.AddDevice(deviceItem, category)
c.processDeviceCapabilities(templateItem.Capabilities)

View file

@ -76,11 +76,9 @@ func (c *CmdConfigure) processDeviceValues(values map[string]interface{}, templa
c.addedDeviceIndex--
return device, c.errDeviceNotValid
}
} else {
if deviceCategory == DeviceCategoryCharger && testResult == DeviceTestResultValid {
} else if deviceCategory == DeviceCategoryCharger && testResult == DeviceTestResultValid {
device.ChargerHasMeter = true
}
}
templateItem.Params = append(templateItem.Params, templates.Param{Name: "name", Value: device.Name})
if !c.expandedMode {
@ -330,7 +328,7 @@ func (c *CmdConfigure) fetchElements(deviceCategory DeviceCategory) []templates.
}
}
sort.Slice(items[:], func(i, j int) bool {
sort.Slice(items, func(i, j int) bool {
// sort generic templates to the bottom
if items[i].Group != "" && items[j].Group == "" {
return false
@ -376,7 +374,7 @@ func (c *CmdConfigure) processConfig(templateItem *templates.Template, deviceCat
fmt.Println(c.localizedString("Config_Title"))
fmt.Println()
c.processModbusConfig(templateItem, deviceCategory)
c.processModbusConfig(templateItem)
return c.processParams(templateItem, deviceCategory)
}
@ -485,7 +483,7 @@ func (c *CmdConfigure) processInputConfig(param templates.Param) string {
// processModbusConfig adds default values from the modbus Param to the template
// and handles user input for interface type selection
func (c *CmdConfigure) processModbusConfig(templateItem *templates.Template, deviceCategory DeviceCategory) {
func (c *CmdConfigure) processModbusConfig(templateItem *templates.Template) {
var choices []string
var choiceTypes []string

View file

@ -209,11 +209,9 @@ func (c *CmdConfigure) flowNewConfigFile() {
// in case of permission error, we can't write to the file anyway
if os.IsPermission(err) {
fmt.Println(c.localizedString("File_Permissions", localizeMap{"FileName": filename}))
} else {
if c.askYesNo(c.localizedString("File_Exists", localizeMap{"FileName": filename})) {
} else if c.askYesNo(c.localizedString("File_Exists", localizeMap{"FileName": filename})) {
break
}
}
filename = c.askValue(question{
label: c.localizedString("File_NewFilename"),
@ -302,7 +300,6 @@ func (c *CmdConfigure) configureLoadpoints() {
fmt.Println(c.localizedString("Loadpoint_Setup"))
for {
loadpointTitle := c.askValue(question{
label: c.localizedString("Loadpoint_Title"),
defaultValue: c.localizedString("Loadpoint_DefaultTitle"),

View file

@ -74,7 +74,7 @@ type ModbusHandler struct {
Timeout time.Duration
}
func (h *ModbusHandler) testRegister(log *util.Logger, conn gridx.Client) bool {
func (h *ModbusHandler) testRegister(_ *util.Logger, conn gridx.Client) bool {
var bytes []byte
var err error

View file

@ -35,7 +35,7 @@ type TcpHandler struct {
dialer net.Dialer
}
func (h *TcpHandler) Test(log *util.Logger, in ResultDetails) (res []ResultDetails) {
func (h *TcpHandler) Test(_ *util.Logger, in ResultDetails) (res []ResultDetails) {
for _, port := range h.Ports {
addr := net.JoinHostPort(in.IP, strconv.Itoa(port))
conn, err := h.dialer.Dial("tcp", addr)

View file

@ -57,7 +57,8 @@ func redact(src string) string {
"token", "access", "refresh", "accesstoken", "refreshtoken", // tokens, including template variations
"ain", "secret", "serial", "deviceid", "machineid", "idtag", // devices
"app", "chats", "recipients", // push messaging
"vin"} // vehicles
"vin", // vehicles
}
return regexp.
MustCompile(fmt.Sprintf(`(?i)\b(%s)\b.*?:.*`, strings.Join(secrets, "|"))).
ReplaceAllString(src, "$1: *****")

View file

@ -24,12 +24,11 @@ import (
"github.com/evcc-io/evcc/util/telemetry"
"github.com/fatih/structs"
"github.com/jeremywohl/flatten"
"golang.org/x/exp/maps"
_ "github.com/joho/godotenv/autoload"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"golang.org/x/exp/maps"
)
const rebootDelay = 5 * time.Minute // delayed reboot on error

View file

@ -645,19 +645,14 @@ func configureSite(conf map[string]interface{}, loadpoints []*core.Loadpoint, ta
}
func configureLoadpoints(conf globalConfig) (loadpoints []*core.Loadpoint, err error) {
lpInterfaces, ok := viper.AllSettings()["loadpoints"].([]interface{})
if !ok || len(lpInterfaces) == 0 {
if len(conf.Loadpoints) == 0 {
return nil, errors.New("missing loadpoints")
}
for id, lpcI := range lpInterfaces {
var lpc map[string]interface{}
if err := util.DecodeOther(lpcI, &lpc); err != nil {
return nil, fmt.Errorf("failed decoding loadpoint configuration: %w", err)
}
for id, lpc := range conf.Loadpoints {
log := util.NewLoggerWithLoadpoint("lp-"+strconv.Itoa(id+1), id+1)
settings := &core.Settings{Key: "lp" + strconv.Itoa(id+1) + "."}
lp, err := core.NewLoadpointFromConfig(log, settings, lpc)
if err != nil {
return nil, fmt.Errorf("failed configuring loadpoint: %w", err)

View file

@ -33,7 +33,6 @@ func tokenExchangeHandler(oc *oauth2.Config, state string, resC chan *oauth2.Tok
token, err := oc.Exchange(ctx, code,
oauth2.SetAuthURLParam("grant_type", "code"), // app
)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintln(w, err)

View file

@ -49,9 +49,7 @@ func generate(out io.Writer, packageName, functionName, baseType string, dynamic
return dict, nil
},
// contains checks if slice contains string
"contains": func(combo []string, typ string) bool {
return slices.Contains(combo, typ)
},
"contains": slices.Contains[[]string, string],
// ordered returns a slice of typeStructs ordered by dynamicType
"ordered": func() []typeStruct {
ordered := make([]typeStruct, 0)

View file

@ -13,7 +13,7 @@ type API interface {
// Owner returns the loadpoint that currently owns the vehicle
Owner(api.Vehicle) loadpoint.API
// Aquire acquires the vehicle for the loadpoint and releases it at any other loadpoint
// Acquire acquires the vehicle for the loadpoint and releases it at any other loadpoint
Acquire(api.Vehicle)
// Release releases a vehicle from a loadpoint

View file

@ -3,7 +3,7 @@ package core
// EnergyMetrics calculates stats about the charged energy and gives you details about price or co2s
type EnergyMetrics struct {
totalKWh float64 // Total amount of energy used (kWh)
solarKWh float64 // Self-produced energy energy (kWh)
solarKWh float64 // Self-produced energy (kWh)
price *float64 // Total cost (Currency)
co2 *float64 // Amount of emitted CO2 (gCO2eq)
currentGreenShare float64 // Current share of solar energy of site (0-1)

View file

@ -28,72 +28,83 @@ func TestEnergyMetrics(t *testing.T) {
totalWh, solarPercentage float64
price, pricePerKWh, co2PerKWh *float64
}{
{"initial state",
{
"initial state",
[]tcStep{},
0, 0, nil, nil, nil,
},
{"energy value",
{
"energy value",
[]tcStep{
{0.1, 0, nil, nil},
{0.2, 0, nil, nil},
},
200, 0, nil, nil, nil,
},
{"ignore lower energy value",
{
"ignore lower energy value",
[]tcStep{
{0.2, 0, nil, nil},
{0.1, 0, nil, nil},
},
200, 0, nil, nil, nil,
},
{"half solar",
{
"half solar",
[]tcStep{
{0.1, 1, nil, nil},
{0.2, 0, nil, nil},
},
200, 50, nil, nil, nil,
},
{"only solar",
{
"only solar",
[]tcStep{
{0.1, 1, nil, nil},
{0.2, 1, nil, nil},
},
200, 100, nil, nil, nil,
},
{"static price",
{
"static price",
[]tcStep{
{1, 0, f(0.5), nil},
},
1000, 0, f(0.5), f(0.5), nil,
},
{"dynamic price",
{
"dynamic price",
[]tcStep{
{1, 0, f(1), nil},
{2, 0, f(0), nil},
},
2000, 0, f(1), f(0.5), nil,
},
{"dynamic price",
{
"dynamic price",
[]tcStep{
{2, 0, f(1), nil},
{4, 0, f(0), nil},
},
4000, 0, f(2), f(0.5), nil,
},
{"static co2",
{
"static co2",
[]tcStep{
{1, 0, nil, f(500)},
},
1000, 0, nil, nil, f(500),
},
{"dynamic co2",
{
"dynamic co2",
[]tcStep{
{1, 0, nil, f(1000)},
{2, 0, nil, f(0)},
},
2000, 0, nil, nil, f(500),
},
{"grid only, half, full solar, half, grid only",
{
"grid only, half, full solar, half, grid only",
[]tcStep{
{1, 0, f(2), f(200)},
{2, 0.5, f(1), f(50)},
@ -106,8 +117,6 @@ func TestEnergyMetrics(t *testing.T) {
}
for _, tc := range tc {
//t.Logf("%+v", tc)
s := NewEnergyMetrics()
for _, tc := range tc.steps {

View file

@ -13,7 +13,7 @@ type Health struct {
}
// NewHealth creates new health checker
func NewHealth(timeout time.Duration) (health *Health) {
func NewHealth(timeout time.Duration) *Health {
return &Health{timeout: timeout}
}

View file

@ -9,6 +9,9 @@ import (
"sync"
"time"
evbus "github.com/asaskevich/EventBus"
"github.com/avast/retry-go/v4"
"github.com/benbjohnson/clock"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/core/coordinator"
"github.com/evcc-io/evcc/core/keys"
@ -23,10 +26,6 @@ import (
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/config"
"github.com/evcc-io/evcc/util/telemetry"
evbus "github.com/asaskevich/EventBus"
"github.com/avast/retry-go/v4"
"github.com/benbjohnson/clock"
)
const (

View file

@ -123,6 +123,8 @@ func TestMaxActivePhases(t *testing.T) {
}
func testScale(t *testing.T, lp *Loadpoint, sitePower float64, direction string, tc testCase) {
t.Helper()
act := lp.activePhases()
max := lp.maxActivePhases()

View file

@ -11,15 +11,16 @@ import (
"github.com/evcc-io/evcc/util"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestSession(t *testing.T) {
var err error
serverdb.Instance, err = serverdb.New("sqlite", ":memory:")
assert.NoError(t, err)
require.NoError(t, err)
db, err := session.NewStore("foo", serverdb.Instance)
assert.NoError(t, err)
require.NoError(t, err)
clock := clock.NewMock()
@ -68,7 +69,7 @@ func TestSession(t *testing.T) {
assert.Equal(t, clock.Now(), lp.session.Finished)
s, err := db.Sessions()
assert.NoError(t, err)
require.NoError(t, err)
assert.Len(t, s, 1)
t.Logf("session: %+v", s)
@ -82,7 +83,7 @@ func TestSession(t *testing.T) {
assert.Equal(t, clock.Now(), lp.session.Finished)
s, err = db.Sessions()
assert.NoError(t, err)
require.NoError(t, err)
assert.Len(t, s, 1)
t.Logf("session: %+v", s)
}
@ -90,32 +91,32 @@ func TestSession(t *testing.T) {
func TestCloseSessionsOnStartup_emptyDb(t *testing.T) {
var err error
serverdb.Instance, err = serverdb.New("sqlite", ":memory:")
assert.NoError(t, err)
require.NoError(t, err)
db, err := session.NewStore("foo", serverdb.Instance)
assert.NoError(t, err)
require.NoError(t, err)
// assert empty DB is no problem
err = db.ClosePendingSessionsInHistory(1000)
assert.NoError(t, err)
require.NoError(t, err)
}
func TestCloseSessionsOnStartup(t *testing.T) {
var err error
serverdb.Instance, err = serverdb.New("sqlite", ":memory:")
assert.NoError(t, err)
require.NoError(t, err)
db1, err := session.NewStore("foo", serverdb.Instance)
assert.NoError(t, err)
require.NoError(t, err)
db2, err := session.NewStore("bar", serverdb.Instance)
assert.NoError(t, err)
require.NoError(t, err)
clock := clock.NewMock()
// test data, creates 6 sessions for each loadpoint, 3rd and 6th are "unfinished"
var sessions1 []*session.Session = createMockSessions(db1, clock)
var sessions2 []*session.Session = createMockSessions(db2, clock)
sessions1 := createMockSessions(db1, clock)
sessions2 := createMockSessions(db2, clock)
// write interleaved for two loadpoints
for index, session := range sessions1 {
@ -124,12 +125,12 @@ func TestCloseSessionsOnStartup(t *testing.T) {
}
err = db1.ClosePendingSessionsInHistory(1000)
assert.NoError(t, err)
require.NoError(t, err)
// check fixed sessions for db1
var db1Sessions session.Sessions
err = serverdb.Instance.Where("Loadpoint = ?", "foo").Order("ID").Find(&db1Sessions).Error
assert.NoError(t, err)
require.NoError(t, err)
assert.Len(t, db1Sessions, 6)
// check fixed history
@ -146,7 +147,7 @@ func TestCloseSessionsOnStartup(t *testing.T) {
// ensure no side effects on loadpoint 2 data, i.e. data left unfixed
var db2Sessions session.Sessions
err = serverdb.Instance.Where("Loadpoint = ?", "bar").Order("ID").Find(&db2Sessions).Error
assert.NoError(t, err)
require.NoError(t, err)
assert.Len(t, db2Sessions, 6)
for i, s := range db2Sessions {
@ -163,8 +164,7 @@ func TestCloseSessionsOnStartup(t *testing.T) {
func createMockSessions(db *session.DB, clock *clock.Mock) []*session.Session {
var sessions []*session.Session
for i := 1; i <= 6; i++ {
var meter1Start float64 = float64(i * 10)
meter1Start := float64(i * 10)
session := db.New(meter1Start)
session.Created = clock.Now().Add(1 * time.Minute)
@ -175,7 +175,7 @@ func createMockSessions(db *session.DB, clock *clock.Mock) []*session.Session {
}
session.Finished = clock.Now().Add(2 * time.Minute)
meterStop := float64(meter1Start + 10)
meterStop := meter1Start + 10
session.MeterStop = &meterStop
session.ChargedEnergy = 10
sessions = append(sessions, session)

View file

@ -8,6 +8,7 @@ import (
"github.com/evcc-io/evcc/util"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestSyncCharger(t *testing.T) {
@ -42,7 +43,7 @@ func TestSyncCharger(t *testing.T) {
enabled: tc.expected,
}
assert.NoError(t, lp.syncCharger())
require.NoError(t, lp.syncCharger())
assert.Equal(t, tc.corrected, lp.enabled)
}
}

View file

@ -33,6 +33,8 @@ func (n *Null) ChargingTime() (time.Duration, error) {
}
func createChannels(t *testing.T) (chan util.Param, chan push.Event, chan *Loadpoint) {
t.Helper()
uiChan := make(chan util.Param)
pushChan := make(chan push.Event)
lpChan := make(chan *Loadpoint)
@ -67,6 +69,8 @@ func attachChannels(lp *Loadpoint, uiChan chan util.Param, pushChan chan push.Ev
}
func attachListeners(t *testing.T, lp *Loadpoint) {
t.Helper()
Voltage = 230 // V
if charger, ok := lp.charger.(*api.MockCharger); ok && charger != nil {
@ -421,7 +425,7 @@ func TestDisableAndEnableAtTargetSoc(t *testing.T) {
attachListeners(t, lp)
lp.enabled = true
lp.chargeCurrent = float64(minA)
lp.chargeCurrent = minA
lp.status = api.StatusC
t.Log("charging below soc target")
@ -487,7 +491,7 @@ func TestSetModeAndSocAtDisconnect(t *testing.T) {
attachListeners(t, lp)
lp.enabled = true
lp.chargeCurrent = float64(minA)
lp.chargeCurrent = minA
lp.mode = api.ModeNow
t.Log("charging at min")
@ -512,6 +516,8 @@ func TestSetModeAndSocAtDisconnect(t *testing.T) {
// cacheExpecter can be used to verify asynchronously written values from cache
func cacheExpecter(t *testing.T, lp *Loadpoint) (*util.Cache, func(key string, val interface{})) {
t.Helper()
// attach cache for verifying values
paramC := make(chan util.Param)
lp.uiChan = paramC
@ -554,7 +560,7 @@ func TestChargedEnergyAtDisconnect(t *testing.T) {
attachListeners(t, lp)
lp.enabled = true
lp.chargeCurrent = float64(maxA)
lp.chargeCurrent = maxA
lp.mode = api.ModeNow
// attach cache for verifying values

View file

@ -10,6 +10,7 @@ import (
"github.com/evcc-io/evcc/util"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func rates(prices []float64, start time.Time, slotDuration time.Duration) api.Rates {
@ -43,13 +44,13 @@ func TestPlan(t *testing.T) {
}
rates, err := trf.Rates()
assert.NoError(t, err)
require.NoError(t, err)
slices.SortStableFunc(rates, sortByCost)
{
plan := p.plan(rates, time.Hour, clock.Now())
assert.Equal(t, 0, len(plan))
assert.Empty(t, plan)
}
tc := []struct {
@ -81,7 +82,7 @@ func TestPlan(t *testing.T) {
},
{
"plan (30)30-0-60-0-0-0",
time.Duration(90 * time.Minute),
90 * time.Minute,
clock.Now(),
clock.Now().Add(6 * time.Hour),
clock.Now().Add(30 * time.Minute),
@ -105,7 +106,7 @@ func TestPlan(t *testing.T) {
},
{
"plan (30)30-0-60-0-0-0",
time.Duration(90 * time.Minute),
90 * time.Minute,
clock.Now().Add(30 * time.Minute),
clock.Now().Add(6 * time.Hour),
clock.Now().Add(30 * time.Minute),
@ -133,12 +134,12 @@ func TestNilTariff(t *testing.T) {
}
plan, err := p.Plan(time.Hour, clock.Now().Add(30*time.Minute))
assert.NoError(t, err)
assert.True(t, !SlotAt(clock.Now(), plan).IsEmpty(), "should start past start time")
require.NoError(t, err)
assert.False(t, SlotAt(clock.Now(), plan).IsEmpty(), "should start past start time")
plan, err = p.Plan(time.Hour, clock.Now().Add(-30*time.Minute))
assert.NoError(t, err)
assert.False(t, !SlotAt(clock.Now(), plan).IsEmpty(), "should not start past target time")
require.NoError(t, err)
assert.True(t, SlotAt(clock.Now(), plan).IsEmpty(), "should not start past target time")
}
func TestFlatTariffTargetInThePast(t *testing.T) {
@ -155,12 +156,12 @@ func TestFlatTariffTargetInThePast(t *testing.T) {
}
plan, err := p.Plan(time.Hour, clock.Now().Add(30*time.Minute))
assert.NoError(t, err)
assert.True(t, !SlotAt(clock.Now(), plan).IsEmpty(), "should start past start time")
require.NoError(t, err)
assert.False(t, SlotAt(clock.Now(), plan).IsEmpty(), "should start past start time")
plan, err = p.Plan(time.Hour, clock.Now().Add(-30*time.Minute))
assert.NoError(t, err)
assert.False(t, !SlotAt(clock.Now(), plan).IsEmpty(), "should not start past target time")
require.NoError(t, err)
assert.True(t, SlotAt(clock.Now(), plan).IsEmpty(), "should not start past target time")
}
func TestFlatTariffLongSlots(t *testing.T) {
@ -181,13 +182,13 @@ func TestFlatTariffLongSlots(t *testing.T) {
// expect 00:00-01:00 UTC
plan, err := p.Plan(time.Hour, clock.Now().Add(2*time.Hour))
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, api.Rate{Start: clock.Now(), End: clock.Now().Add(time.Hour)}, SlotAt(clock.Now(), plan))
assert.Equal(t, api.Rate{}, SlotAt(clock.Now().Add(time.Hour), plan))
// expect 00:00-01:00 UTC
plan, err = p.Plan(time.Hour, clock.Now().Add(time.Hour))
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, api.Rate{Start: clock.Now(), End: clock.Now().Add(time.Hour)}, SlotAt(clock.Now(), plan))
}
@ -205,11 +206,11 @@ func TestTargetAfterKnownPrices(t *testing.T) {
}
plan, err := p.Plan(40*time.Minute, clock.Now().Add(2*time.Hour)) // charge efficiency does not allow to test with 1h
assert.NoError(t, err)
require.NoError(t, err)
assert.False(t, !SlotAt(clock.Now(), plan).IsEmpty(), "should not start if car can be charged completely after known prices ")
plan, err = p.Plan(2*time.Hour, clock.Now().Add(2*time.Hour))
assert.NoError(t, err)
require.NoError(t, err)
assert.True(t, !SlotAt(clock.Now(), plan).IsEmpty(), "should start if car can not be charged completely after known prices ")
}
@ -227,10 +228,10 @@ func TestChargeAfterTargetTime(t *testing.T) {
}
plan, err := p.Plan(time.Hour, clock.Now())
assert.NoError(t, err)
assert.False(t, !SlotAt(clock.Now(), plan).IsEmpty(), "should not start past target time")
require.NoError(t, err)
assert.True(t, SlotAt(clock.Now(), plan).IsEmpty(), "should not start past target time")
plan, err = p.Plan(time.Hour, clock.Now().Add(-time.Hour))
assert.NoError(t, err)
assert.False(t, !SlotAt(clock.Now(), plan).IsEmpty(), "should not start past target time")
require.NoError(t, err)
assert.True(t, SlotAt(clock.Now(), plan).IsEmpty(), "should not start past target time")
}

View file

@ -8,6 +8,7 @@ import (
"github.com/evcc-io/evcc/util"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestDetermineBatteryMode(t *testing.T) {
@ -49,7 +50,6 @@ func TestDetermineBatteryMode(t *testing.T) {
}
func TestUpdateBatteryMode(t *testing.T) {
expBatMode := api.BatteryHold
ctrl := gomock.NewController(t)
@ -70,6 +70,6 @@ func TestUpdateBatteryMode(t *testing.T) {
}
err := s.updateBatteryMode(expBatMode)
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, expBatMode, s.GetBatteryMode())
}

View file

@ -37,51 +37,81 @@ func TestGreenShare(t *testing.T) {
grid, pv, battery, home, lp float64
greenShareTotal, greenShareHome, greenShareLoadpoints float64
}{
{"half grid, half pv, green home",
{
"half grid, half pv, green home",
1000, 1000, 0, 1000, 1000,
0.5, 1, 0},
{"half grid, half pv, no home",
0.5, 1, 0,
},
{
"half grid, half pv, no home",
1000, 1000, 0, 0, 2000,
0.5, 1, 0.5},
{"half grid, half pv, no lp",
0.5, 1, 0.5,
},
{
"half grid, half pv, no lp",
2500, 2500, 0, 5000, 0,
0.5, 0.5, 0},
{"full pv",
0.5, 0.5, 0,
},
{
"full pv",
0, 5000, 0, 1000, 4000,
1, 1, 1},
{"full grid",
1, 1, 1,
},
{
"full grid",
5000, 0, 0, 1000, 4000,
0, 0, 0},
{"half grid, half battery, green home",
0, 0, 0,
},
{
"half grid, half battery, green home",
1000, 0, 1000, 1000, 1000,
0.5, 1, 0},
{"half grid, half battery, no home",
0.5, 1, 0,
},
{
"half grid, half battery, no home",
1000, 0, 1000, 0, 2000,
0.5, 1, 0.5},
{"half grid, half battery, no lp",
0.5, 1, 0.5,
},
{
"half grid, half battery, no lp",
1000, 0, 1000, 2000, 0,
0.5, 0.5, 0},
{"full pv, pv export",
0.5, 0.5, 0,
},
{
"full pv, pv export",
-5000, 10000, 0, 1000, 4000,
1, 1, 1},
{"full pv, pv export, no lp",
1, 1, 1,
},
{
"full pv, pv export, no lp",
-5000, 10000, 0, 5000, 0,
1, 1, 1},
{"full pv, pv export, battery charge",
1, 1, 1,
},
{
"full pv, pv export, battery charge",
-2500, 10000, -2500, 1000, 4000,
1, 1, 1},
{"full grid, battery charge",
1, 1, 1,
},
{
"full grid, battery charge",
3000, 0, -1000, 1000, 1000,
0, 0, 0},
{"full grid, battery charge, no lp",
0, 0, 0,
},
{
"full grid, battery charge, no lp",
2000, 0, -1000, 1000, 0,
0, 0, 0},
{"half grid, half pv, battery charge, no lp",
0, 0, 0,
},
{
"half grid, half pv, battery charge, no lp",
1000, 1000, -1000, 1000, 0,
1, 1, 0},
{"half grid, half pv, battery charge, home, lp",
1, 1, 0,
},
{
"half grid, half pv, battery charge, home, lp",
1000, 1000, -1000, 500, 500,
1, 1, 1},
1, 1, 1,
},
}
for _, tc := range tc {

View file

@ -50,7 +50,7 @@ func (s *Estimator) Reset() {
s.prevSoc = 0
s.prevChargedEnergy = 0
s.initialSoc = 0
s.capacity = float64(s.vehicle.Capacity()) * 1e3 // cache to simplify debugging
s.capacity = s.vehicle.Capacity() * 1e3 // cache to simplify debugging
s.virtualCapacity = s.capacity / ChargeEfficiency // initial capacity taking efficiency into account
s.energyPerSocStep = s.virtualCapacity / 100
s.minChargePower = 1000 // default 1 kW

View file

@ -17,14 +17,14 @@ func TestTimer(t *testing.T) {
// start
at.Start()
clck.Add(10 * time.Second)
require.Equal(t, at.Expired(), false)
require.False(t, at.Expired())
// wait another 20 sec to expire the timer - this will reset the timer as well
clck.Add(wakeupTimeout + 10*time.Second)
require.Equal(t, at.Expired(), true)
require.True(t, at.Expired())
// start
at.Start()
clck.Add(time.Minute)
require.Equal(t, at.Expired(), true)
require.True(t, at.Expired())
}

View file

@ -11,7 +11,6 @@ import (
"github.com/evcc-io/evcc/hems/ocpp/profile"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/machine"
ocpp16 "github.com/lorenzodonini/ocpp-go/ocpp1.6"
ocppcore "github.com/lorenzodonini/ocpp-go/ocpp1.6/core"
"github.com/lorenzodonini/ocpp-go/ws"

View file

@ -84,7 +84,6 @@ func (c *API) updateValues() error {
req, err := request.New(http.MethodPost, uri, strings.NewReader(data), map[string]string{
"Content-Type": "text/plain",
})
if err != nil {
return err
}

View file

@ -6,17 +6,17 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// Test MethodResponse response
func TestUnmarshalMethodResponse(t *testing.T) {
{
// BidCos-RF (Port 2001) getParamset measure-channel response test
var res MethodResponse
xmlstr := `<?xml version="1.0" encoding="iso-8859-1"?><methodResponse><params><param><value><struct><member><name>IEC_ENERGY_COUNTER</name><value><double>689.586500</double></value></member><member><name>IEC_POWER</name><value><double>166.390000</double></value></member></struct></value></param></params></methodResponse>`
assert.NoError(t, xml.Unmarshal([]byte(strings.Replace(string(xmlstr), "iso-8859-1", "UTF-8", 1)), &res))
require.NoError(t, xml.Unmarshal([]byte(strings.Replace(xmlstr, "iso-8859-1", "UTF-8", 1)), &res))
assert.Equal(t, "IEC_ENERGY_COUNTER", res.Member[0].Name)
assert.Equal(t, float64(689.586500), res.Member[0].Value.CCUFloat)
@ -27,7 +27,7 @@ func TestUnmarshalMethodResponse(t *testing.T) {
var res MethodResponse
xmlstr := `<?xml version="1.0" encoding="ISO-8859-1"?><methodResponse><params><param><value><struct><member><name>VOLTAGE</name><value><double>230.6</double></value></member><member><name>POWER_STATUS</name><value><i4>0</i4></value></member><member><name>ENERGY_COUNTER</name><value><double>10888.7</double></value></member><member><name>CURRENT_STATUS</name><value><i4>0</i4></value></member><member><name>FREQUENCY</name><value><double>49.97</double></value></member><member><name>ENERGY_COUNTER_OVERFLOW</name><value><boolean>0</boolean></value></member><member><name>POWER</name><value><double>0.05</double></value></member><member><name>VOLTAGE_STATUS</name><value><i4>0</i4></value></member><member><name>CURRENT</name><value><double>0.0</double></value></member><member><name>FREQUENCY_STATUS</name><value><i4>0</i4></value></member></struct></value></param></params></methodResponse>`
assert.NoError(t, xml.Unmarshal([]byte(strings.Replace(string(xmlstr), "ISO-8859-1", "UTF-8", 1)), &res))
require.NoError(t, xml.Unmarshal([]byte(strings.Replace(xmlstr, "ISO-8859-1", "UTF-8", 1)), &res))
assert.Equal(t, "ENERGY_COUNTER", res.Member[2].Name)
assert.Equal(t, float64(10888.7), res.Member[2].Value.CCUFloat)
@ -40,10 +40,10 @@ func TestUnmarshalMethodResponse(t *testing.T) {
var res MethodResponse
xmlstr := `<?xml version="1.0" encoding="ISO-8859-1"?><methodResponse><params><param><value><struct><member><name>SECTION_STATUS</name><value><i4>0</i4></value></member><member><name>PROCESS</name><value><i4>0</i4></value></member><member><name>STATE</name><value><boolean>1</boolean></value></member><member><name>SECTION</name><value><i4>2</i4></value></member></struct></value></param></params></methodResponse>`
assert.NoError(t, xml.Unmarshal([]byte(strings.Replace(string(xmlstr), "ISO-8859-1", "UTF-8", 1)), &res))
require.NoError(t, xml.Unmarshal([]byte(strings.Replace(xmlstr, "ISO-8859-1", "UTF-8", 1)), &res))
assert.Equal(t, "STATE", res.Member[2].Name)
assert.Equal(t, true, res.Member[2].Value.CCUBool)
assert.True(t, res.Member[2].Value.CCUBool)
}
{
@ -51,12 +51,11 @@ func TestUnmarshalMethodResponse(t *testing.T) {
var res MethodResponse
xmlstr := `<?xml version="1.0" encoding="ISO-8859-1"?><methodResponse><fault><value><struct><member><name>faultCode</name><value><i4>-2</i4></value></member><member><name>faultString</name><value>Invalid device</value></member></struct></value></fault></methodResponse>`
assert.NoError(t, xml.Unmarshal([]byte(strings.Replace(string(xmlstr), "ISO-8859-1", "UTF-8", 1)), &res))
require.NoError(t, xml.Unmarshal([]byte(strings.Replace(xmlstr, "ISO-8859-1", "UTF-8", 1)), &res))
assert.Equal(t, "faultCode", res.Fault[0].Name)
assert.Equal(t, int64(-2), res.Fault[0].Value.CCUInt)
assert.Equal(t, "faultString", res.Fault[1].Name)
assert.Equal(t, "Invalid device", res.Fault[1].Value.CCUString)
}
}

View file

@ -5,6 +5,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// Test ApiResponse
@ -13,7 +14,7 @@ func TestUnmarshalApiResponse(t *testing.T) {
var res ApiResponse
jsonstr := `{"product_type": "HWE-SKT","product_name": "P1 Meter","serial": "3c39e7aabbcc","firmware_version": "2.11","api_version": "v1"}`
assert.NoError(t, json.Unmarshal([]byte(jsonstr), &res))
require.NoError(t, json.Unmarshal([]byte(jsonstr), &res))
assert.Equal(t, "HWE-SKT", res.ProductType)
assert.Equal(t, "v1", res.ApiVersion)
@ -26,9 +27,9 @@ func TestUnmarshalStateResponse(t *testing.T) {
var res StateResponse
jsonstr := `{"power_on": true,"switch_lock": false,"brightness": 255}`
assert.NoError(t, json.Unmarshal([]byte(jsonstr), &res))
require.NoError(t, json.Unmarshal([]byte(jsonstr), &res))
assert.Equal(t, true, res.PowerOn)
assert.True(t, res.PowerOn)
}
}
@ -38,7 +39,7 @@ func TestUnmarshalDataResponse(t *testing.T) {
var res DataResponse
jsonstr := `{"wifi_ssid": "My Wi-Fi","wifi_strength": 100,"total_power_import_t1_kwh": 30.511,"total_power_export_t1_kwh": 85.951,"active_power_w": 543,"active_power_l1_w": 676}`
assert.NoError(t, json.Unmarshal([]byte(jsonstr), &res))
require.NoError(t, json.Unmarshal([]byte(jsonstr), &res))
assert.Equal(t, float64(30.511), res.TotalPowerImportT1kWh+res.TotalPowerImportT2kWh+res.TotalPowerImportT3kWh+res.TotalPowerImportT4kWh)
assert.Equal(t, float64(543), res.ActivePowerW)

View file

@ -155,7 +155,7 @@ func NewPowerWall(uri, usage, user, password string, cache time.Duration, refres
}
batteryCapacity = func() float64 {
return float64(res.NominalFullPackEnergy) / 1e3
return res.NominalFullPackEnergy / 1e3
}
}
@ -212,7 +212,7 @@ func (m *PowerWall) batterySoc() (float64, error) {
return 0, err
}
return float64(res.Percentage), err
return res.Percentage, err
}
// decorate soc

View file

@ -9,7 +9,6 @@ import (
"github.com/cenkalti/backoff/v4"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/util"
"github.com/mlnoga/rct"
)

View file

@ -145,7 +145,7 @@ func (sh *Switch) TotalEnergy() (float64, error) {
func gen1Energy(devicetype string, energy float64) float64 {
// Gen 1 Shelly EM devices are providing Watt hours, Gen 1 Shelly PM devices are providing Watt minutes
if !strings.Contains(devicetype, "EM") {
energy = energy / 60
energy /= 60
}
return energy
}

View file

@ -5,6 +5,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// Test Gen1StatusResponse response
@ -14,7 +15,7 @@ func TestUnmarshalGen1StatusResponse(t *testing.T) {
var res Gen1StatusResponse
jsonstr := `{"wifi_sta":{"connected":true,"ssid":"XXXX-WLAN","ip":"192.168.178.XXX","rssi":-54},"cloud":{"enabled":false,"connected":false},"mqtt":{"connected":false},"time":"17:59","unixtime":1676134770,"serial":2437,"has_update":true,"mac":"84CCA8XXXXXXX","cfg_changed_cnt":1,"actions_stats":{"skipped":0},"relays":[{"ison":false,"has_timer":false,"timer_started":0,"timer_duration":0,"timer_remaining":0,"overpower":false,"source":"http"}],"meters":[{"power":4711.12,"overpower":0.00,"is_valid":true,"timestamp":1676138370,"counters":[0.000, 0.000, 0.000],"total":6472513}],"inputs":[{"input":0,"event":"","event_cnt":0}],"temperature":16.79,"overtemperature":false,"tmp":{"tC":16.79,"tF":62.22, "is_valid":true},"temperature_status":"Normal","ext_sensors":{},"ext_temperature":{},"ext_humidity":{},"update":{"status":"pending","has_update":true,"new_version":"20221108-153925/v1.12.1-1PM-fix-g2821131","old_version":"20220209-094317/v1.11.8-g8c7bb8d"},"ram_total":50456,"ram_free":37056,"fs_size":233681,"fs_free":149094,"uptime":17284290}`
assert.NoError(t, json.Unmarshal([]byte(jsonstr), &res))
require.NoError(t, json.Unmarshal([]byte(jsonstr), &res))
assert.Equal(t, 107875.21666666666, gen1Energy("SHSW-PM", res.Meters[0].Total))
assert.Equal(t, 4711.12, res.Meters[0].Power)
@ -25,7 +26,7 @@ func TestUnmarshalGen1StatusResponse(t *testing.T) {
var res Gen1StatusResponse
jsonstr := `{"wifi_sta":{"connected":true,"ssid":"XXXX-WLAN","ip":"192.168.178.XXX","rssi":-57},"cloud":{"enabled":false,"connected":false},"mqtt":{"connected":false},"time":"19:25","unixtime":1676139913,"serial":959,"has_update":true,"mac":"E8DB8XXXXXX","cfg_changed_cnt":1,"actions_stats":{"skipped":0},"relays":[{"ison":false,"has_timer":false,"timer_started":0,"timer_duration":0,"timer_remaining":0,"source":"timer"}],"meters":[{"power":81.5,"is_valid":true}],"inputs":[{"input":0,"event":"","event_cnt":0}],"ext_sensors":{},"ext_temperature":{},"ext_humidity":{},"update":{"status":"pending","has_update":true,"new_version":"20221027-091427/v1.12.1-ga9117d3","old_version":"20211109-124958/v1.11.7-g682a0db"},"ram_total":50880,"ram_free":38796,"fs_size":233681,"fs_free":151102,"uptime":20319391}`
assert.NoError(t, json.Unmarshal([]byte(jsonstr), &res))
require.NoError(t, json.Unmarshal([]byte(jsonstr), &res))
assert.Equal(t, 0.0, gen1Energy("SHSW-1", res.Meters[0].Total))
assert.Equal(t, 81.5, res.Meters[0].Power)
@ -36,7 +37,7 @@ func TestUnmarshalGen1StatusResponse(t *testing.T) {
var res Gen1StatusResponse
jsonstr := `{"wifi_sta":{"connected":true,"ssid":"XXXX","ip":"192.168.178.XXX","rssi":-55},"cloud":{"enabled":false,"connected":false},"mqtt":{"connected":false},"time":"11:16","unixtime":1676110566,"serial":21580,"has_update":false,"mac":"C45BXXXXX","cfg_changed_cnt":0,"actions_stats":{"skipped":0},"relays":[{"ison":false,"has_timer":false,"timer_started":0,"timer_duration":0,"timer_remaining":0,"overpower":false,"is_valid":true,"source":"input"}],"emeters":[{"power":-620.34,"reactive":714.48,"pf":-0.66,"voltage":235.68,"is_valid":true,"total":401472.9,"total_returned":653673.7},{"power":0.00,"reactive":0.00,"pf":0.00,"voltage":235.68,"is_valid":true,"total":173411.3,"total_returned":294.2}],"update":{"status":"idle","has_update":false,"new_version":"20221027-105518/v1.12.1-ga9117d3","old_version":"20221027-105518/v1.12.1-ga9117d3"},"ram_total":51072,"ram_free":35660,"fs_size":233681,"fs_free":156373,"uptime":2226140}`
assert.NoError(t, json.Unmarshal([]byte(jsonstr), &res))
require.NoError(t, json.Unmarshal([]byte(jsonstr), &res))
assert.Equal(t, 401472.9, gen1Energy("SHEM", res.EMeters[0].Total))
assert.Equal(t, -620.34, res.EMeters[0].Power)
@ -49,7 +50,7 @@ func TestUnmarshalGen2StatusResponse(t *testing.T) {
var res Gen2StatusResponse
jsonstr := `{"ble":{},"cloud":{"connected":true},"eth":{"ip":null},"input:0":{"id":0,"state":false},"input:1":{"id":1,"state":false},"mqtt":{"connected":false},"switch:0":{"id":0, "source":"HTTP", "output":false, "apower":47.11, "voltage":232.0, "current":0.000, "pf":0.00, "aenergy":{"total":5.125,"by_minute":[0.000,0.000,0.000],"minute_ts":1675718520},"temperature":{"tC":25.3, "tF":77.5}},"sys":{"mac":"30C6F78BB4D8","restart_required":false,"time":"22:22","unixtime":1675718522,"uptime":45070,"ram_size":234204,"ram_free":137716,"fs_size":524288,"fs_free":172032,"cfg_rev":13,"kvs_rev":1,"schedule_rev":0,"webhook_rev":0,"available_updates":{"beta":{"version":"0.13.0-beta3"}}},"wifi":{"sta_ip":"192.168.178.64","status":"got ip","ssid":"***","rssi":-62},"ws":{"connected":false}}`
assert.NoError(t, json.Unmarshal([]byte(jsonstr), &res))
require.NoError(t, json.Unmarshal([]byte(jsonstr), &res))
assert.Equal(t, 5.125, res.Switch0.Aenergy.Total)
assert.Equal(t, 47.11, res.Switch0.Apower)

View file

@ -100,7 +100,7 @@ func (c *Connection) ChargedEnergy() (float64, error) {
}
if int64(resp.TodayEnergy) > c.lasttodayenergy {
c.energy = c.energy + (int64(resp.TodayEnergy) - c.lasttodayenergy)
c.energy += (int64(resp.TodayEnergy) - c.lasttodayenergy)
}
c.lasttodayenergy = int64(resp.TodayEnergy)

View file

@ -114,7 +114,6 @@ func (c *Connection) RelayExists() error {
// Enable implements the api.Charger interface
func (c *Connection) Enable(enable bool) error {
for _, channel := range c.channels {
cmd := fmt.Sprintf("Power%d off", channel)
if enable {
cmd = fmt.Sprintf("Power%d on", channel)
@ -191,7 +190,6 @@ func (c *Connection) Enabled() (bool, error) {
enabled = strings.ToUpper(res.StatusSTS.Power8) == "ON"
default:
enabled = strings.ToUpper(res.StatusSTS.Power) == "ON" || strings.ToUpper(res.StatusSTS.Power1) == "ON"
}
}
return enabled, nil

View file

@ -31,6 +31,8 @@ var acceptable = []string{
func TestTemplates(t *testing.T) {
templates.TestClass(t, templates.Meter, func(t *testing.T, values map[string]any) {
t.Helper()
if _, err := NewFromConfig("template", values); err != nil && !test.Acceptable(err, acceptable) {
t.Log(values)
t.Error(err)

View file

@ -37,7 +37,7 @@ func (d *Connection) ExecCmd(cmd string, res interface{}) error {
buf := bytes.NewBuffer([]byte{0, 0, 0, 0})
var key byte = 171 // initialization vector
for i := 0; i < len(cmd); i++ {
key = key ^ cmd[i]
key ^= cmd[i]
_ = buf.WriteByte(key)
}

View file

@ -67,7 +67,7 @@ func process(filepath string) error {
fmt.Fprintln(out, sc.Text())
}
if err := os.WriteFile(filepath, []byte(out.String()), 0644); err != nil {
if err := os.WriteFile(filepath, []byte(out.String()), 0o644); err != nil {
return fmt.Errorf("%s: %v", filepath, err)
}

View file

@ -36,7 +36,8 @@ func TestCachedGetter(t *testing.T) {
expect := func(s struct {
f float64
e error
}) {
},
) {
f, e := c.Get()
if f != s.f || e != s.e {
t.Errorf("unexpected cache value: %f, %v\n", f, e)

View file

@ -37,7 +37,6 @@ func RegisteredClient(log *util.Logger, broker, user, password, clientID string,
mu.Lock()
defer mu.Unlock()
client, err := registry.Get(key)
if err != nil {
if clientID == "" {
clientID = ClientID()

View file

@ -60,7 +60,7 @@ func (h *msgHandler) stringGetter() (string, error) {
return "", err
}
return string(v), nil
return v, nil
}
func (h *msgHandler) boolGetter() (bool, error) {

View file

@ -31,7 +31,6 @@ func TestRegexDefault(t *testing.T) {
func TestJq(t *testing.T) {
for _, uuid := range []string{"a8232ee0-a4ab-11ec-8d36-211f6b082dc8", "08232ee0-a4ab-11ec-8d36-211f6b082dc8"} {
p, err := new(Pipeline).WithJq(fmt.Sprintf(`.data[] | select(.uuid=="%s") | .tuples[0][1]`, uuid))
require.NoError(t, err)

View file

@ -5,13 +5,14 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestString(t *testing.T) {
v := "foo"
SetString("string", v)
res, err := String("string")
assert.Nil(t, err)
require.NoError(t, err)
assert.Equal(t, v, res)
}
@ -19,7 +20,7 @@ func TestInt(t *testing.T) {
v := int64(math.MaxInt64)
SetInt("int64", v)
res, err := Int("int64")
assert.Nil(t, err)
require.NoError(t, err)
assert.Equal(t, v, res)
}
@ -27,6 +28,6 @@ func TestFloat(t *testing.T) {
v := 3.141
SetFloat("float64", v)
res, err := Float("float64")
assert.Nil(t, err)
require.NoError(t, err)
assert.Equal(t, v, res)
}

View file

@ -26,7 +26,7 @@ func (w *influxWriter) WritePoint(p *write.Point) {
}
func (w *influxWriter) finish() {
assert.Equal(w.t, len(w.p), w.idx, "not enough points")
assert.Len(w.t, w.p, w.idx, "not enough points")
}
func TestInfluxTypes(t *testing.T) {

View file

@ -12,23 +12,24 @@ import (
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/modbus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestConcurrentRead(t *testing.T) {
l, err := net.Listen("tcp", "localhost:0")
assert.NoError(t, err)
require.NoError(t, err)
defer l.Close()
srv, _ := mbserver.New(&echoHandler{
id: 0,
RequestHandler: new(mbserver.DummyHandler),
})
assert.NoError(t, srv.Start(l))
require.NoError(t, srv.Start(l))
defer func() { _ = srv.Stop() }()
// client
conn, err := modbus.NewConnection(l.Addr().String(), "", "", 0, modbus.Tcp, 1)
assert.NoError(t, err)
require.NoError(t, err)
var wg sync.WaitGroup
@ -41,7 +42,7 @@ func TestConcurrentRead(t *testing.T) {
qty := uint16(rand.Int31n(32) + 1)
b, err := conn.ReadInputRegistersWithSlave(uint8(id), addr, qty)
assert.NoError(t, err)
require.NoError(t, err)
if err == nil {
for u := uint16(0); u < qty; u++ {
@ -62,56 +63,56 @@ func TestConcurrentRead(t *testing.T) {
func TestReadCoils(t *testing.T) {
// downstream server
l, err := net.Listen("tcp", "localhost:0")
assert.NoError(t, err)
require.NoError(t, err)
defer l.Close()
srv, _ := mbserver.New(&echoHandler{
id: 0,
RequestHandler: new(mbserver.DummyHandler),
})
assert.NoError(t, srv.Start(l))
require.NoError(t, srv.Start(l))
defer func() { _ = srv.Stop() }()
// proxy server
pl, err := net.Listen("tcp", "localhost:0")
assert.NoError(t, err)
require.NoError(t, err)
defer pl.Close()
downstreamConn, err := modbus.NewConnection(l.Addr().String(), "", "", 0, modbus.Tcp, 1)
assert.NoError(t, err)
require.NoError(t, err)
proxy, _ := mbserver.New(&handler{
log: util.NewLogger("foo"),
conn: downstreamConn,
})
assert.NoError(t, proxy.Start(pl))
require.NoError(t, proxy.Start(pl))
defer func() { _ = proxy.Stop() }()
// test client
{
conn, err := modbus.NewConnection(pl.Addr().String(), "", "", 0, modbus.Tcp, 1)
assert.NoError(t, err)
require.NoError(t, err)
{ // read
b, err := conn.ReadCoilsWithSlave(1, 1, 1)
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, []byte{0x01}, b)
b, err = conn.ReadCoilsWithSlave(1, 1, 2)
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, []byte{0x03}, b)
b, err = conn.ReadCoilsWithSlave(1, 1, 9)
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, []byte{0xFF, 0x01}, b)
}
{ // write
b, err := conn.WriteSingleCoilWithSlave(1, 1, 0xFF00)
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, []byte{0xFF, 0x00}, b)
b, err = conn.WriteMultipleCoilsWithSlave(1, 1, 9, []byte{0xFF, 0x01})
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, []byte{0x00, 0x09}, b)
}
}

View file

@ -167,7 +167,7 @@ func (m *MQTT) listenSiteSetters(topic string, site site.API) error {
var err error
if err == nil {
err = m.Handler.ListenSetter(m.root+"/site/prioritySoc", func(payload string) error {
err = m.Handler.ListenSetter(topic+"/site/prioritySoc", func(payload string) error {
val, err := parseFloat(payload)
if err == nil {
err = site.SetPrioritySoc(val)
@ -177,7 +177,7 @@ func (m *MQTT) listenSiteSetters(topic string, site site.API) error {
}
if err == nil {
err = m.Handler.ListenSetter(m.root+"/site/bufferSoc", func(payload string) error {
err = m.Handler.ListenSetter(topic+"/site/bufferSoc", func(payload string) error {
val, err := parseFloat(payload)
if err == nil {
err = site.SetBufferSoc(val)
@ -187,7 +187,7 @@ func (m *MQTT) listenSiteSetters(topic string, site site.API) error {
}
if err == nil {
err = m.Handler.ListenSetter(m.root+"/site/bufferStartSoc", func(payload string) error {
err = m.Handler.ListenSetter(topic+"/site/bufferStartSoc", func(payload string) error {
val, err := parseFloat(payload)
if err == nil {
err = site.SetBufferStartSoc(val)
@ -197,7 +197,7 @@ func (m *MQTT) listenSiteSetters(topic string, site site.API) error {
}
if err == nil {
err = m.Handler.ListenSetter(m.root+"/site/residualPower", func(payload string) error {
err = m.Handler.ListenSetter(topic+"/site/residualPower", func(payload string) error {
val, err := parseFloat(payload)
if err == nil {
err = site.SetResidualPower(val)
@ -207,7 +207,7 @@ func (m *MQTT) listenSiteSetters(topic string, site site.API) error {
}
if err == nil {
err = m.Handler.ListenSetter(m.root+"/site/smartCostLimit", func(payload string) error {
err = m.Handler.ListenSetter(topic+"/site/smartCostLimit", func(payload string) error {
val, err := parseFloat(payload)
if err == nil {
err = site.SetSmartCostLimit(val)

View file

@ -6,6 +6,7 @@ import (
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestEncode(t *testing.T) {
@ -27,7 +28,7 @@ func TestEncode(t *testing.T) {
for _, tc := range tc {
out, err := encode(tc.in)
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, tc.out, out)
}
}
@ -44,7 +45,7 @@ func TestEncodeSlice(t *testing.T) {
for _, tc := range tc {
out, err := encodeSlice(tc.in)
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, tc.out, out)
}
}

View file

@ -113,7 +113,7 @@ var zones = map[string][]string{
"10YPT-REN------W": {"LFA|PT", "LFB|PT", "MBA|PT", "SCA|PT", "CTA|PT", "Portugal (PT)", "BZN|PT"},
"10YRO-TEL------P": {"BZN|RO", "Romania (RO)", "CTA|RO", "SCA|RO", "MBA|RO", "LFB|RO", "LFA|RO"},
"10YSE-1--------K": {"MBA|SE", "SCA|SE", "CTA|SE", "Sweden (SE)"},
"10YSI-ELES-----O": {"Slovenia (SI)", "BZN|SI", "CTA|SI", "SCA|SI", "MBA|SI", "LFA|SI"},
"10YSI-EELS-----O": {"Slovenia (SI)", "BZN|SI", "CTA|SI", "SCA|SI", "MBA|SI", "LFA|SI"},
"10YSK-SEPS-----K": {"LFA|SK", "LFB|SK", "MBA|SK", "SCA|SK", "CTA|SK", "BZN|SK", "Slovakia (SK)"},
"10YTR-TEIAS----W": {"Turkey (TR)", "BZN|TR", "CTA|TR", "SCA|TR", "MBA|TR", "LFB|TR", "LFA|TR"},
"10YUA-WEPS-----0": {"LFA|UA-BEI", "LFB|UA-BEI", "MBA|UA-BEI", "SCA|UA-BEI", "CTA|UA-BEI", "BZN|UA-BEI"},

View file

@ -88,7 +88,7 @@ func ParseDays(s string) ([]Day, error) {
if err != nil {
return nil, err
}
res = append(res, Day(from%7))
res = append(res, from%7)
if len(fromto) == 2 {
to, err := ParseDay(fromto[1])

View file

@ -4,43 +4,44 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestParseDays(t *testing.T) {
d, err := ParseDays(" sunday ")
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, []Day{Sunday}, d)
d, err = ParseDays("sun")
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, []Day{Sunday}, d)
d, err = ParseDays("so")
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, []Day{Sunday}, d)
d, err = ParseDays("0 ")
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, []Day{Sunday}, d)
d, err = ParseDays(" 7")
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, []Day{Sunday}, d)
d, err = ParseDays(" 6-7 ")
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, []Day{Saturday, Sunday}, d)
d, err = ParseDays("1- 7")
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, []Day{Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday}, d)
_, err = ParseDays(" ")
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, []Day{Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday}, d)
d, err = ParseDays("1, 3-7")
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, []Day{Monday, Wednesday, Thursday, Friday, Saturday, Sunday}, d)
_, err = ParseDays("-")

View file

@ -4,15 +4,16 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestParseTimeRange(t *testing.T) {
d, err := ParseTimeRanges(" 7:00 - 12:30 ")
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, []TimeRange{{HourMin{7, 0}, HourMin{12, 30}}}, d, "hour:min range")
d, err = ParseTimeRanges(" 8-10 ")
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, []TimeRange{{HourMin{8, 0}, HourMin{10, 0}}}, d, "hour range")
_, err = ParseTimeRanges("-1-10")
@ -22,14 +23,14 @@ func TestParseTimeRange(t *testing.T) {
assert.EqualError(t, err, "invalid time: 25", "<to> after midnight")
d, err = ParseTimeRanges("8-0")
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, []TimeRange{{HourMin{8, 0}, HourMin{0, 0}}}, d, "hour till midnight (0)")
_, err = ParseTimeRanges("10-8")
assert.EqualError(t, err, "invalid time range: 10-8, <from> must be before <to>")
d, err = ParseTimeRanges("8-10, 20-22:30")
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, []TimeRange{
{HourMin{8, 0}, HourMin{10, 0}},
{HourMin{20, 0}, HourMin{22, 30}},

View file

@ -9,6 +9,7 @@ import (
"github.com/evcc-io/evcc/tariff/fixed"
"github.com/jinzhu/now"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestFixed(t *testing.T) {
@ -33,7 +34,7 @@ func TestFixed(t *testing.T) {
}
rates, err := tf.Rates()
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, expect, rates)
}
@ -47,7 +48,7 @@ func TestFixedSplitZones(t *testing.T) {
{0.1, "0-5:30,21-0"},
},
})
assert.NoError(t, err)
require.NoError(t, err)
tf := at.(*Fixed)
tf.clock = clock.NewMock()
@ -99,6 +100,6 @@ func TestFixedSplitZones(t *testing.T) {
}
rates, err := tf.Rates()
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, expect, rates)
}

View file

@ -5,6 +5,7 @@ import (
"github.com/mitchellh/mapstructure"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestDecodeNil(t *testing.T) {
@ -23,12 +24,12 @@ func TestDecodeNil(t *testing.T) {
}
decoder, err := mapstructure.NewDecoder(decoderConfig)
assert.NoError(t, err)
require.NoError(t, err)
err = decoder.Decode(map[string]any{
"user": nil,
})
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, struct {
User, Password string

View file

@ -54,7 +54,7 @@ func newLogger(area string, lp int) *Logger {
padded := area
for len(padded) < LogAreaPadding {
padded = padded + " "
padded += " "
}
level := LogLevelForArea(area)
@ -188,10 +188,7 @@ func captureLogger(l *Logger) {
}
func captureLogLevel(level string, lp int, l *log.Logger) {
re, err := regexp.Compile(`^\[[a-zA-Z0-9-]+\s*\] \w+ .{19} `)
if err != nil {
panic(err)
}
re := regexp.MustCompile(`^\[[a-zA-Z0-9-]+\s*\] \w+ .{19} `)
ui := uiWriter{
lp: lp,

View file

@ -7,9 +7,8 @@ import (
"fmt"
"strings"
"github.com/samber/lo"
"github.com/denisbrodbeck/machineid"
"github.com/samber/lo"
)
var id string

View file

@ -53,7 +53,7 @@ func decodeBool16(mask uint64) func(b []byte) float64 {
return func(b []byte) float64 {
u := binary.BigEndian.Uint16(b)
if mask > 0 {
u = u & uint16(mask)
u &= uint16(mask)
}
if u > 0 {
return 1

View file

@ -390,7 +390,6 @@ func RegisterOperation(r Register) (rs485.Operation, error) {
}
switch strings.ToLower(r.Decode) {
// 8 bit (coil)
case "bool8":
op.Transform = decodeBool8

View file

@ -3,7 +3,7 @@ package templates
import (
"bytes"
_ "embed"
"fmt"
"strconv"
"strings"
"text/template"
@ -36,7 +36,7 @@ func (t *Template) RenderDocumentation(product Product, lang string) ([]byte, er
case string:
t.Params[index].Value = yamlQuote(v)
case int:
t.Params[index].Value = fmt.Sprintf("%d", v)
t.Params[index].Value = strconv.Itoa(v)
}
}
}

View file

@ -11,6 +11,8 @@ import (
// test renders and instantiates plus yaml-parses the template per usage
func test(t *testing.T, tmpl Template, values map[string]interface{}, cb func(values map[string]interface{})) {
t.Helper()
b, _, err := tmpl.RenderResult(TemplateRenderModeInstance, values)
if err != nil {
t.Log(string(b))
@ -32,6 +34,8 @@ func test(t *testing.T, tmpl Template, values map[string]interface{}, cb func(va
}
func TestClass(t *testing.T, class Class, instantiate func(t *testing.T, values map[string]interface{})) {
t.Parallel()
for _, tmpl := range ByClass(class) {
tmpl := tmpl

View file

@ -5,6 +5,7 @@ import (
_ "embed"
"fmt"
"slices"
"strconv"
"strings"
"text/template"
@ -230,7 +231,7 @@ func (t *Template) RenderProxyWithValues(values map[string]interface{}, lang str
case string:
t.Params[index].Value = yamlQuote(v)
case int:
t.Params[index].Value = fmt.Sprintf("%d", v)
t.Params[index].Value = strconv.Itoa(v)
}
}
}

View file

@ -57,9 +57,7 @@ func FuncMap(tmpl *template.Template) *template.Template {
}
return buf.String(), nil
},
"urlEncode": func(v string) string {
return url.QueryEscape(v)
},
"urlEncode": url.QueryEscape,
"toDuration": func(v string) time.Duration {
d, err := time.ParseDuration(v)
if err != nil {

View file

@ -5,6 +5,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v3"
)
@ -19,7 +20,7 @@ func TestYamlDecode(t *testing.T) {
}
err := yaml.Unmarshal([]byte(input), &res)
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, value, res.Value)
})
}

View file

@ -6,6 +6,8 @@ import (
)
func SkipCI(t *testing.T) {
t.Helper()
if os.Getenv("CI") != "" {
t.Skip("Skipping testing in CI environment")
}

View file

@ -11,7 +11,6 @@ import (
"github.com/evcc-io/evcc/provider"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/request"
"github.com/joeshaw/carwings"
)
@ -111,7 +110,7 @@ func (v *CarWings) status() (carwings.BatteryStatus, error) {
// api result is stale
if v.refreshKey != "" {
if err := v.refreshResult(); err != nil {
return *new(carwings.BatteryStatus), err
return carwings.BatteryStatus{}, err
}
}
@ -120,7 +119,7 @@ func (v *CarWings) status() (carwings.BatteryStatus, error) {
if err == nil {
if elapsed := time.Since(bs.Timestamp); elapsed > carwingsStatusExpiry {
if err = v.refreshRequest(); err != nil {
return *new(carwings.BatteryStatus), err
return carwings.BatteryStatus{}, err
}
err = api.ErrMustRetry

View file

@ -94,7 +94,7 @@ func (ct *TimeMillis) UnmarshalJSON(data []byte) error {
if err == nil {
t := time.Unix(0, i*1e6)
(*ct).Time = t
ct.Time = t
}
return err

View file

@ -103,7 +103,7 @@ func (v *Identity) login() (*oauth.Token, error) {
var settings Settings
if err := json.Unmarshal([]byte(match[1]), &settings); err != nil {
if err := json.Unmarshal(match[1], &settings); err != nil {
return nil, err
}

View file

@ -64,7 +64,7 @@ func (ct *Timestamp) UnmarshalJSON(data []byte) error {
t, err := time.Parse(TimeFormat, s)
if err == nil {
(*ct).Time = t
ct.Time = t
}
return err

View file

@ -86,7 +86,7 @@ func (v *Identity) Login(user, password string) error {
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", string(res.Errors[0].Key), err)
err = fmt.Errorf("%s: %w", res.Errors[0].Key, err)
}
}
@ -98,7 +98,7 @@ func (v *Identity) Login(user, password string) error {
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", string(res.Errors[0].Key), err)
err = fmt.Errorf("%s: %w", res.Errors[0].Key, err)
}
}
}

View file

@ -111,7 +111,7 @@ func (ct *Timestamp) UnmarshalJSON(data []byte) error {
t, err := time.Parse(timeFormat, s)
if err == nil {
(*ct).Time = t
ct.Time = t
}
return err

View file

@ -2,6 +2,7 @@ package vehicle
import (
"fmt"
"net"
"net/http"
"net/http/cookiejar"
"net/url"
@ -102,31 +103,31 @@ func (v *Ovms) loginToServer() (err error) {
return err
}
func (v *Ovms) uri(path string) string {
return fmt.Sprintf("https://%s/api/%s/%s", net.JoinHostPort(v.server, "6869"), path, v.vehicleId)
}
func (v *Ovms) connectRequest() (ovmsConnectResponse, error) {
uri := fmt.Sprintf("https://%s:6869/api/vehicle/%s", v.server, v.vehicleId)
var res ovmsConnectResponse
err := v.GetJSON(uri, &res)
err := v.GetJSON(v.uri("vehicle"), &res)
return res, err
}
func (v *Ovms) chargeRequest() (ovmsChargeResponse, error) {
uri := fmt.Sprintf("https://%s:6869/api/charge/%s", v.server, v.vehicleId)
var res ovmsChargeResponse
err := v.GetJSON(uri, &res)
err := v.GetJSON(v.uri("charge"), &res)
return res, err
}
func (v *Ovms) statusRequest() (ovmsStatusResponse, error) {
uri := fmt.Sprintf("https://%s:6869/api/status/%s", v.server, v.vehicleId)
var res ovmsStatusResponse
err := v.GetJSON(uri, &res)
err := v.GetJSON(v.uri("status"), &res)
return res, err
}
func (v *Ovms) locationRequest() (ovmsLocationResponse, error) {
uri := fmt.Sprintf("https://%s:6869/api/location/%s", v.server, v.vehicleId)
var res ovmsLocationResponse
err := v.GetJSON(uri, &res)
err := v.GetJSON(v.uri("location"), &res)
return res, err
}

View file

@ -102,7 +102,6 @@ func newPSA(log *util.Logger, brand, realm, id, secret string, other map[string]
return v.VIN
},
)
if err != nil {
return nil, err
}

View file

@ -58,7 +58,6 @@ func (v *API) request_(uri string, body io.Reader) (Response, error) {
func (v *API) request(uri string, body io.Reader) (Response, error) {
res, err := v.request_(uri, body)
// repeat auth if error
if err != nil {
if err = v.login(); err == nil {

View file

@ -32,7 +32,7 @@ var _ api.Battery = (*Provider)(nil)
// Soc implements the api.Vehicle interface
func (v *Provider) Soc() (float64, error) {
res, err := v.statusG()
return float64(res.Engines.Primary.Level), err
return res.Engines.Primary.Level, err
}
var _ api.ChargeState = (*Provider)(nil)
@ -89,7 +89,7 @@ var _ api.VehicleOdometer = (*Provider)(nil)
// Odometer implements the api.VehicleOdometer interface
func (v *Provider) Odometer() (float64, error) {
res, err := v.statusG()
return float64(res.Measurements.MileageKm), err
return res.Measurements.MileageKm, err
}
var _ api.VehicleClimater = (*Provider)(nil)

View file

@ -55,7 +55,7 @@ func (ct *TimeSecs) UnmarshalJSON(data []byte) error {
if err == nil {
t := time.Unix(i, 0)
(*ct).Time = t
ct.Time = t
}
return err

View file

@ -26,6 +26,8 @@ var acceptable = []string{
func TestTemplates(t *testing.T) {
templates.TestClass(t, templates.Vehicle, func(t *testing.T, values map[string]any) {
t.Helper()
if _, err := NewFromConfig("template", values); err != nil && !test.Acceptable(err, acceptable) {
t.Log(values)
t.Error(err)

View file

@ -94,7 +94,7 @@ func (ct *Timestamp) UnmarshalJSON(data []byte) error {
t, err := time.Parse(timeFormat, s)
if err == nil {
(*ct).Time = t
ct.Time = t
}
return err

View file

@ -331,7 +331,7 @@ func (ct *Timestamp) UnmarshalJSON(data []byte) error {
t, err := time.Parse(time.RFC3339, s)
if err == nil {
(*ct).Time = t
ct.Time = t
}
return err

View file

@ -133,12 +133,12 @@ func (t *TimedTemperature) UnmarshalJSON(data []byte) error {
err := json.Unmarshal(data, &temp)
if err == nil {
(*t).Timestamp = temp.Timestamp
t.Timestamp = temp.Timestamp
if val, err := strconv.Atoi(string(temp.Content)); err == nil {
(*t).Content = temp2Float(val)
t.Content = temp2Float(val)
} else {
(*t).Content = math.NaN()
t.Content = math.NaN()
}
}