Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Michael Geers <michael@geers.tv>
346 lines
9.1 KiB
Vue
346 lines
9.1 KiB
Vue
<template>
|
|
<FormRow
|
|
v-if="showConnectionOptions"
|
|
id="modbusTcpIp"
|
|
:label="$t('config.modbus.connection')"
|
|
:help="
|
|
connection === MODBUS_CONNECTION.TCPIP
|
|
? $t('config.modbus.connectionHintTcpip')
|
|
: $t('config.modbus.connectionHintSerial')
|
|
"
|
|
>
|
|
<div class="btn-group" role="group">
|
|
<input
|
|
:id="formId('modbusTcpIp')"
|
|
v-model="connection"
|
|
type="radio"
|
|
class="btn-check"
|
|
:name="formId('modbusConnection')"
|
|
value="tcpip"
|
|
tabindex="0"
|
|
autocomplete="off"
|
|
/>
|
|
<label class="btn btn-outline-primary" :for="formId('modbusTcpIp')">
|
|
{{ $t("config.modbus.connectionValueTcpip") }}
|
|
</label>
|
|
<input
|
|
:id="formId('modbusSerial')"
|
|
v-model="connection"
|
|
type="radio"
|
|
class="btn-check"
|
|
:name="formId('modbusConnection')"
|
|
value="serial"
|
|
tabindex="0"
|
|
autocomplete="off"
|
|
/>
|
|
<label class="btn btn-outline-primary" :for="formId('modbusSerial')">
|
|
{{ $t("config.modbus.connectionValueSerial") }}
|
|
</label>
|
|
</div>
|
|
</FormRow>
|
|
<FormRow v-if="!hideModbusId" id="modbusId" :label="$t('config.modbus.id')">
|
|
<PropertyField
|
|
id="modbusId"
|
|
property="id"
|
|
type="Int"
|
|
class="me-2"
|
|
required
|
|
:model-value="id || defaultId"
|
|
@update:model-value="(v) => $emit('update:id', v)"
|
|
/>
|
|
</FormRow>
|
|
<div v-if="connection === MODBUS_CONNECTION.TCPIP">
|
|
<FormRow
|
|
:id="formId('modbusHost')"
|
|
:label="$t('config.modbus.host')"
|
|
:help="$t('config.modbus.hostHint')"
|
|
>
|
|
<PropertyField
|
|
:id="formId('modbusHost')"
|
|
property="host"
|
|
type="String"
|
|
class="me-2"
|
|
required
|
|
:model-value="host"
|
|
@update:model-value="(v) => $emit('update:host', v)"
|
|
/>
|
|
</FormRow>
|
|
<FormRow :id="formId('modbusPort')" :label="$t('config.modbus.port')">
|
|
<PropertyField
|
|
:id="formId('modbusPort')"
|
|
property="port"
|
|
type="Int"
|
|
class="me-2 w-50"
|
|
required
|
|
:model-value="port || defaultPort"
|
|
@update:model-value="(v) => $emit('update:port', v)"
|
|
/>
|
|
</FormRow>
|
|
<FormRow
|
|
v-if="showProtocolOptions"
|
|
:id="formId('modbusTcp')"
|
|
:label="$t('config.modbus.protocol')"
|
|
:help="
|
|
protocol === 'tcp'
|
|
? $t('config.modbus.protocolHintTcp')
|
|
: $t('config.modbus.protocolHintRtu')
|
|
"
|
|
>
|
|
<div class="btn-group" role="group">
|
|
<input
|
|
:id="formId('modbusTcp')"
|
|
v-model="protocol"
|
|
type="radio"
|
|
class="btn-check"
|
|
:name="formId('modbusProtocol')"
|
|
value="tcp"
|
|
tabindex="0"
|
|
autocomplete="off"
|
|
/>
|
|
<label class="btn btn-outline-primary" :for="formId('modbusTcp')">
|
|
{{ $t("config.modbus.protocolValueTcp") }}
|
|
</label>
|
|
<input
|
|
:id="formId('modbusRtu')"
|
|
v-model="protocol"
|
|
type="radio"
|
|
class="btn-check"
|
|
:name="formId('modbusProtocol')"
|
|
value="rtu"
|
|
tabindex="0"
|
|
autocomplete="off"
|
|
/>
|
|
<label class="btn btn-outline-primary" :for="formId('modbusRtu')">
|
|
{{ $t("config.modbus.protocolValueRtu") }}
|
|
</label>
|
|
</div>
|
|
</FormRow>
|
|
</div>
|
|
<div v-else>
|
|
<FormRow
|
|
: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
|
|
:service-values="deviceServiceValues"
|
|
/>
|
|
</FormRow>
|
|
<FormRow :id="formId('modbusBaudrate')" :label="$t('config.modbus.baudrate')">
|
|
<PropertyField
|
|
:id="formId('modbusBaudrate')"
|
|
property="baudrate"
|
|
type="Choice"
|
|
class="me-2 w-50"
|
|
:choice="baudrateOptions"
|
|
required
|
|
:model-value="baudrate || defaultBaudrate"
|
|
@update:model-value="(v) => $emit('update:baudrate', parseInt(v))"
|
|
/>
|
|
</FormRow>
|
|
<FormRow :id="formId('modbusComset')" :label="$t('config.modbus.comset')">
|
|
<PropertyField
|
|
:id="formId('modbusComset')"
|
|
property="comset"
|
|
type="Choice"
|
|
class="me-2 w-50"
|
|
:choice="comsetOptions"
|
|
required
|
|
:model-value="comset || defaultComset"
|
|
@update:model-value="(v) => $emit('update:comset', v)"
|
|
/>
|
|
</FormRow>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { defineComponent } from "vue";
|
|
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,
|
|
MODBUS_CONNECTION,
|
|
MODBUS_PROTOCOL,
|
|
MODBUS_TYPE,
|
|
} from "@/types/evcc";
|
|
|
|
export default defineComponent({
|
|
name: "Modbus",
|
|
components: { FormRow, PropertyField },
|
|
props: {
|
|
componentId: { type: String, required: true },
|
|
capabilities: {
|
|
type: Array as PropType<ModbusCapability[]>,
|
|
default: () => [],
|
|
},
|
|
modbus: String as PropType<MODBUS_TYPE>,
|
|
host: String,
|
|
port: [Number, String],
|
|
id: [Number, String],
|
|
baudrate: [Number, String],
|
|
comset: String,
|
|
device: String,
|
|
defaultPort: Number,
|
|
defaultId: Number,
|
|
defaultComset: String,
|
|
defaultBaudrate: Number,
|
|
hideModbusId: Boolean,
|
|
},
|
|
emits: [
|
|
"update:modbus",
|
|
"update:id",
|
|
"update:host",
|
|
"update:port",
|
|
"update:device",
|
|
"update:baudrate",
|
|
"update:comset",
|
|
],
|
|
data() {
|
|
return {
|
|
connection: MODBUS_CONNECTION.TCPIP as MODBUS_CONNECTION,
|
|
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;
|
|
}
|
|
return this.protocol === MODBUS_PROTOCOL.RTU
|
|
? MODBUS_TYPE.RS485_TCPIP
|
|
: MODBUS_TYPE.TCPIP;
|
|
},
|
|
showConnectionOptions() {
|
|
return this.capabilities.includes("rs485");
|
|
},
|
|
showProtocolOptions() {
|
|
return (
|
|
this.connection === MODBUS_CONNECTION.TCPIP && this.capabilities.includes("rs485")
|
|
);
|
|
},
|
|
comsetOptions() {
|
|
return Object.values(MODBUS_COMSET).map((v) => {
|
|
return { key: v, name: v };
|
|
});
|
|
},
|
|
baudrateOptions() {
|
|
return Object.values(MODBUS_BAUDRATE)
|
|
.filter((v) => typeof v === "number")
|
|
.map((v) => {
|
|
return { key: v, name: `${v}` };
|
|
});
|
|
},
|
|
},
|
|
watch: {
|
|
selectedModbus(newValue: MODBUS_TYPE) {
|
|
this.$emit("update:modbus", newValue);
|
|
},
|
|
capabilities(newValue: ModbusCapability[]) {
|
|
this.setProtocolByCapabilities(newValue);
|
|
this.$emit("update:modbus", this.selectedModbus);
|
|
},
|
|
modbus(newValue: MODBUS_TYPE, oldValue: MODBUS_TYPE) {
|
|
if (newValue) {
|
|
this.setConnectionAndProtocolByModbus(newValue, oldValue);
|
|
}
|
|
},
|
|
connection(newValue: MODBUS_CONNECTION, oldValue: MODBUS_CONNECTION) {
|
|
if (newValue !== oldValue) {
|
|
// Clear connection-specific parameters to ensure correct dependency group is used
|
|
if (newValue === MODBUS_CONNECTION.TCPIP) {
|
|
this.$emit("update:device", undefined);
|
|
} else if (newValue === MODBUS_CONNECTION.SERIAL) {
|
|
this.$emit("update:host", undefined);
|
|
}
|
|
}
|
|
this.applyServiceDefault();
|
|
},
|
|
device(newValue: string | undefined) {
|
|
// Sync prop to local state
|
|
if (newValue !== this.localDevice) {
|
|
this.localDevice = newValue;
|
|
}
|
|
},
|
|
},
|
|
mounted() {
|
|
this.localDevice = this.device;
|
|
if (this.modbus) {
|
|
this.setConnectionAndProtocolByModbus(this.modbus);
|
|
} else {
|
|
// new device: rs485-only templates default to RTU, not native TCP
|
|
this.setProtocolByCapabilities(this.capabilities);
|
|
}
|
|
this.$emit("update:modbus", this.selectedModbus);
|
|
this.updateServiceValues();
|
|
},
|
|
methods: {
|
|
setProtocolByCapabilities(capabilities: ModbusCapability[]) {
|
|
this.protocol = capabilities.includes("tcpip")
|
|
? MODBUS_PROTOCOL.TCP
|
|
: MODBUS_PROTOCOL.RTU;
|
|
},
|
|
setConnectionAndProtocolByModbus(newModbus?: MODBUS_TYPE, oldModbus?: MODBUS_TYPE) {
|
|
switch (newModbus) {
|
|
case MODBUS_TYPE.RS485_SERIAL:
|
|
this.connection = MODBUS_CONNECTION.SERIAL;
|
|
this.protocol = MODBUS_PROTOCOL.RTU;
|
|
break;
|
|
case MODBUS_TYPE.RS485_TCPIP:
|
|
this.connection = MODBUS_CONNECTION.TCPIP;
|
|
this.protocol = MODBUS_PROTOCOL.RTU;
|
|
break;
|
|
case MODBUS_TYPE.TCPIP:
|
|
this.connection = MODBUS_CONNECTION.TCPIP;
|
|
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}`;
|
|
},
|
|
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];
|
|
}
|
|
},
|
|
},
|
|
});
|
|
</script>
|