From 452a1301b151f46ed8f31a1c3a1dfeb2b4421cde Mon Sep 17 00:00:00 2001 From: Maschga <88616799+Maschga@users.noreply.github.com> Date: Fri, 20 Mar 2026 13:13:34 +0100 Subject: [PATCH] fix/refactor: modbus form (#28226) Co-authored-by: Michael Geers --- .../components/Config/DeviceModal/Modbus.vue | 23 ++-- .../Config/ModbusProxyConnection.vue | 126 ++++++++++++++++++ .../js/components/Config/ModbusProxyModal.vue | 89 ++++--------- tests/config-modbusproxy.spec.ts | 29 ++++ 4 files changed, 192 insertions(+), 75 deletions(-) create mode 100644 assets/js/components/Config/ModbusProxyConnection.vue diff --git a/assets/js/components/Config/DeviceModal/Modbus.vue b/assets/js/components/Config/DeviceModal/Modbus.vue index 1568972aa..acf3fb724 100644 --- a/assets/js/components/Config/DeviceModal/Modbus.vue +++ b/assets/js/components/Config/DeviceModal/Modbus.vue @@ -46,7 +46,7 @@ class="me-2" required :model-value="id || defaultId" - @input="$emit('update:id', $event.target.value)" + @update:model-value="(v) => $emit('update:id', v)" />
@@ -62,7 +62,7 @@ class="me-2" required :model-value="host" - @input="$emit('update:host', $event.target.value)" + @update:model-value="(v) => $emit('update:host', v)" /> @@ -73,7 +73,7 @@ class="me-2 w-50" required :model-value="port || defaultPort" - @input="$emit('update:port', $event.target.value)" + @update:model-value="(v) => $emit('update:port', v)" /> @@ -154,7 +154,7 @@ :choice="comsetOptions" required :model-value="comset || defaultComset" - @input="$emit('update:comset', $event.target.value)" + @update:model-value="(v) => $emit('update:comset', v)" />
@@ -263,9 +263,9 @@ export default defineComponent({ this.setProtocolByCapabilities(newValue); this.$emit("update:modbus", this.selectedModbus); }, - modbus(newValue: MODBUS_TYPE) { + modbus(newValue: MODBUS_TYPE, oldValue: MODBUS_TYPE) { if (newValue) { - this.setConnectionAndProtocolByModbus(newValue); + this.setConnectionAndProtocolByModbus(newValue, oldValue); } }, connection(newValue: MODBUS_CONNECTION, oldValue: MODBUS_CONNECTION) { @@ -298,8 +298,8 @@ export default defineComponent({ ? MODBUS_PROTOCOL.TCP : MODBUS_PROTOCOL.RTU; }, - setConnectionAndProtocolByModbus(modbus?: MODBUS_TYPE) { - switch (modbus) { + setConnectionAndProtocolByModbus(newModbus?: MODBUS_TYPE, oldModbus?: MODBUS_TYPE) { + switch (newModbus) { case MODBUS_TYPE.RS485_SERIAL: this.connection = MODBUS_CONNECTION.SERIAL; this.protocol = MODBUS_PROTOCOL.RTU; @@ -313,6 +313,11 @@ export default defineComponent({ this.protocol = MODBUS_PROTOCOL.TCP; break; } + + // when switching from serial to TCP/IP, default protocol to Modbus-TCP (rtu=false) + if (oldModbus === MODBUS_TYPE.RS485_SERIAL && newModbus === MODBUS_TYPE.RS485_TCPIP) { + this.protocol = MODBUS_PROTOCOL.TCP; + } }, formId(name: string): string { return `${name}-${this.componentId}`; diff --git a/assets/js/components/Config/ModbusProxyConnection.vue b/assets/js/components/Config/ModbusProxyConnection.vue new file mode 100644 index 000000000..981aa6193 --- /dev/null +++ b/assets/js/components/Config/ModbusProxyConnection.vue @@ -0,0 +1,126 @@ + + + diff --git a/assets/js/components/Config/ModbusProxyModal.vue b/assets/js/components/Config/ModbusProxyModal.vue index d1a051c8a..e0f7eb1d2 100644 --- a/assets/js/components/Config/ModbusProxyModal.vue +++ b/assets/js/components/Config/ModbusProxyModal.vue @@ -85,19 +85,12 @@ - @@ -143,17 +136,13 @@ import "@h2d2/shopicons/es/regular/arrowdown"; import "@h2d2/shopicons/es/regular/plus"; import "@h2d2/shopicons/es/regular/trash"; import JsonModal from "./JsonModal.vue"; -import { - MODBUS_COMSET, - MODBUS_CONNECTION, - MODBUS_PROTOCOL, - MODBUS_PROXY_READONLY, - MODBUS_TYPE, - type ModbusProxy, - type ModbusProxySettings, -} from "@/types/evcc"; +import { MODBUS_PROXY_READONLY, type ModbusProxy } from "@/types/evcc"; import ASCII_DIAGRAM from "./modbus-diagram.txt?raw"; -import Modbus from "./DeviceModal/Modbus.vue"; +import ModbusProxyConnection, { + DEFAULT_BAUDRATE, + DEFAULT_COMSET, + DEFAULT_PORT, +} from "./ModbusProxyConnection.vue"; import PropertyField from "./PropertyField.vue"; import FormRow from "./FormRow.vue"; import SponsorTokenRequired from "./DeviceModal/SponsorTokenRequired.vue"; @@ -162,7 +151,14 @@ import { defineComponent } from "vue"; export default defineComponent({ name: "ModbusProxyModal", - components: { JsonModal, Modbus, FormRow, PropertyField, SponsorTokenRequired, SelectGroup }, + components: { + JsonModal, + ModbusProxyConnection, + FormRow, + PropertyField, + SponsorTokenRequired, + SelectGroup, + }, props: { isSponsor: Boolean, }, @@ -170,10 +166,6 @@ export default defineComponent({ data() { return { ASCII_DIAGRAM, - MODBUS_PROXY_READONLY, - MODBUS_CONNECTION, - MODBUS_PROTOCOL, - MODBUS_TYPE, }; }, computed: { @@ -188,12 +180,6 @@ export default defineComponent({ formId(index: number, name: string) { return `modbusproxy-connection-${index}-${name}`; }, - getModbus(s: ModbusProxySettings) { - if (s.device) { - return MODBUS_TYPE.RS485_SERIAL; - } - return s.rtu ? MODBUS_TYPE.RS485_TCPIP : MODBUS_TYPE.TCPIP; - }, getReadonlyHelp(readonly = MODBUS_PROXY_READONLY.FALSE): string { return this.$t(`config.modbusproxy.readonly.help.${readonly}`); }, @@ -203,41 +189,12 @@ export default defineComponent({ port: highestPort + 1, readonly: MODBUS_PROXY_READONLY.FALSE, settings: { - uri: ":502", - baudrate: 9600, - comset: "8N1" as MODBUS_COMSET, + uri: `:${DEFAULT_PORT}`, + baudrate: DEFAULT_BAUDRATE, + comset: DEFAULT_COMSET, }, }); }, - getHost(uri?: string) { - return uri?.split(":")[0] || ""; - }, - getPort(uri?: string) { - return uri?.split(":")[1] || ""; - }, - updateHost(newHost: string, settings: ModbusProxySettings) { - const port = this.getPort(settings.uri); - settings.uri = `${newHost}:${port}`; - }, - updatePort(newPort: string | number, settings: ModbusProxySettings) { - const host = this.getHost(settings.uri); - settings.uri = `${host}:${newPort}`; - }, - updateModbus(settings: ModbusProxySettings, modbus: MODBUS_TYPE) { - switch (modbus) { - case MODBUS_TYPE.RS485_SERIAL: - settings.uri = undefined; - settings.rtu = undefined; - break; - case MODBUS_TYPE.RS485_TCPIP: - case MODBUS_TYPE.TCPIP: - settings.device = undefined; - settings.baudrate = undefined; - settings.comset = undefined; - settings.rtu = modbus === MODBUS_TYPE.RS485_TCPIP; - break; - } - }, }, }); diff --git a/tests/config-modbusproxy.spec.ts b/tests/config-modbusproxy.spec.ts index 2762fc764..383074d3d 100644 --- a/tests/config-modbusproxy.spec.ts +++ b/tests/config-modbusproxy.spec.ts @@ -87,6 +87,35 @@ test.describe("modbusproxy", async () => { await expect(modal).not.toContainText("Connection #1"); }); + test("modbusproxy serial defaults", async ({ page }) => { + await start(); + await page.goto("/#/config"); + + const modbusproxyCard = page.getByTestId("modbusproxy"); + await modbusproxyCard.getByRole("button", { name: "edit" }).click(); + const modal = page.getByTestId("modbusproxy-modal"); + await expectModalVisible(modal); + + await modal.getByRole("button", { name: "Add proxy connection" }).click(); + const deviceBox = modal.getByTestId("device-box"); + + await expect(deviceBox.getByLabel("Network")).toBeChecked(); + await expect(deviceBox.getByLabel("Port")).toHaveValue("502"); + + // switch to RS485 + await deviceBox.getByText("RS485", { exact: true }).click(); + await expect(deviceBox.getByLabel("Baud rate")).toHaveValue("9600"); + await expect(deviceBox.getByLabel("ComSet")).toHaveValue("8N1"); + + await deviceBox.getByLabel("Baud rate").selectOption("19200"); + + // switch to Network and back: defaults restored + await deviceBox.getByText("Network").click(); + await deviceBox.getByText("RS485", { exact: true }).click(); + await expect(deviceBox.getByLabel("Baud rate")).toHaveValue("9600"); + await expect(deviceBox.getByLabel("ComSet")).toHaveValue("8N1"); + }); + test("modbusproxy via db (yaml to json migration)", async ({ page }) => { await start(undefined, CONFIG_MODBUSPROXY_MIGRATE); await page.goto("/#/config");