Config UI: suggest serial devices for modbus (#25780)

This commit is contained in:
andig 2025-12-09 12:09:33 +01:00 • committed by GitHub
parent 25625e461b
commit 3c7476762b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 89 additions and 6 deletions

View file

@ -108,6 +108,7 @@
v-model:device="values['device']"
v-model:baudrate="values['baudrate']"
v-model:comset="values['comset']"
component-id="device"
:defaultId="modbus.ID ? Number(modbus.ID) : undefined"
:defaultComset="modbus.Comset"
:defaultBaudrate="modbus.Baudrate"

View file

@ -121,15 +121,16 @@
:id="formId('modbusDevice')"
:label="$t('config.modbus.device')"
:help="$t('config.modbus.deviceHint')"
data-testid="modbus-device"
>
<PropertyField
:id="formId('modbusDevice')"
v-model:model-value="deviceModel"
property="device"
type="String"
class="me-2"
required
:model-value="device"
@change="$emit('update:device', $event.target.value)"
:service-values="deviceServiceValues"
/>
</FormRow>
<FormRow :id="formId('modbusBaudrate')" :label="$t('config.modbus.baudrate')">
@ -165,6 +166,7 @@ import FormRow from "../FormRow.vue";
import PropertyField from "../PropertyField.vue";
import type { PropType } from "vue";
import type { ModbusCapability } from "./index";
import { loadServiceValues } from "./index";
import {
MODBUS_BAUDRATE,
MODBUS_COMSET,
@ -177,6 +179,7 @@ export default defineComponent({
name: "Modbus",
components: { FormRow, PropertyField },
props: {
componentId: { type: String, required: true },
capabilities: {
type: Array as PropType<ModbusCapability[]>,
default: () => [],
@ -209,9 +212,20 @@ export default defineComponent({
protocol: MODBUS_PROTOCOL.TCP as MODBUS_PROTOCOL,
MODBUS_PROTOCOL,
MODBUS_CONNECTION,
deviceServiceValues: [] as string[],
localDevice: undefined as string | undefined,
};
},
computed: {
deviceModel: {
get(): string | undefined {
return this.localDevice !== undefined ? this.localDevice : this.device;
},
set(value: string | undefined) {
this.localDevice = value;
this.$emit("update:device", value);
},
},
selectedModbus(): MODBUS_TYPE {
if (this.connection === MODBUS_CONNECTION.SERIAL) {
return MODBUS_TYPE.RS485_SERIAL;
@ -254,10 +268,21 @@ export default defineComponent({
this.setConnectionAndProtocolByModbus(newValue);
}
},
connection() {
this.applyServiceDefault();
},
device(newValue: string | undefined) {
// Sync prop to local state
if (newValue !== this.localDevice) {
this.localDevice = newValue;
}
},
},
mounted() {
this.localDevice = this.device;
this.setConnectionAndProtocolByModbus(this.modbus);
this.$emit("update:modbus", this.selectedModbus);
this.updateServiceValues();
},
methods: {
setProtocolByCapabilities(capabilities: ModbusCapability[]) {
@ -282,7 +307,21 @@ export default defineComponent({
}
},
formId(name: string): string {
return `${name}-${this.id}`;
return `${name}-${this.componentId}`;
},
async updateServiceValues() {
this.deviceServiceValues = await loadServiceValues("hardware/serial");
this.applyServiceDefault();
},
applyServiceDefault() {
// auto-apply device value if it's needed and exactly one option exists
if (
this.connection === MODBUS_CONNECTION.SERIAL &&
this.deviceServiceValues.length === 1 &&
!this.device
) {
this.deviceModel = this.deviceServiceValues[0];
}
},
},
});

View file

@ -87,11 +87,11 @@
</h5>
</div>
<Modbus
:id="index"
v-model:baudrate="c.settings.baudrate"
v-model:comset="c.settings.comset"
v-model:device="c.settings.device"
:modbus="getModbus(c.settings)"
:component-id="`proxy-${index}`"
:host="getHost(c.settings.uri)"
:port="getPort(c.settings.uri)"
:capabilities="['rs485', 'tcpip']"

View file

@ -42,10 +42,10 @@ import (
"github.com/evcc-io/evcc/tariff"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/config"
_ "github.com/evcc-io/evcc/util/demo"
"github.com/evcc-io/evcc/util/locale"
"github.com/evcc-io/evcc/util/machine"
"github.com/evcc-io/evcc/util/request"
_ "github.com/evcc-io/evcc/util/service"
"github.com/evcc-io/evcc/util/sponsor"
"github.com/evcc-io/evcc/util/templates"
"github.com/evcc-io/evcc/vehicle"

2
go.mod
View file

@ -103,6 +103,7 @@ require (
github.com/volkszaehler/mbmd v0.0.0-20250808161051-499ae856f44e
github.com/writeas/go-strip-markdown/v2 v2.1.1
gitlab.com/bboehmke/sunny v0.16.0
go.bug.st/serial v1.6.4
go.uber.org/mock v0.6.0
go.yaml.in/yaml/v4 v4.0.0-rc.3
golang.org/x/crypto v0.45.0
@ -146,6 +147,7 @@ require (
github.com/clipperhouse/stringish v0.1.1 // indirect
github.com/clipperhouse/uax29/v2 v2.3.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
github.com/creack/goselect v0.1.2 // indirect
github.com/cronokirby/saferith v0.33.0 // indirect
github.com/cstockton/go-conv v1.0.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect

4
go.sum
View file

@ -146,6 +146,8 @@ github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfc
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0=
github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
@ -779,6 +781,8 @@ gitlab.com/bboehmke/sunny v0.16.0 h1:arcU5MNupJ9KELOcSC82mYPGP/friBop+PxtybhqRwo
gitlab.com/bboehmke/sunny v0.16.0/go.mod h1:F5AIuL7kYteSJFR5E+YEocxIdpyCXmtDciFmMQVjP88=
gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a h1:DxppxFKRqJ8WD6oJ3+ZXKDY0iMONQDl5UTg2aTyHh8k=
gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a/go.mod h1:NREvu3a57BaK0R1+ztrEzHWiZAihohNLQ6trPxlIqZI=
go.bug.st/serial v1.6.4 h1:7FmqNPgVp3pu2Jz5PoPtbZ9jJO5gnEnZIvnI1lzve8A=
go.bug.st/serial v1.6.4/go.mod h1:nofMJxTeNVny/m6+KaafC6vJGj3miwQZ6vW4BZUGJPI=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=

View file

@ -1,4 +1,4 @@
package demo
package service
import (
"encoding/json"

36
util/service/hardware.go Normal file
View file

@ -0,0 +1,36 @@
package service
import (
"encoding/json"
"net/http"
"os"
"strings"
"sync"
"github.com/evcc-io/evcc/server/service"
serialports "go.bug.st/serial"
)
func init() {
mux := http.NewServeMux()
mux.HandleFunc("GET /serial", getSerialPorts)
service.Register("hardware", mux)
}
var (
once sync.Once
ports []string
)
func getSerialPorts(w http.ResponseWriter, req *http.Request) {
once.Do(func() {
if env := os.Getenv("EVCC_SERIAL_PORTS"); env != "" {
ports = strings.Split(env, ",")
} else {
ports, _ = serialports.GetPortsList()
}
})
json.NewEncoder(w).Encode(ports)
}

View file

@ -706,6 +706,7 @@ modbus:
help:
de: USB-RS485 Gerätename
en: USB-RS485 device name
service: hardware/serial # not actually used
example: /dev/ttyUSB0
- name: baudrate
description: