Modbus: unify delay and timeout settings (BC) (#31996)

This commit is contained in:
andig 2026-07-23 17:03:28 +02:00 • committed by GitHub
parent ff2905a366
commit 02a66252ca
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
82 changed files with 410 additions and 369 deletions

View file

@ -151,7 +151,13 @@
/>
<PropertyCollapsible>
<template v-if="advancedParams.length" #advanced>
<template v-if="advancedParams.length || modbus" #advanced>
<ModbusAdvanced
v-if="modbus"
v-model:delay="values['delay']"
v-model:timeout="values['timeout']"
component-id="device"
/>
<PropertyEntry
v-for="param in advancedParams"
:id="`${deviceType}Param${param.Name}`"
@ -208,6 +214,7 @@ import ErrorMessage from "../../Helper/ErrorMessage.vue";
import PropertyEntry from "../PropertyEntry.vue";
import PropertyCollapsible from "../PropertyCollapsible.vue";
import Modbus from "./Modbus.vue";
import ModbusAdvanced from "./ModbusAdvanced.vue";
import DeviceModalActions from "./Actions.vue";
import Markdown from "../Markdown.vue";
import SponsorTokenRequired from "./SponsorTokenRequired.vue";
@ -250,6 +257,7 @@ export default defineComponent({
PropertyEntry,
PropertyCollapsible,
Modbus,
ModbusAdvanced,
DeviceModalActions,
Markdown,
SponsorTokenRequired,

View file

@ -0,0 +1,52 @@
<template>
<FormRow
:id="formId('modbusTimeout')"
:label="$t('config.modbus.timeout')"
:help="$t('config.modbus.timeoutHint')"
>
<PropertyField
:id="formId('modbusTimeout')"
property="timeout"
type="Duration"
class="me-2"
:model-value="timeout"
@update:model-value="(v) => $emit('update:timeout', v)"
/>
</FormRow>
<FormRow
:id="formId('modbusDelay')"
:label="$t('config.modbus.delay')"
:help="$t('config.modbus.delayHint')"
>
<PropertyField
:id="formId('modbusDelay')"
property="delay"
type="Duration"
class="me-2"
:model-value="delay"
@update:model-value="(v) => $emit('update:delay', v)"
/>
</FormRow>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import FormRow from "../FormRow.vue";
import PropertyField from "../PropertyField.vue";
export default defineComponent({
name: "ModbusAdvanced",
components: { FormRow, PropertyField },
props: {
componentId: { type: String, required: true },
delay: [Number, String],
timeout: [Number, String],
},
emits: ["update:delay", "update:timeout"],
methods: {
formId(name: string): string {
return `${name}-${this.componentId}`;
},
},
});
</script>

View file

@ -66,12 +66,12 @@ func NewABBFromConfig(ctx context.Context, other map[string]any) (api.Charger, e
return nil, err
}
return NewABB(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Protocol(), cc.ID)
return NewABB(ctx, cc)
}
// NewABB creates ABB charger
func NewABB(ctx context.Context, uri, device, comset string, baudrate int, proto modbus.Protocol, slaveID uint8) (api.Charger, error) {
conn, err := modbus.NewConnection(ctx, uri, device, comset, baudrate, proto, slaveID)
func NewABB(ctx context.Context, settings modbus.Settings) (api.Charger, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -68,12 +68,12 @@ func NewAblEm4FromConfig(ctx context.Context, other map[string]any) (api.Charger
return nil, err
}
return NewAblEm4(ctx, cc.URI, cc.ID, cc.Connector)
return NewAblEm4(ctx, cc.TcpSettings, cc.Connector)
}
// NewAblEm4 creates an ABL eM4 charger
func NewAblEm4(ctx context.Context, uri string, id uint8, connector uint16) (*AblEm4, error) {
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
func NewAblEm4(ctx context.Context, settings modbus.TcpSettings, connector uint16) (*AblEm4, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -82,33 +82,24 @@ func init() {
// NewABLeMHFromConfig creates a ABLeMH charger from generic config
func NewABLeMHFromConfig(ctx context.Context, other map[string]any) (api.Charger, error) {
cc := struct {
modbus.Settings `mapstructure:",squash"`
Timeout time.Duration
}{
Settings: modbus.Settings{
cc := modbus.Settings{
ID: 1,
},
}
if err := util.DecodeOther(other, &cc); err != nil {
return nil, err
}
return NewABLeMH(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.ID, cc.Timeout)
return NewABLeMH(ctx, cc)
}
// NewABLeMH creates ABLeMH charger
func NewABLeMH(ctx context.Context, uri, device, comset string, baudrate int, slaveID uint8, timeout time.Duration) (api.Charger, error) {
conn, err := modbus.NewConnection(ctx, uri, device, comset, baudrate, modbus.Ascii, slaveID)
func NewABLeMH(ctx context.Context, settings modbus.Settings) (api.Charger, error) {
conn, err := settings.Connection(ctx, modbus.Ascii)
if err != nil {
return nil, err
}
if timeout > 0 {
conn.Timeout(timeout)
}
if !sponsor.IsAuthorized() {
return nil, api.ErrSponsorRequired
}

View file

@ -68,12 +68,12 @@ func NewAlfenFromConfig(ctx context.Context, other map[string]any) (api.Charger,
return nil, err
}
return NewAlfen(ctx, cc.URI, cc.ID)
return NewAlfen(ctx, cc)
}
// NewAlfen creates Alfen charger
func NewAlfen(ctx context.Context, uri string, slaveID uint8) (api.Charger, error) {
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, slaveID)
func NewAlfen(ctx context.Context, settings modbus.TcpSettings) (api.Charger, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -50,24 +50,23 @@ func init() {
func NewAlphatecFromConfig(ctx context.Context, other map[string]any) (api.Charger, error) {
cc := modbus.Settings{
ID: 1,
Delay: 20 * time.Millisecond,
}
if err := util.DecodeOther(other, &cc); err != nil {
return nil, err
}
return NewAlphatec(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Protocol(), cc.ID)
return NewAlphatec(ctx, cc)
}
// NewAlphatec creates Alphatec charger
func NewAlphatec(ctx context.Context, uri, device, comset string, baudrate int, proto modbus.Protocol, slaveID uint8) (api.Charger, error) {
conn, err := modbus.NewConnection(ctx, uri, device, comset, baudrate, proto, slaveID)
func NewAlphatec(ctx context.Context, settings modbus.Settings) (api.Charger, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}
conn.Delay(20 * time.Millisecond)
if !sponsor.IsAuthorized() {
return nil, api.ErrSponsorRequired
}

View file

@ -73,12 +73,12 @@ func NewAlpitronicHYCFromConfig(ctx context.Context, other map[string]any) (api.
return nil, err
}
return NewAlpitronicHYC(ctx, cc.URI, cc.ID, cc.Connector)
return NewAlpitronicHYC(ctx, cc.TcpSettings, cc.Connector)
}
// NewAlpitronicHYC creates Alpitronic charger
func NewAlpitronicHYC(ctx context.Context, uri string, id uint8, connector uint16) (*AlpitronicHYC, error) {
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
func NewAlpitronicHYC(ctx context.Context, settings modbus.TcpSettings, connector uint16) (*AlpitronicHYC, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -110,12 +110,12 @@ func NewAmbiboxFromConfig(ctx context.Context, other map[string]any) (api.Charge
return nil, api.ErrSponsorRequired
}
return NewAmbibox(ctx, cc.URI, cc.ID, cc.Connector)
return NewAmbibox(ctx, cc.TcpSettings, cc.Connector)
}
// NewAmbibox creates an Ambibox charger
func NewAmbibox(ctx context.Context, uri string, id uint8, connector int) (*Ambibox, error) {
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
func NewAmbibox(ctx context.Context, settings modbus.TcpSettings, connector int) (*Ambibox, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -76,12 +76,12 @@ func NewAmperfiedFromConfig(ctx context.Context, other map[string]any) (api.Char
return nil, err
}
return NewAmperfied(ctx, cc.URI, cc.ID, cc.Phases1p3p)
return NewAmperfied(ctx, cc.TcpSettings, cc.Phases1p3p)
}
// NewAmperfied creates Amperfied charger
func NewAmperfied(ctx context.Context, uri string, slaveID uint8, phases bool) (api.Charger, error) {
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, slaveID)
func NewAmperfied(ctx context.Context, settings modbus.TcpSettings, phases bool) (api.Charger, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -109,12 +109,12 @@ func NewBenderCCFromConfig(ctx context.Context, other map[string]any) (api.Charg
return nil, err
}
return NewBenderCC(ctx, cc.URI, cc.ID, cc.Cache)
return NewBenderCC(ctx, cc.TcpSettings, cc.Cache)
}
// NewBenderCC creates BenderCC charger
func NewBenderCC(ctx context.Context, uri string, id uint8, cache time.Duration) (api.Charger, error) {
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
func NewBenderCC(ctx context.Context, settings modbus.TcpSettings, cache time.Duration) (api.Charger, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}
@ -174,7 +174,7 @@ func NewBenderCC(ctx context.Context, uri string, id uint8, cache time.Duration)
implement.Has(wb, implement.PhaseGetter(wb.getPhasesMennekes))
} else {
// check feature semp phase switching
if wb.supportsSEMPPhaseSwitching(uri, cache) {
if wb.supportsSEMPPhaseSwitching(settings.URI, cache) {
// set initial SEMP power limit to max so modbus control from 6 to 16 A is possible
if err := wb.semp.conn.SendDeviceControl(wb.semp.deviceID, 0xffff); err == nil {
implement.Has(wb, implement.PhaseSwitcher(wb.phases1p3pSEMP))

View file

@ -67,14 +67,14 @@ func NewCfosPowerBrainFromConfig(ctx context.Context, other map[string]any) (api
return nil, err
}
return NewCfosPowerBrain(ctx, cc.URI, cc.ID)
return NewCfosPowerBrain(ctx, cc)
}
// NewCfosPowerBrain creates a cFos charger
func NewCfosPowerBrain(ctx context.Context, uri string, id uint8) (api.Charger, error) {
uri = util.DefaultPort(uri, 4701)
func NewCfosPowerBrain(ctx context.Context, settings modbus.TcpSettings) (api.Charger, error) {
settings.URI = util.DefaultPort(settings.URI, 4701)
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -77,12 +77,12 @@ func NewChargeXFromConfig(ctx context.Context, other map[string]any) (api.Charge
return nil, err
}
return NewChargeX(ctx, cc.URI, cc.ID, cc.Connector)
return NewChargeX(ctx, cc.TcpSettings, cc.Connector)
}
// NewChargeX creates ChargeX charger
func NewChargeX(ctx context.Context, uri string, id uint8, connector uint16) (api.Charger, error) {
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
func NewChargeX(ctx context.Context, settings modbus.TcpSettings, connector uint16) (api.Charger, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -76,12 +76,12 @@ func NewCompleoFromConfig(ctx context.Context, other map[string]any) (api.Charge
return nil, err
}
return NewCompleo(ctx, cc.URI, cc.ID, cc.Connector)
return NewCompleo(ctx, cc.TcpSettings, cc.Connector)
}
// NewCompleo creates Compleo charger
func NewCompleo(ctx context.Context, uri string, slaveID uint8, connector uint16) (api.Charger, error) {
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, slaveID)
func NewCompleo(ctx context.Context, settings modbus.TcpSettings, connector uint16) (api.Charger, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -66,12 +66,12 @@ func NewDadapowerFromConfig(ctx context.Context, other map[string]any) (api.Char
return nil, err
}
return NewDadapower(ctx, cc.URI, cc.ID)
return NewDadapower(ctx, cc)
}
// NewDadapower creates a Dadapower charger
func NewDadapower(ctx context.Context, uri string, id uint8) (*Dadapower, error) {
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
func NewDadapower(ctx context.Context, settings modbus.TcpSettings) (*Dadapower, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}
@ -94,8 +94,8 @@ func NewDadapower(ctx context.Context, uri string, id uint8) (*Dadapower, error)
}
// The charging station may have multiple charging ports - use offset for register addresses for each port
if id > 1 {
wb.regOffset = (uint16(id) - 1) * 1000
if settings.ID > 1 {
wb.regOffset = (uint16(settings.ID) - 1) * 1000
}
go wb.heartbeat(ctx)

View file

@ -87,12 +87,12 @@ func NewDaheimLadenFromConfig(ctx context.Context, other map[string]any) (api.Ch
return nil, err
}
return NewDaheimLaden(ctx, cc.URI, cc.ID, cc.Phases1p3p)
return NewDaheimLaden(ctx, cc.TcpSettings, cc.Phases1p3p)
}
// NewDaheimLaden creates DaheimLaden charger
func NewDaheimLaden(ctx context.Context, uri string, id uint8, phases bool) (api.Charger, error) {
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
func NewDaheimLaden(ctx context.Context, settings modbus.TcpSettings, phases bool) (api.Charger, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -96,12 +96,12 @@ func NewDeltaFromConfig(ctx context.Context, other map[string]any) (api.Charger,
return nil, err
}
return NewDelta(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Settings.Protocol(), cc.ID, cc.Connector)
return NewDelta(ctx, cc.Settings, cc.Connector)
}
// NewDelta creates Delta charger
func NewDelta(ctx context.Context, uri, device, comset string, baudrate int, proto modbus.Protocol, slaveID uint8, connector uint16) (api.Charger, error) {
conn, err := modbus.NewConnection(ctx, uri, device, comset, baudrate, proto, slaveID)
func NewDelta(ctx context.Context, settings modbus.Settings, connector uint16) (api.Charger, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -77,12 +77,12 @@ func NewEgoFromConfig(ctx context.Context, other map[string]any) (api.Charger, e
return nil, err
}
return NewEgo(ctx, &cc.embed, cc.URI, cc.ID)
return NewEgo(ctx, &cc.embed, cc.TcpSettings)
}
// NewEgo creates E.G.O. Smart Heater charger
func NewEgo(ctx context.Context, embed *embed, uri string, slaveID uint8) (api.Charger, error) {
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, slaveID)
func NewEgo(ctx context.Context, embed *embed, settings modbus.TcpSettings) (api.Charger, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -64,7 +64,10 @@ func NewEm2GoDuoFromConfig(ctx context.Context, other map[string]any) (api.Charg
modbus.TcpSettings `mapstructure:",squash"`
Connector int
}{
TcpSettings: modbus.TcpSettings{ID: 255},
TcpSettings: modbus.TcpSettings{
ID: 255,
Delay: 60 * time.Millisecond,
},
Connector: 1,
}
@ -72,25 +75,20 @@ func NewEm2GoDuoFromConfig(ctx context.Context, other map[string]any) (api.Charg
return nil, err
}
return NewEm2GoDuo(ctx, cc.URI, cc.ID, cc.Connector)
return NewEm2GoDuo(ctx, cc.TcpSettings, cc.Connector)
}
// NewEm2GoDuo creates Em2GoDuo charger
func NewEm2GoDuo(ctx context.Context, uri string, slaveID uint8, connector int) (api.Charger, error) {
func NewEm2GoDuo(ctx context.Context, settings modbus.TcpSettings, connector int) (api.Charger, error) {
if connector < 1 || connector > 2 {
return nil, fmt.Errorf("invalid connector %d, must be 1 or 2", connector)
}
uri = util.DefaultPort(uri, 502)
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, slaveID)
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}
// Add delay of 60 milliseconds between requests
conn.Delay(60 * time.Millisecond)
log := util.NewLogger("em2go-duo")
conn.Logger(log.TRACE)

View file

@ -78,28 +78,26 @@ func NewEm2GoFromConfig(ctx context.Context, other map[string]any) (api.Charger,
modbus.TcpSettings `mapstructure:",squash"`
Connector int // TODO remove deprecated
}{
TcpSettings: modbus.TcpSettings{ID: 255},
TcpSettings: modbus.TcpSettings{
ID: 255,
Delay: 60 * time.Millisecond,
},
}
if err := util.DecodeOther(other, &cc); err != nil {
return nil, err
}
return NewEm2Go(ctx, cc.URI, cc.ID)
return NewEm2Go(ctx, cc.TcpSettings)
}
// NewEm2Go creates Em2Go charger
func NewEm2Go(ctx context.Context, uri string, slaveID uint8) (api.Charger, error) {
uri = util.DefaultPort(uri, 502)
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, slaveID)
func NewEm2Go(ctx context.Context, settings modbus.TcpSettings) (api.Charger, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}
// Add delay of 60 milliseconds between requests
conn.Delay(60 * time.Millisecond)
log := util.NewLogger("em2go")
conn.Logger(log.TRACE)

View file

@ -69,14 +69,12 @@ func NewEnovatesFromConfig(ctx context.Context, other map[string]any) (api.Charg
return nil, err
}
return NewEnovates(ctx, cc.URI, cc.ID)
return NewEnovates(ctx, cc)
}
// NewEnovates creates an Enovates charger
func NewEnovates(ctx context.Context, uri string, slaveID uint8) (api.Charger, error) {
uri = util.DefaultPort(uri, 502)
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, slaveID)
func NewEnovates(ctx context.Context, settings modbus.TcpSettings) (api.Charger, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -60,12 +60,12 @@ func NewEProWallboxFromConfig(ctx context.Context, other map[string]any) (api.Ch
return nil, err
}
return NewEProWallbox(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Protocol(), cc.ID)
return NewEProWallbox(ctx, cc)
}
// NewEProWallbox creates eProWallbox charger
func NewEProWallbox(ctx context.Context, uri, device, comset string, baudrate int, proto modbus.Protocol, slaveID uint8) (api.Charger, error) {
conn, err := modbus.NewConnection(ctx, uri, device, comset, baudrate, proto, slaveID)
func NewEProWallbox(ctx context.Context, settings modbus.Settings) (api.Charger, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -77,7 +77,7 @@ func NewEtekFromConfig(ctx context.Context, other map[string]any) (api.Charger,
return nil, err
}
wb, err := NewEtek(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Protocol(), cc.ID)
wb, err := NewEtek(ctx, cc)
if err != nil {
return nil, err
}
@ -113,8 +113,8 @@ func NewEtekFromConfig(ctx context.Context, other map[string]any) (api.Charger,
}
// NewEtek creates an ETEK EKEPC2 charger
func NewEtek(ctx context.Context, uri, device, comset string, baudrate int, proto modbus.Protocol, slaveID uint8) (*Etek, error) {
conn, err := modbus.NewConnection(ctx, uri, device, comset, baudrate, proto, slaveID)
func NewEtek(ctx context.Context, settings modbus.Settings) (*Etek, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -79,12 +79,12 @@ func NewEtrelFromConfig(ctx context.Context, other map[string]any) (api.Charger,
return nil, err
}
return NewEtrel(ctx, cc.URI, cc.ID, cc.Connector)
return NewEtrel(ctx, cc.TcpSettings, cc.Connector)
}
// NewEtrel creates a Etrel charger
func NewEtrel(ctx context.Context, uri string, id uint8, connector int) (*Etrel, error) {
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
func NewEtrel(ctx context.Context, settings modbus.TcpSettings, connector int) (*Etrel, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -39,26 +39,26 @@ func NewEvseDINFromConfig(ctx context.Context, other map[string]any) (api.Charge
Baudrate: 9600,
Comset: "8N1",
ID: 1,
Delay: 200 * time.Millisecond,
}
if err := util.DecodeOther(other, &cc); err != nil {
return nil, err
}
return NewEvseDIN(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Protocol(), cc.ID)
return NewEvseDIN(ctx, cc)
}
// NewEvseDIN creates EVSE DIN charger
func NewEvseDIN(ctx context.Context, uri, device, comset string, baudrate int, proto modbus.Protocol, slaveID uint8) (api.Charger, error) {
func NewEvseDIN(ctx context.Context, settings modbus.Settings) (api.Charger, error) {
log := util.NewLogger("evse")
conn, err := modbus.NewConnection(ctx, uri, device, comset, baudrate, proto, slaveID)
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}
conn.Logger(log.TRACE)
conn.Delay(200 * time.Millisecond)
evse := &EvseDIN{
Caps: implement.New(),

View file

@ -71,24 +71,18 @@ func init() {
// NewGoodWeFromConfig creates a GoodWe wallbox charger from generic config.
func NewGoodWeFromConfig(ctx context.Context, other map[string]any) (api.Charger, error) {
cc := struct {
modbus.TcpSettings `mapstructure:",squash"`
}{
TcpSettings: modbus.TcpSettings{ID: 247},
}
cc := modbus.TcpSettings{ID: 247}
if err := util.DecodeOther(other, &cc); err != nil {
return nil, err
}
return NewGoodWe(ctx, cc.URI, cc.ID)
return NewGoodWe(ctx, cc)
}
// NewGoodWe creates a GoodWe wallbox charger.
func NewGoodWe(ctx context.Context, uri string, slaveID uint8) (api.Charger, error) {
uri = util.DefaultPort(uri, 502)
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, slaveID)
func NewGoodWe(ctx context.Context, settings modbus.TcpSettings) (api.Charger, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -64,18 +64,20 @@ func init() {
func NewHeidelbergECFromConfig(ctx context.Context, other map[string]any) (api.Charger, error) {
cc := modbus.Settings{
ID: 1,
// https://github.com/evcc-io/evcc/issues/15437
Delay: 100 * time.Millisecond,
}
if err := util.DecodeOther(other, &cc); err != nil {
return nil, err
}
return NewHeidelbergEC(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Protocol(), cc.ID)
return NewHeidelbergEC(ctx, cc)
}
// NewHeidelbergEC creates HeidelbergEC charger
func NewHeidelbergEC(ctx context.Context, uri, device, comset string, baudrate int, proto modbus.Protocol, slaveID uint8) (api.Charger, error) {
conn, err := modbus.NewConnection(ctx, uri, device, comset, baudrate, proto, slaveID)
func NewHeidelbergEC(ctx context.Context, settings modbus.Settings) (api.Charger, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}
@ -93,9 +95,6 @@ func NewHeidelbergEC(ctx context.Context, uri, device, comset string, baudrate i
current: 60, // assume min current
}
// https://github.com/evcc-io/evcc/issues/15437
conn.Delay(100 * time.Millisecond)
// disable standby to prevent comm loss
if err := wb.set(hecRegStandbyConfig, hecStandbyDisabled); err != nil {
return nil, err

View file

@ -69,12 +69,12 @@ func NewHesotecFromConfig(ctx context.Context, other map[string]any) (api.Charge
return nil, err
}
return NewHesotec(ctx, cc.URI, cc.ID)
return NewHesotec(ctx, cc)
}
// NewHesotec creates Hesotec charger
func NewHesotec(ctx context.Context, uri string, id uint8) (api.Charger, error) {
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
func NewHesotec(ctx context.Context, settings modbus.TcpSettings) (api.Charger, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -67,7 +67,7 @@ func NewInnogyFromConfig(ctx context.Context, other map[string]any) (api.Charger
return nil, err
}
wb, err := NewInnogy(ctx, cc.URI, cc.ID)
wb, err := NewInnogy(ctx, cc)
if err != nil {
return nil, err
}
@ -83,8 +83,8 @@ func NewInnogyFromConfig(ctx context.Context, other map[string]any) (api.Charger
}
// NewInnogy creates a Innogy charger
func NewInnogy(ctx context.Context, uri string, id uint8) (*Innogy, error) {
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
func NewInnogy(ctx context.Context, settings modbus.TcpSettings) (*Innogy, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -167,7 +167,7 @@ func NewKathreinFromConfig(ctx context.Context, other map[string]any) (api.Charg
return nil, err
}
wb, err := NewKathrein(ctx, cc.URI, cc.ID)
wb, err := NewKathrein(ctx, cc)
if err != nil {
return nil, err
}
@ -176,8 +176,8 @@ func NewKathreinFromConfig(ctx context.Context, other map[string]any) (api.Charg
}
// NewKathrein creates Kathrein charger
func NewKathrein(ctx context.Context, uri string, id uint8) (*Kathrein, error) {
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
func NewKathrein(ctx context.Context, settings modbus.TcpSettings) (*Kathrein, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -89,7 +89,7 @@ func NewKebaFromConfig(ctx context.Context, other map[string]any) (api.Charger,
return nil, err
}
wb, err := NewKeba(ctx, cc.embed, cc.URI, cc.ID)
wb, err := NewKeba(ctx, cc.embed, cc.TcpSettings)
if err != nil {
return nil, err
}
@ -162,8 +162,8 @@ func NewKebaFromConfig(ctx context.Context, other map[string]any) (api.Charger,
}
// NewKeba creates a new charger
func NewKeba(ctx context.Context, embed embed, uri string, slaveID uint8) (*Keba, error) {
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, slaveID)
func NewKeba(ctx context.Context, embed embed, settings modbus.TcpSettings) (*Keba, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -70,12 +70,12 @@ func NewKSEFromConfig(ctx context.Context, other map[string]any) (api.Charger, e
return nil, err
}
return NewKSE(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.ID)
return NewKSE(ctx, cc)
}
// NewKSE creates KSE charger
func NewKSE(ctx context.Context, uri, device, comset string, baudrate int, slaveID uint8) (api.Charger, error) {
conn, err := modbus.NewConnection(ctx, uri, device, comset, baudrate, modbus.Rtu, slaveID)
func NewKSE(ctx context.Context, settings modbus.Settings) (api.Charger, error) {
conn, err := settings.Connection(ctx, modbus.Rtu)
if err != nil {
return nil, err
}

View file

@ -71,35 +71,26 @@ func init() {
// NewMennekesCompactFromConfig creates a new Mennekes ModbusTCP charger
func NewMennekesCompactFromConfig(ctx context.Context, other map[string]any) (api.Charger, error) {
cc := struct {
modbus.Settings `mapstructure:",squash"`
Timeout time.Duration
}{
Settings: modbus.Settings{
cc := modbus.Settings{
Baudrate: 57600,
Comset: "8N2",
ID: 50,
},
}
if err := util.DecodeOther(other, &cc); err != nil {
return nil, err
}
return NewMennekesCompact(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Settings.Protocol(), cc.ID, cc.Timeout)
return NewMennekesCompact(ctx, cc)
}
// NewMennekesCompact creates Mennekes charger
func NewMennekesCompact(ctx context.Context, uri, device, comset string, baudrate int, proto modbus.Protocol, slaveID uint8, timeout time.Duration) (api.Charger, error) {
conn, err := modbus.NewConnection(ctx, uri, device, comset, baudrate, proto, slaveID)
func NewMennekesCompact(ctx context.Context, settings modbus.Settings) (api.Charger, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}
if timeout > 0 {
conn.Timeout(timeout)
}
if !sponsor.IsAuthorized() {
return nil, api.ErrSponsorRequired
}

View file

@ -62,14 +62,12 @@ func NewMennekesHcc3FromConfig(ctx context.Context, other map[string]any) (api.C
return nil, err
}
return NewMennekesHcc3(ctx, cc.URI, cc.ID)
return NewMennekesHcc3(ctx, cc)
}
// NewMennekesHcc3 creates Mennekes HCC3 charger
func NewMennekesHcc3(ctx context.Context, uri string, slaveID uint8) (api.Charger, error) {
uri = util.DefaultPort(uri, 502)
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, slaveID)
func NewMennekesHcc3(ctx context.Context, settings modbus.TcpSettings) (api.Charger, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -97,12 +97,12 @@ func newMyPvFromConfig(ctx context.Context, name string, other map[string]any, s
return nil, err
}
return NewMyPv(ctx, name, cc.URI, cc.ID, cc.TempSource, statusC, cc.Scale)
return NewMyPv(ctx, name, cc.TcpSettings, cc.TempSource, statusC, cc.Scale)
}
// NewMyPv creates myPV AC Elwa 2 or Thor charger
func NewMyPv(ctx context.Context, name, uri string, slaveID uint8, tempSource int, statusC uint16, scale float64) (api.Charger, error) {
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, slaveID)
func NewMyPv(ctx context.Context, name string, settings modbus.TcpSettings, tempSource int, statusC uint16, scale float64) (api.Charger, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -84,7 +84,7 @@ func NewNRGKickGen2FromConfig(ctx context.Context, other map[string]any) (api.Ch
return nil, err
}
nrg, err := NewNRGKickGen2(ctx, cc.URI, cc.ID)
nrg, err := NewNRGKickGen2(ctx, cc.TcpSettings)
if err != nil {
return nil, err
}
@ -102,8 +102,8 @@ func NewNRGKickGen2FromConfig(ctx context.Context, other map[string]any) (api.Ch
}
// NewNRGKickGen2 creates NRGKickGen2 charger
func NewNRGKickGen2(ctx context.Context, uri string, slaveID uint8) (*NRGKickGen2, error) {
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, slaveID)
func NewNRGKickGen2(ctx context.Context, settings modbus.TcpSettings) (*NRGKickGen2, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -40,12 +40,12 @@ func NewOboFromConfig(ctx context.Context, other map[string]any) (api.Charger, e
return nil, err
}
return NewObo(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Protocol(), cc.ID)
return NewObo(ctx, cc)
}
// NewObo creates OBO Bettermann charger
func NewObo(ctx context.Context, uri, device, comset string, baudrate int, proto modbus.Protocol, slaveID uint8) (api.Charger, error) {
conn, err := modbus.NewConnection(ctx, uri, device, comset, baudrate, proto, slaveID)
func NewObo(ctx context.Context, settings modbus.Settings) (api.Charger, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -61,7 +61,7 @@ func NewOpenWB20FromConfig(ctx context.Context, other map[string]any) (api.Charg
return nil, err
}
wb, err := NewOpenWB20(ctx, cc.URI, cc.ID, cc.Connector)
wb, err := NewOpenWB20(ctx, cc.TcpSettings, cc.Connector)
if err != nil {
return nil, err
}
@ -78,10 +78,10 @@ func NewOpenWB20FromConfig(ctx context.Context, other map[string]any) (api.Charg
}
// NewOpenWB20 creates OpenWB20 charger
func NewOpenWB20(ctx context.Context, uri string, slaveID uint8, connector uint16) (*OpenWB20, error) {
uri = util.DefaultPort(uri, 1502)
func NewOpenWB20(ctx context.Context, settings modbus.TcpSettings, connector uint16) (*OpenWB20, error) {
settings.URI = util.DefaultPort(settings.URI, 1502)
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, slaveID)
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -59,6 +59,7 @@ func NewOpenWbNativeFromConfig(ctx context.Context, other map[string]any) (api.C
Baudrate: 9600,
Comset: "8N1",
ID: 1,
Delay: 200 * time.Millisecond,
},
}
@ -73,14 +74,14 @@ func NewOpenWbNativeFromConfig(ctx context.Context, other map[string]any) (api.C
return nil, fmt.Errorf("invalid cpwait value: %v, needs to be greater than %s", cc.CpWait, minCpWaitTime)
}
return NewOpenWbNative(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Protocol(), cc.ID, cc.Phases1p3p, cc.RfId, cc.CpWait, cc.Connector, cc.Chip)
return NewOpenWbNative(ctx, cc.Settings, cc.Phases1p3p, cc.RfId, cc.CpWait, cc.Connector, cc.Chip)
}
// NewOpenWbNative creates OpenWbNative charger
func NewOpenWbNative(ctx context.Context, uri, device, comset string, baudrate int, proto modbus.Protocol, slaveID uint8, hasPhases1p3p bool, rfIdVidPid string, cpWait time.Duration, connector int, chip string) (api.Charger, error) {
func NewOpenWbNative(ctx context.Context, settings modbus.Settings, hasPhases1p3p bool, rfIdVidPid string, cpWait time.Duration, connector int, chip string) (api.Charger, error) {
log := util.NewLogger("openwb-native")
evse, err := NewEvseDIN(ctx, uri, device, comset, baudrate, proto, slaveID)
evse, err := NewEvseDIN(ctx, settings)
if err != nil {
return nil, err
}

View file

@ -82,12 +82,12 @@ func NewPeblarFromConfig(ctx context.Context, other map[string]any) (api.Charger
return nil, err
}
return NewPeblar(ctx, cc.URI, cc.ID)
return NewPeblar(ctx, cc)
}
// NewPeblar creates Peblar charger
func NewPeblar(ctx context.Context, uri string, id uint8) (api.Charger, error) {
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
func NewPeblar(ctx context.Context, settings modbus.TcpSettings) (api.Charger, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -65,7 +65,7 @@ func NewPhoenixCharxFromConfig(ctx context.Context, other map[string]any) (api.C
return nil, err
}
wb, err := NewPhoenixCharx(ctx, cc.URI, cc.ID, cc.Connector)
wb, err := NewPhoenixCharx(ctx, cc.TcpSettings, cc.Connector)
if err != nil {
return nil, err
}
@ -86,8 +86,8 @@ func NewPhoenixCharxFromConfig(ctx context.Context, other map[string]any) (api.C
}
// NewPhoenixCharx creates a Phoenix charger
func NewPhoenixCharx(ctx context.Context, uri string, id uint8, connector uint16) (*PhoenixCharx, error) {
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
func NewPhoenixCharx(ctx context.Context, settings modbus.TcpSettings, connector uint16) (*PhoenixCharx, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -46,7 +46,7 @@ func NewPhoenixEMEthFromConfig(ctx context.Context, other map[string]any) (api.C
return nil, err
}
wb, err := NewPhoenixEMEth(ctx, cc.URI, cc.ID)
wb, err := NewPhoenixEMEth(ctx, cc)
if err != nil {
return nil, err
}
@ -63,8 +63,8 @@ func NewPhoenixEMEthFromConfig(ctx context.Context, other map[string]any) (api.C
}
// NewPhoenixEMEth creates a Phoenix charger
func NewPhoenixEMEth(ctx context.Context, uri string, slaveID uint8) (*PhoenixEMEth, error) {
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, slaveID)
func NewPhoenixEMEth(ctx context.Context, settings modbus.TcpSettings) (*PhoenixEMEth, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -79,12 +79,12 @@ func NewPhoenixEVEthFromConfig(ctx context.Context, other map[string]any) (api.C
return nil, err
}
return NewPhoenixEVEth(ctx, cc.URI, cc.ID, cc.MilliCurrentSupported)
return NewPhoenixEVEth(ctx, cc.TcpSettings, cc.MilliCurrentSupported)
}
// NewPhoenixEVEth creates a PhoenixEVEth charger
func NewPhoenixEVEth(ctx context.Context, uri string, slaveID uint8, milliCurrentSupported bool) (api.Charger, error) {
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, slaveID)
func NewPhoenixEVEth(ctx context.Context, settings modbus.TcpSettings, milliCurrentSupported bool) (api.Charger, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -35,12 +35,12 @@ func NewPhoenixEVSerFromConfig(ctx context.Context, other map[string]any) (api.C
return nil, err
}
return NewPhoenixEVSer(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Protocol(), cc.ID)
return NewPhoenixEVSer(ctx, cc)
}
// NewPhoenixEVSer creates a Phoenix charger
func NewPhoenixEVSer(ctx context.Context, uri, device, comset string, baudrate int, proto modbus.Protocol, id uint8) (*PhoenixEVSer, error) {
conn, err := modbus.NewConnection(ctx, uri, device, comset, baudrate, proto, id)
func NewPhoenixEVSer(ctx context.Context, settings modbus.Settings) (*PhoenixEVSer, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -21,7 +21,6 @@ import (
"context"
"encoding/binary"
"fmt"
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/util"
@ -55,7 +54,6 @@ func NewPrachtAlphaFromConfig(ctx context.Context, other map[string]any) (api.Ch
cc := struct {
Connector uint16
modbus.Settings `mapstructure:",squash"`
Timeout time.Duration
}{
Connector: 1,
Settings: modbus.Settings{
@ -67,20 +65,16 @@ func NewPrachtAlphaFromConfig(ctx context.Context, other map[string]any) (api.Ch
return nil, err
}
return NewPrachtAlpha(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Settings.Protocol(), cc.ID, cc.Timeout, cc.Connector)
return NewPrachtAlpha(ctx, cc.Settings, cc.Connector)
}
// NewPrachtAlpha creates PrachtAlpha charger
func NewPrachtAlpha(ctx context.Context, uri, device, comset string, baudrate int, proto modbus.Protocol, slaveID uint8, timeout time.Duration, vehicle uint16) (api.Charger, error) {
conn, err := modbus.NewConnection(ctx, uri, device, comset, baudrate, proto, slaveID)
func NewPrachtAlpha(ctx context.Context, settings modbus.Settings, vehicle uint16) (api.Charger, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}
if timeout > 0 {
conn.Timeout(timeout)
}
if !sponsor.IsAuthorized() {
return nil, api.ErrSponsorRequired
}

View file

@ -63,7 +63,7 @@ func NewPulsaresFromConfig(ctx context.Context, other map[string]any) (api.Charg
return nil, err
}
wb, err := NewPulsares(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Protocol(), cc.ID)
wb, err := NewPulsares(ctx, cc)
if err != nil {
return nil, err
}
@ -76,8 +76,8 @@ func NewPulsaresFromConfig(ctx context.Context, other map[string]any) (api.Charg
}
// NewPulsares creates Pulsares charger
func NewPulsares(ctx context.Context, uri, device, comset string, baudrate int, proto modbus.Protocol, slaveID uint8) (*Pulsares, error) {
conn, err := modbus.NewConnection(ctx, uri, device, comset, baudrate, proto, slaveID)
func NewPulsares(ctx context.Context, settings modbus.Settings) (*Pulsares, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -62,12 +62,12 @@ func NewRaedianFromConfig(ctx context.Context, other map[string]any) (api.Charge
return nil, err
}
return NewRaedian(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Protocol(), cc.ID)
return NewRaedian(ctx, cc)
}
// NewRaedian creates Raedian charger
func NewRaedian(ctx context.Context, uri, device, comset string, baudrate int, proto modbus.Protocol, slaveID uint8) (*Raedian, error) {
conn, err := modbus.NewConnection(ctx, uri, device, comset, baudrate, proto, slaveID)
func NewRaedian(ctx context.Context, settings modbus.Settings) (*Raedian, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -71,12 +71,12 @@ func NewSchneiderV3FromConfig(ctx context.Context, other map[string]any) (api.Ch
return nil, err
}
return NewSchneiderV3(ctx, cc.URI, cc.ID)
return NewSchneiderV3(ctx, cc)
}
// NewSchneiderV3 creates Schneider charger
func NewSchneiderV3(ctx context.Context, uri string, id uint8) (api.Charger, error) {
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
func NewSchneiderV3(ctx context.Context, settings modbus.TcpSettings) (api.Charger, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -57,12 +57,12 @@ func NewSigenergyFromConfig(ctx context.Context, other map[string]any) (api.Char
return nil, err
}
return NewSigenergy(ctx, cc.URI, cc.ID)
return NewSigenergy(ctx, cc)
}
// NewSigenergy creates a new charger
func NewSigenergy(ctx context.Context, uri string, slaveID uint8) (*Sigenergy, error) {
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, slaveID)
func NewSigenergy(ctx context.Context, settings modbus.TcpSettings) (*Sigenergy, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -79,12 +79,12 @@ func NewsmartEVSEFromConfig(ctx context.Context, other map[string]any) (api.Char
return nil, err
}
return NewsmartEVSE(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Protocol(), cc.ID)
return NewsmartEVSE(ctx, cc)
}
// NewsmartEVSE creates a new charger
func NewsmartEVSE(ctx context.Context, uri, device, comset string, baudrate int, proto modbus.Protocol, slaveID uint8) (*smartEVSE, error) {
conn, err := modbus.NewConnection(ctx, uri, device, comset, baudrate, proto, slaveID)
func NewsmartEVSE(ctx context.Context, settings modbus.Settings) (*smartEVSE, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -96,12 +96,12 @@ func NewSolaxFromConfig(ctx context.Context, other map[string]any, isLegacyHw bo
return nil, err
}
return NewSolax(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Protocol(), cc.ID, isLegacyHw)
return NewSolax(ctx, cc, isLegacyHw)
}
// NewSolax creates Solax charger
func NewSolax(ctx context.Context, uri, device, comset string, baudrate int, proto modbus.Protocol, id uint8, isLegacyHw bool) (api.Charger, error) {
conn, err := modbus.NewConnection(ctx, uri, device, comset, baudrate, proto, id)
func NewSolax(ctx context.Context, settings modbus.Settings, isLegacyHw bool) (api.Charger, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -77,12 +77,12 @@ func NewSungrowFromConfig(ctx context.Context, other map[string]any) (api.Charge
return nil, err
}
return NewSungrow(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Protocol(), cc.ID)
return NewSungrow(ctx, cc)
}
// NewSungrow creates Sungrow charger
func NewSungrow(ctx context.Context, uri, device, comset string, baudrate int, proto modbus.Protocol, id uint8) (api.Charger, error) {
conn, err := modbus.NewConnection(ctx, uri, device, comset, baudrate, proto, id)
func NewSungrow(ctx context.Context, settings modbus.Settings) (api.Charger, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -69,12 +69,12 @@ func NewVersichargeFromConfig(ctx context.Context, other map[string]any) (api.Ch
return nil, err
}
return NewVersicharge(ctx, cc.URI, cc.ID)
return NewVersicharge(ctx, cc)
}
// NewVersicharge creates a Versicharge charger
func NewVersicharge(ctx context.Context, uri string, id uint8) (*Versicharge, error) {
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
func NewVersicharge(ctx context.Context, settings modbus.TcpSettings) (*Versicharge, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -81,12 +81,12 @@ func NewVestelFromConfig(ctx context.Context, other map[string]any) (api.Charger
return nil, err
}
return NewVestel(ctx, cc.URI, cc.ID)
return NewVestel(ctx, cc)
}
// NewVestel creates a Vestel charger
func NewVestel(ctx context.Context, uri string, id uint8) (api.Charger, error) {
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
func NewVestel(ctx context.Context, settings modbus.TcpSettings) (api.Charger, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -98,12 +98,12 @@ func NewVictronFromConfig(ctx context.Context, other map[string]any, regs victro
return nil, err
}
return NewVictron(ctx, cc.URI, cc.ID, regs)
return NewVictron(ctx, cc, regs)
}
// NewVictron creates Victron charger
func NewVictron(ctx context.Context, uri string, slaveID uint8, regs victronRegs) (api.Charger, error) {
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, slaveID)
func NewVictron(ctx context.Context, settings modbus.TcpSettings, regs victronRegs) (api.Charger, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -61,24 +61,20 @@ func init() {
// NewVoltieFromConfig creates a Voltie charger from generic config
func NewVoltieFromConfig(ctx context.Context, other map[string]any) (api.Charger, error) {
cc := struct {
modbus.TcpSettings `mapstructure:",squash"`
}{
TcpSettings: modbus.TcpSettings{
cc := modbus.TcpSettings{
ID: 1,
},
}
if err := util.DecodeOther(other, &cc); err != nil {
return nil, err
}
return NewVoltie(ctx, cc.URI, cc.ID)
return NewVoltie(ctx, cc)
}
// NewVoltie creates a Voltie charger
func NewVoltie(ctx context.Context, uri string, slaveID uint8) (*Voltie, error) {
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, slaveID)
func NewVoltie(ctx context.Context, settings modbus.TcpSettings) (*Voltie, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -65,12 +65,12 @@ func NewWebastoNextFromConfig(ctx context.Context, other map[string]any) (api.Ch
return nil, err
}
return NewWebastoNext(ctx, cc.URI, cc.ID)
return NewWebastoNext(ctx, cc)
}
// NewWebastoNext creates WebastoNext charger
func NewWebastoNext(ctx context.Context, uri string, id uint8) (api.Charger, error) {
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
func NewWebastoNext(ctx context.Context, settings modbus.TcpSettings) (api.Charger, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -69,12 +69,12 @@ func NewWeidmüllerFromConfig(ctx context.Context, other map[string]any) (api.Ch
return nil, err
}
return NewWeidmüller(ctx, cc.URI, cc.ID)
return NewWeidmüller(ctx, cc)
}
// NewWeidmüller creates Weidmüller charger
func NewWeidmüller(ctx context.Context, uri string, id uint8) (api.Charger, error) {
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
func NewWeidmüller(ctx context.Context, settings modbus.TcpSettings) (api.Charger, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -641,6 +641,8 @@
"connectionHintTcpip": "Das Gerät ist über Netzwerk (LAN/WiFi) erreichbar.",
"connectionValueSerial": "RS485",
"connectionValueTcpip": "Netzwerk",
"delay": "Verzögerung",
"delayHint": "Optionale Wartezeit vor jedem Registerzugriff. Nur für langsame Geräte notwendig.",
"device": "Gerätename",
"deviceHint": "Beispiel: /dev/ttyUSB0",
"host": "IP Adresse oder Hostname",
@ -651,7 +653,9 @@
"protocolHintRtu": "Verbindung über eine RS485 Schnittstelle an einem Ethernet Adapter ohne Protokollübersetzung.",
"protocolHintTcp": "Gerät hat native LAN/Wifi Unterstützung oder ist über eine RS485 Schnittstelle an einen Ethernet Adapter mit Protokollübersetzung angeschlossen.",
"protocolValueRtu": "RTU",
"protocolValueTcp": "TCP"
"protocolValueTcp": "TCP",
"timeout": "Zeitüberschreitung",
"timeoutHint": "Optionale maximale Wartezeit auf eine Antwort des Geräts."
},
"modbusproxy": {
"add": "Proxy-Verbindung hinzufügen",

View file

@ -649,6 +649,8 @@
"connectionHintTcpip": "The device is reachable via network (LAN/WiFi).",
"connectionValueSerial": "RS485",
"connectionValueTcpip": "Network",
"delay": "Delay",
"delayHint": "Optional wait time before each register access. Only needed for slow devices.",
"device": "Device name",
"deviceHint": "Example: /dev/ttyUSB0",
"host": "IP address or hostname",
@ -659,7 +661,9 @@
"protocolHintRtu": "Connection through a RS485 to Ethernet adapter without protocol translation.",
"protocolHintTcp": "Device has native LAN/Wifi support or is connected through a RS485 to Ethernet adapter with protocol translation.",
"protocolValueRtu": "RTU",
"protocolValueTcp": "TCP"
"protocolValueTcp": "TCP",
"timeout": "Timeout",
"timeoutHint": "Optional maximum time to wait for a device response."
},
"modbusproxy": {
"add": "Add proxy connection",

View file

@ -37,12 +37,12 @@ func NewCfosPowerBrainFromConfig(ctx context.Context, other map[string]any) (api
return nil, err
}
return NewCfosPowerBrain(ctx, cc.URI, cc.ID)
return NewCfosPowerBrain(ctx, cc)
}
// NewCfosPowerBrain creates a cFos meter
func NewCfosPowerBrain(ctx context.Context, uri string, id uint8) (*CfosPowerBrain, error) {
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
func NewCfosPowerBrain(ctx context.Context, settings modbus.TcpSettings) (*CfosPowerBrain, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -48,11 +48,11 @@ func NewHoymilesDTUModbusTcpFromConfig(ctx context.Context, other map[string]any
return nil, err
}
return newHoymilesDTUModbusTCP(ctx, cc.URI, cc.ID, cc.pvMaxACPower.Decorator(), cc.Cache)
return newHoymilesDTUModbusTCP(ctx, cc.TcpSettings, cc.pvMaxACPower.Decorator(), cc.Cache)
}
func newHoymilesDTUModbusTCP(ctx context.Context, uri string, id uint8, maxACPower func() float64, cache time.Duration) (api.Meter, error) {
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
func newHoymilesDTUModbusTCP(ctx context.Context, settings modbus.TcpSettings, maxACPower func() float64, cache time.Duration) (api.Meter, error) {
conn, err := settings.Connection(ctx)
if err != nil {
return nil, err
}

View file

@ -5,7 +5,6 @@ import (
"errors"
"fmt"
"strings"
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
@ -39,8 +38,6 @@ func NewMbmdFromConfig(ctx context.Context, other map[string]any) (api.Meter, er
Currents []string
Voltages []string
Powers []string
Delay time.Duration
Timeout time.Duration
}{
Power: "Power",
Settings: modbus.Settings{
@ -62,17 +59,11 @@ func NewMbmdFromConfig(ctx context.Context, other map[string]any) (api.Meter, er
modbus.Lock()
defer modbus.Unlock()
conn, err := modbus.NewConnection(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Settings.Protocol(), cc.ID)
conn, err := cc.Settings.Connection(ctx)
if err != nil {
return nil, err
}
// set non-default timeout
conn.Timeout(cc.Timeout)
// set non-default delay
conn.Delay(cc.Delay)
log := util.NewLogger("modbus")
conn.Logger(log.TRACE)

View file

@ -42,9 +42,7 @@ func NewModbusFromConfig(ctx context.Context, other map[string]any) (Plugin, err
Register modbus.Register
Block modbus.Block
Scale float64
Delay time.Duration
ConnectDelay time.Duration
Timeout time.Duration
}{
Scale: 1,
}
@ -56,17 +54,11 @@ func NewModbusFromConfig(ctx context.Context, other map[string]any) (Plugin, err
modbus.Lock()
defer modbus.Unlock()
conn, err := modbus.NewConnection(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Settings.Protocol(), cc.ID)
conn, err := cc.Settings.Connection(ctx)
if err != nil {
return nil, err
}
// set non-default timeout
conn.Timeout(cc.Timeout)
// set non-default delay
conn.Delay(cc.Delay)
// set non-default connect delay
conn.ConnectDelay(cc.ConnectDelay)

View file

@ -36,9 +36,7 @@ func NewModbusSunspecFromConfig(ctx context.Context, other map[string]any) (Plug
Value []string
Scale float64
BitMask string
Delay time.Duration
ConnectDelay time.Duration
Timeout time.Duration
}{
Scale: 1,
}
@ -58,17 +56,11 @@ func NewModbusSunspecFromConfig(ctx context.Context, other map[string]any) (Plug
modbus.Lock()
defer modbus.Unlock()
conn, err := modbus.NewConnection(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Settings.Protocol(), cc.ID)
conn, err := cc.Settings.Connection(ctx)
if err != nil {
return nil, err
}
// set non-default timeout
conn.Timeout(cc.Timeout)
// set non-default delay
conn.Delay(cc.Delay)
// set non-default connect delay
conn.ConnectDelay(cc.ConnectDelay)

View file

@ -13,7 +13,7 @@ import (
)
func StartProxy(port int, config modbus.Settings, readOnly ReadOnlyMode) error {
conn, err := modbus.NewConnection(context.Background(), config.URI, config.Device, config.Comset, config.Baudrate, config.Protocol(), config.ID)
conn, err := config.Connection(context.Background())
if err != nil {
return err
}

View file

@ -18,7 +18,7 @@ params:
baudrate: 38400
comset: 8E1
- name: timeout
deprecated: true
render: |
type: abl
{{- include "modbus" . }}
timeout: {{ .timeout }}

View file

@ -39,8 +39,9 @@ requirements:
params:
- name: modbus
choice: ["tcpip"]
timeout: 10s
- name: timeout
default: 10s
deprecated: true
- name: wwoffset
type: float
unit: K
@ -80,7 +81,6 @@ render: |
config:
source: modbus
{{- include "modbus" . | indent 6 }}
timeout: {{ .timeout }}
register:
address: 10040 # LPC mode [0=No-Limit;1=Soft-Limit;2=Hard-Limit]
type: holding
@ -90,7 +90,6 @@ render: |
config:
source: modbus
{{- include "modbus" . | indent 6 }}
timeout: {{ .timeout }}
register:
address: 10000 # Heiz.Mode [0=Aus;1=Setpoint;2=Offset]
type: holding
@ -100,7 +99,6 @@ render: |
config:
source: modbus
{{- include "modbus" . | indent 6 }}
timeout: {{ .timeout }}
register:
address: 10005 # WW Mode [0=Aus;1=Setpoint;2=Offset]
type: holding
@ -117,7 +115,6 @@ render: |
set:
source: modbus
{{- include "modbus" . | indent 10 }}
timeout: {{ .timeout }}
register:
address: 10040 # LPC mode [0=No-Limit;1=Soft-Limit;2=Hard-Limit]
type: writeholding
@ -128,7 +125,6 @@ render: |
set:
source: modbus
{{- include "modbus" . | indent 10 }}
timeout: {{ .timeout }}
register:
address: 10000 # Heiz.Mode [0=Aus;1=Setpoint;2=Offset]
type: writeholding
@ -140,7 +136,6 @@ render: |
set:
source: modbus
{{- include "modbus" . | indent 10 }}
timeout: {{ .timeout }}
register:
address: 10005 # WW Mode [0=Aus;1=Setpoint;2=Offset]
type: writeholding
@ -155,7 +150,6 @@ render: |
set:
source: modbus
{{- include "modbus" . | indent 10 }}
timeout: {{ .timeout }}
register:
address: 10040 # LPC mode [0=No-Limit;1=Soft-Limit;2=Hard-Limit]
type: writeholding
@ -166,7 +160,6 @@ render: |
set:
source: modbus
{{- include "modbus" . | indent 10 }}
timeout: {{ .timeout }}
register:
address: 10000 # Heiz.Mode [0=Aus;1=Setpoint;2=Offset]
type: writeholding
@ -178,7 +171,6 @@ render: |
set:
source: modbus
{{- include "modbus" . | indent 10 }}
timeout: {{ .timeout }}
register:
address: 10005 # WW Mode [0=Aus;1=Setpoint;2=Offset]
type: writeholding
@ -193,7 +185,6 @@ render: |
set:
source: modbus
{{- include "modbus" . | indent 10 }}
timeout: {{ .timeout }}
register:
address: 10040 # LPC mode [0=No-Limit;1=Soft-Limit;2=Hard-Limit]
type: writeholding
@ -204,7 +195,6 @@ render: |
set:
source: modbus
{{- include "modbus" . | indent 10 }}
timeout: {{ .timeout }}
register:
address: 10000 # Heiz.Mode [0=Aus;1=Setpoint;2=Offset]
type: writeholding
@ -214,7 +204,6 @@ render: |
set:
source: modbus
{{- include "modbus" . | indent 10 }}
timeout: {{ .timeout }}
register:
address: 10002 # Heiz. Offset [0.1 K]
type: writeholding
@ -226,7 +215,6 @@ render: |
set:
source: modbus
{{- include "modbus" . | indent 10 }}
timeout: {{ .timeout }}
register:
address: 10005 # WW Mode [0=Aus;1=Setpoint;2=Offset]
type: writeholding
@ -236,7 +224,6 @@ render: |
set:
source: modbus
{{- include "modbus" . | indent 10 }}
timeout: {{ .timeout }}
register:
address: 10007 # WW Offset [0.1 K]
type: writeholding
@ -245,7 +232,6 @@ render: |
temp:
source: modbus
{{- include "modbus" . | indent 2 }}
timeout: {{ .timeout }}
register:
address: {{ if gt $waterint 0 -}} 10120 {{ else }} 10100 {{- end }} # 10100 = Temp. x10 RL-Ist, 10120 = Temp x10 WW-Ist
type: input
@ -254,7 +240,6 @@ render: |
limittemp:
source: modbus
{{- include "modbus" . | indent 2 }}
timeout: {{ .timeout }}
register:
address: {{ if gt $waterint 0 -}} 10121 {{ else }} 10101 {{- end }} # 10101 = Temp. x10 RL-Soll, 10121 = Temp x10 WW-Soll, 10123 = Temp x10 Tdi_solltemp
type: input
@ -263,7 +248,6 @@ render: |
power:
source: modbus
{{- include "modbus" . | indent 2 }}
timeout: {{ .timeout }}
register:
address: 10301 # 10301 = kW x0.01 Power-In elektrisch
type: input
@ -272,7 +256,6 @@ render: |
energy:
source: modbus
{{- include "modbus" . | indent 2 }}
timeout: {{ .timeout }}
register:
address: 10311 # 10311 = kWh x10 kumulierter Stromverbrauch
type: input

View file

@ -23,8 +23,8 @@ params:
id: 1
- name: connector
- name: timeout
deprecated: true
render: |
type: pracht-alpha
{{- include "modbus" . }}
connector: {{ .connector }}
timeout: {{ .timeout }}

View file

@ -27,8 +27,9 @@ params:
choice: ["grid", "pv", "battery"]
- name: modbus
choice: ["rs485", "tcpip"]
timeout: 15s
- name: timeout
default: 15s
deprecated: true
- name: maxacpower
service: modbus/read?address=30075&type=holding&encoding=uint32&{modbus}
- name: curtailpercent
@ -69,7 +70,6 @@ render: |
power:
source: modbus
{{- include "modbus" . | indent 2 }}
timeout: {{ .timeout }}
connectdelay: 1s
register:
address: 37113 # Grid import/export power
@ -82,7 +82,6 @@ render: |
energy:
source: modbus
{{- include "modbus" . | indent 2 }}
timeout: {{ .timeout }}
register:
address: 37121 # Active energy imported from the grid
type: holding
@ -94,7 +93,6 @@ render: |
returnenergy:
source: modbus
{{- include "modbus" . | indent 2 }}
timeout: {{ .timeout }}
register:
address: 37119 # Active energy exported to the grid
type: holding
@ -106,7 +104,6 @@ render: |
currents:
- source: modbus
{{- include "modbus" . | indent 2 }}
timeout: {{ .timeout }}
register:
address: 37107 # Huawei phase A grid current
type: holding
@ -117,7 +114,6 @@ render: |
scale: -0.01
- source: modbus
{{- include "modbus" . | indent 2 }}
timeout: {{ .timeout }}
register:
address: 37109 # Huawei phase B grid current
type: holding
@ -128,7 +124,6 @@ render: |
scale: -0.01
- source: modbus
{{- include "modbus" . | indent 2 }}
timeout: {{ .timeout }}
register:
address: 37111 # Huawei phase C grid current
type: holding
@ -140,7 +135,6 @@ render: |
voltages:
- source: modbus
{{- include "modbus" . | indent 2 }}
timeout: {{ .timeout }}
register:
address: 37101 # Huawei phase A grid voltage
type: holding
@ -151,7 +145,6 @@ render: |
scale: 0.1
- source: modbus
{{- include "modbus" . | indent 2 }}
timeout: {{ .timeout }}
register:
address: 37103 # Huawei phase B grid voltage
type: holding
@ -162,7 +155,6 @@ render: |
scale: 0.1
- source: modbus
{{- include "modbus" . | indent 2 }}
timeout: {{ .timeout }}
register:
address: 37105 # Huawei phase C grid voltage
type: holding
@ -174,7 +166,6 @@ render: |
powers:
- source: modbus
{{- include "modbus" . | indent 2 }}
timeout: {{ .timeout }}
register:
address: 37132 # Huawei phase A grid active power
type: holding
@ -185,7 +176,6 @@ render: |
scale: -1
- source: modbus
{{- include "modbus" . | indent 2 }}
timeout: {{ .timeout }}
register:
address: 37134 # Huawei phase B grid active power
type: holding
@ -196,7 +186,6 @@ render: |
scale: -1
- source: modbus
{{- include "modbus" . | indent 2 }}
timeout: {{ .timeout }}
register:
address: 37136 # Huawei phase C grid active power
type: holding
@ -210,7 +199,6 @@ render: |
power:
source: modbus
{{- include "modbus" . | indent 2 }}
timeout: {{ .timeout }}
connectdelay: 1s
register:
address: 32064 # Input power DC
@ -219,7 +207,6 @@ render: |
energy:
source: modbus
{{- include "modbus" . | indent 2 }}
timeout: {{ .timeout }}
register:
address: 32108 # Total DC Input Energy
type: holding
@ -295,7 +282,6 @@ render: |
power:
source: modbus
{{- include "modbus" . | indent 2 }}
timeout: {{ .timeout }}
connectdelay: 1s
register:
{{- if eq .storageunit "2" }}
@ -309,7 +295,6 @@ render: |
energy:
source: modbus
{{- include "modbus" . | indent 2 }}
timeout: {{ .timeout }}
register:
{{- if eq .storageunit "2" }}
address: 37755 # [Energy storage unit 2] Total discharge
@ -322,7 +307,6 @@ render: |
returnenergy:
source: modbus
{{- include "modbus" . | indent 2 }}
timeout: {{ .timeout }}
register:
{{- if eq .storageunit "2" }}
address: 37753 # [Energy storage unit 2] Total charge
@ -335,7 +319,6 @@ render: |
soc:
source: modbus
{{- include "modbus" . | indent 2 }}
timeout: {{ .timeout }}
register:
{{- if eq .storageunit "2" }}
address: 37738

View file

@ -20,14 +20,14 @@ params:
choice: ["pv"]
- name: modbus
choice: ["rs485", "tcpip"]
timeout: 15s
- name: timeout
default: 15s
deprecated: true
render: |
type: custom
power:
source: modbus
{{- include "modbus" . | indent 2 }}
timeout: {{ .timeout }}
connectdelay: 1s
register:
address: 32080 # Active generation power AC
@ -36,7 +36,6 @@ render: |
energy:
source: modbus
{{- include "modbus" . | indent 2 }}
timeout: {{ .timeout }}
register:
address: 32106 # Accumulated energy yield
type: holding

View file

@ -15,6 +15,7 @@ params:
choice: ["tcpip"]
id: 1
port: 503
timeout: 30s
- preset: battery-params
render: |
type: custom
@ -29,14 +30,12 @@ render: |
type: input
decode: int32
scale: -1
timeout: 30s
- source: modbus
{{- include "modbus" . | indent 4 }}
register:
address: 2 # Verbrauch
type: input
decode: int32
timeout: 30s
- source: modbus
{{- include "modbus" . | indent 4 }}
register:
@ -44,7 +43,6 @@ render: |
type: input
decode: int32
scale: -1
timeout: 30s
{{- end }}
{{- if eq .usage "pv" }}
power:
@ -54,7 +52,6 @@ render: |
address: 0 # Erzeugung
type: input
decode: int32
timeout: 30s
{{- end }}
{{- if eq .usage "battery" }}
power:
@ -64,7 +61,6 @@ render: |
address: 37 # Batterie Leistung
type: input
decode: int32
timeout: 30s
soc:
source: modbus
{{- include "modbus" . | indent 2 }}
@ -72,6 +68,5 @@ render: |
address: 36 # Batterie SOC
type: input
decode: uint16
timeout: 30s
{{- include "battery-params" . }}
{{- end }}

View file

@ -14,6 +14,7 @@ params:
choice: ["grid", "pv", "battery"]
- name: modbus
choice: ["tcpip"]
timeout: 5s
- preset: battery-params
render: |
type: custom
@ -25,7 +26,6 @@ render: |
address: 14
type: holding
decode: int32
timeout: 5s
{{- end }}
{{- if eq .usage "pv" }}
power:
@ -35,7 +35,6 @@ render: |
address: 16
type: holding
decode: int32
timeout: 5s
{{- end }}
{{- if eq .usage "battery" }}
power:

View file

@ -16,78 +16,67 @@ params:
id: 1
port: 1502
- name: timeout
deprecated: true
render: |
type: custom
{{- if eq .usage "grid" }}
power:
source: sunspec
{{- include "modbus" . | indent 2 }}
timeout: {{ .timeout }}
subdevice: 1 # Metering device
value: 203:W # sunspec 3-phase meter power reading
scale: -1
energy:
source: sunspec
{{- include "modbus" . | indent 2 }}
timeout: {{ .timeout }}
subdevice: 1 # Metering device
value: 203:TotWhImp
scale: 0.001
returnenergy:
source: sunspec
{{- include "modbus" . | indent 2 }}
timeout: {{ .timeout }}
subdevice: 1 # Metering device
value: 203:TotWhExp
scale: 0.001
currents:
- source: sunspec
{{- include "modbus" . | indent 4 }}
timeout: {{ .timeout }}
subdevice: 1 # Metering device
value: 203:AphA
- source: sunspec
{{- include "modbus" . | indent 4 }}
timeout: {{ .timeout }}
subdevice: 1 # Metering device
value: 203:AphB
- source: sunspec
{{- include "modbus" . | indent 4 }}
timeout: {{ .timeout }}
subdevice: 1 # Metering device
value: 203:AphC
voltages:
- source: sunspec
{{- include "modbus" . | indent 4 }}
timeout: {{ .timeout }}
subdevice: 1 # Metering device
value: 203:PhVphA
- source: sunspec
{{- include "modbus" . | indent 4 }}
timeout: {{ .timeout }}
subdevice: 1 # Metering device
value: 203:PhVphB
- source: sunspec
{{- include "modbus" . | indent 4 }}
timeout: {{ .timeout }}
subdevice: 1 # Metering device
value: 203:PhVphC
powers:
- source: sunspec
{{- include "modbus" . | indent 4 }}
timeout: {{ .timeout }}
subdevice: 1 # Metering device
value: 203:WphA
scale: -1
- source: sunspec
{{- include "modbus" . | indent 4 }}
timeout: {{ .timeout }}
subdevice: 1 # Metering device
value: 203:WphB
scale: -1
- source: sunspec
{{- include "modbus" . | indent 4 }}
timeout: {{ .timeout }}
subdevice: 1 # Metering device
value: 203:WphC
scale: -1
@ -96,14 +85,12 @@ render: |
power:
source: sunspec
{{- include "modbus" . | indent 2 }}
timeout: {{ .timeout }}
value:
- 101:W
- 103:W
energy:
source: sunspec
{{- include "modbus" . | indent 2 }}
timeout: {{ .timeout }}
value:
- 101:WH
- 103:WH
@ -111,19 +98,16 @@ render: |
currents:
- source: sunspec
{{- include "modbus" . | indent 4 }}
timeout: {{ .timeout }}
value:
- 101:AphA
- 103:AphA
- source: sunspec
{{- include "modbus" . | indent 4 }}
timeout: {{ .timeout }}
value:
- 101:AphB
- 103:AphB
- source: sunspec
{{- include "modbus" . | indent 4 }}
timeout: {{ .timeout }}
value:
- 101:AphC
- 103:AphC

View file

@ -13,17 +13,16 @@ params:
choice: ["rs485"]
port: 502
id: 247
- preset: battery-params
delay: 100ms
- name: delay
default: 100ms
advanced: true
deprecated: true
- preset: battery-params
render: |
type: custom
{{- if eq .usage "grid" }}
power: # power (W)
source: modbus
{{- include "modbus" . | indent 2 }}
delay: {{ .delay }}
register:
address: 11000 # Total Power on Meter
type: holding
@ -32,7 +31,6 @@ render: |
powers:
- source: modbus
{{- include "modbus" . | indent 4 }}
delay: {{ .delay }}
register:
address: 10994 # Phase A Power on Meter
type: holding
@ -40,7 +38,6 @@ render: |
scale: -1
- source: modbus
{{- include "modbus" . | indent 4 }}
delay: {{ .delay }}
register:
address: 10996 # Phase B Power on Meter
type: holding
@ -48,7 +45,6 @@ render: |
scale: -1
- source: modbus
{{- include "modbus" . | indent 4 }}
delay: {{ .delay }}
register:
address: 10998 # Phase C Power on Meter
type: holding
@ -59,7 +55,6 @@ render: |
power: # power (W)
source: modbus
{{- include "modbus" . | indent 2 }}
delay: {{ .delay }}
register:
address: 11028 # PV Input Total Power
type: holding
@ -69,7 +64,6 @@ render: |
power: # power (W)
source: modbus
{{- include "modbus" . | indent 2 }}
delay: {{ .delay }}
register:
address: 40258 # Total_Backup_P
type: holding
@ -77,7 +71,6 @@ render: |
soc:
source: modbus
{{- include "modbus" . | indent 2 }}
delay: {{ .delay }}
scale: 0.01
register:
address: 43000 # SOC
@ -86,7 +79,6 @@ render: |
limitsoc:
source: modbus
{{- include "modbus" . | indent 2 }}
delay: {{ .delay }}
register:
address: 52503 # min soc
type: writeholding

View file

@ -6,6 +6,7 @@ import (
"fmt"
"strings"
"sync"
"time"
"github.com/evcc-io/evcc/util"
"github.com/volkszaehler/mbmd/meters"
@ -29,6 +30,21 @@ type TcpSettings struct {
URI string
ID uint8
RTU *bool `mapstructure:"rtu"`
Delay time.Duration
Timeout time.Duration
}
// Connection creates a modbus TCP connection from the settings
func (s TcpSettings) Connection(ctx context.Context) (*Connection, error) {
settings := Settings{
ID: s.ID,
URI: s.URI,
RTU: s.RTU,
Delay: s.Delay,
Timeout: s.Timeout,
}
return settings.Connection(ctx, Tcp)
}
// Settings contains the ModBus settings
@ -41,6 +57,27 @@ type Settings struct {
Baudrate int `json:"baudrate,omitempty" yaml:",omitempty"`
UDP bool `json:"udp,omitempty" yaml:",omitempty"`
RTU *bool `json:"rtu,omitempty" yaml:",omitempty"`
Delay time.Duration `json:"delay,omitempty" yaml:",omitempty"`
Timeout time.Duration `json:"timeout,omitempty" yaml:",omitempty"`
}
// Connection creates a modbus connection from the settings, applying delay and timeout.
// The optional proto overrides the protocol derived from the settings.
func (s Settings) Connection(ctx context.Context, proto ...Protocol) (*Connection, error) {
p := s.Protocol()
if len(proto) > 0 {
p = proto[0]
}
conn, err := NewConnection(ctx, s.URI, s.Device, s.Comset, s.Baudrate, p, s.ID)
if err != nil {
return nil, err
}
conn.Timeout(s.Timeout)
conn.Delay(s.Delay)
return conn, nil
}
// Protocol identifies the wire format from the RTU setting

View file

@ -40,6 +40,17 @@ func (c *configDefaults) Load() {
panic("failed to parse config defaults: " + err.Error())
}
// inherit shared param properties into modbus definitions, except examples
// which would render into every modbus template
for i, p := range c.Modbus.Definitions {
if idx, def := c.ParamByName(p.Name); idx >= 0 {
example := p.Example
p.OverwriteProperties(def)
p.Example = example
c.Modbus.Definitions[i] = p
}
}
// resolve modbus param references
for typ := range c.Modbus.Types {
for i, p := range c.Modbus.Types[typ].Params {

View file

@ -878,6 +878,14 @@ modbus:
en: Port
default: 502
type: int
- name: delay
help:
de: Wartezeit vor jedem Registerzugriff
en: Wait time before each register access
type: duration
advanced: true
- name: timeout
advanced: true
types:
rs485serial:
description:
@ -887,6 +895,8 @@ modbus:
- name: device
- name: baudrate
- name: comset
- name: delay
- name: timeout
rs485tcpip:
description:
generic: Serial (Ethernet-RS485 Adapter)
@ -895,6 +905,8 @@ modbus:
- name: host
- name: port
default: 502
- name: delay
- name: timeout
tcpip:
description:
generic: TCP/IP
@ -903,6 +915,8 @@ modbus:
- name: host
- name: port
default: 502
- name: delay
- name: timeout
udp:
description:
generic: UDP
@ -911,6 +925,8 @@ modbus:
- name: host
- name: port
default: 502
- name: delay
- name: timeout
devicegroups:
generic:

View file

@ -6,6 +6,12 @@ id: {{ .id }}
device: {{ .device }} # USB-RS485 Adapter Adresse
baudrate: {{ .baudrate }} # Prüfe die Geräteeinstellungen, typische Werte sind 9600, 19200, 38400, 57600, 115200
comset: "{{ .comset }}" # Kommunikationsparameter für den Adapter
{{- if .delay }}
delay: {{ .delay }} # optional
{{- end }}
{{- if .timeout }}
timeout: {{ .timeout }} # optional
{{- end }}
{{- end }}
{{- if .rs485tcpip }}
@ -14,6 +20,12 @@ modbus: rs485tcpip
id: {{ .id }}
host: {{ .host }} # Hostname
port: {{ .port }} # Port
{{- if .delay }}
delay: {{ .delay }} # optional
{{- end }}
{{- if .timeout }}
timeout: {{ .timeout }} # optional
{{- end }}
{{- end }}
{{- if .tcpip }}
@ -22,4 +34,10 @@ modbus: tcpip
id: {{ .id }}
host: {{ .host }} # Hostname
port: {{ .port }} # Port
{{- if .delay }}
delay: {{ .delay }} # optional
{{- end }}
{{- if .timeout }}
timeout: {{ .timeout }} # optional
{{- end }}
{{- end -}}

View file

@ -22,4 +22,10 @@ rtu: true
# configuration error - should not happen
modbusConnectionTypeNotDefined: {{ .modbus }}
{{- end }}
{{- if .delay }}
delay: {{ .delay }}
{{- end }}
{{- if .timeout }}
timeout: {{ .timeout }}
{{- end }}
{{- end }}

View file

@ -3,6 +3,7 @@ package templates
import (
_ "embed"
"fmt"
"slices"
"strconv"
"strings"
)
@ -29,7 +30,11 @@ func (t *Template) ModbusParams(modbusType string, values map[string]any) {
return
}
modbusParams := ConfigDefaults.Modbus.Types[values[ParamModbus].(string)].Params
// skip params the template already defines (e.g. deprecated delay, timeout)
modbusParams := slices.DeleteFunc(slices.Clone(ConfigDefaults.Modbus.Types[values[ParamModbus].(string)].Params), func(p Param) bool {
i, _ := t.ParamByName(p.Name)
return i >= 0
})
// add the modbus params at the beginning
t.Params = append(modbusParams, t.Params...)
@ -90,6 +95,14 @@ func (t *Template) ModbusValues(renderMode int, values map[string]any) {
if modbusParam.Comset != "" {
defaultValue = modbusParam.Comset
}
case ModbusParamDelay:
if modbusParam.Delay != "" {
defaultValue = modbusParam.Delay
}
case ModbusParamTimeout:
if modbusParam.Timeout != "" {
defaultValue = modbusParam.Timeout
}
}
if defaultValue != "" {

View file

@ -36,6 +36,8 @@ const (
ModbusParamHost = "host"
ModbusParamPort = "port"
ModbusParamRTU = "rtu"
ModbusParamDelay = "delay"
ModbusParamTimeout = "timeout"
)
const (
@ -51,6 +53,7 @@ var (
ModbusParams = []string{
ModbusParamId, ModbusParamDevice, ModbusParamBaudrate, ModbusParamComset,
ModbusParamURI, ModbusParamHost, ModbusParamPort, ModbusParamRTU,
ModbusParamDelay, ModbusParamTimeout,
}
ModbusConnectionTypes = []string{
@ -234,6 +237,8 @@ type Param struct {
Comset string `json:",omitempty"` // device specific default for modbus RS485 comset
Port int `json:",omitempty"` // device specific default for modbus TCPIP port
ID int `json:",omitempty"` // device specific default for modbus ID
Delay string `json:",omitempty"` // device specific default for modbus delay
Timeout string `json:",omitempty"` // device specific default for modbus timeout
}
// DefaultValue returns a default or example value depending on the renderMode