add 4 relay controls on configuration tab for sinexcel of sodistore home and pro

This commit is contained in:
Yinyin Liu 2026-05-22 09:43:38 +02:00
parent b11e3340fd
commit 646b6c0e20
7 changed files with 86 additions and 0 deletions

View File

@ -47,6 +47,13 @@ public class Configuration
public string? TimeToBuyFrom { get; set; }
public string? TimeToBuyTo { get; set; }
// Relay control (Sinexcel only, device=4) — surfaced on Configuration tab for SodistoreHome + SodistorePro.
// Nullable so WhenWritingNull keeps them out of payloads for non-Sinexcel installations.
public bool? Relay1 { get; set; }
public bool? Relay2 { get; set; }
public bool? Relay3 { get; set; }
public bool? Relay4 { get; set; }
}
public enum CalibrationChargeType

View File

@ -747,6 +747,12 @@ export type ConfigurationValues = {
timeToSellTo?: string;
timeToBuyFrom?: string;
timeToBuyTo?: string;
// For SodistoreHome + SodistorePro Sinexcel (device=4): relay control toggles
relay1?: boolean;
relay2?: boolean;
relay3?: boolean;
relay4?: boolean;
};
//
// export interface Pv {

View File

@ -161,6 +161,10 @@ function SodistoreHomeConfigurationV2(props: SodistoreHomeConfigurationProps) {
return parsed && parsed.year() >= 2020 ? parsed.toDate() : new Date();
})(),
controlPermission: String(props.values.Config.ControlPermission).toLowerCase() === "true",
relay1: String((props.values.Config as any).Relay1).toLowerCase() === "true",
relay2: String((props.values.Config as any).Relay2).toLowerCase() === "true",
relay3: String((props.values.Config as any).Relay3).toLowerCase() === "true",
relay4: String((props.values.Config as any).Relay4).toLowerCase() === "true",
dynamicPricingMode: (props.values.Config as any).DynamicPricingMode ?? 'Disabled',
currentPrice: (props.values.Config as any).CurrentPrice?.toString() ?? '',
priceToSell: (props.values.Config as any).PriceToSell?.toString() ?? '',
@ -347,6 +351,14 @@ function SodistoreHomeConfigurationV2(props: SodistoreHomeConfigurationProps) {
? new Date(formValues.stopTimeChargeandDischargeDayandTime.getTime() - formValues.stopTimeChargeandDischargeDayandTime.getTimezoneOffset() * 60000)
: null,
controlPermission:formValues.controlPermission,
// Relay control — only send for Sinexcel (device=4); leaving them undefined
// ensures they're omitted from the UDP payload for Growatt installations.
...(device === 4 ? {
relay1: Boolean(formValues.relay1),
relay2: Boolean(formValues.relay2),
relay3: Boolean(formValues.relay3),
relay4: Boolean(formValues.relay4),
} : {}),
dynamicPricingMode: formValues.dynamicPricingMode,
currentPrice: formValues.currentPrice,
priceToSell: formValues.priceToSell,
@ -522,6 +534,47 @@ function SodistoreHomeConfigurationV2(props: SodistoreHomeConfigurationProps) {
/>
</div>
{device === 4 && (
<>
<Typography variant="h6" sx={{ mt: 3, mb: 1 }}>
<FormattedMessage id="relayControl" defaultMessage="Relay Control" />
</Typography>
<Divider sx={{ mb: 2 }} />
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 3, mb: 1, ml: 1 }}>
{[1, 2, 3, 4].map((n) => {
const key = `relay${n}` as 'relay1' | 'relay2' | 'relay3' | 'relay4';
return (
<FormControlLabel
key={key}
labelPlacement="start"
control={
<Switch
name={key}
checked={Boolean(formValues[key])}
onChange={(e) => {
setFormDirty(true);
setFormValues((prev) => ({
...prev,
[key]: e.target.checked,
}));
}}
sx={{ transform: 'scale(1.2)', ml: 1 }}
/>
}
sx={{ ml: 0, mr: 0 }}
label={
<FormattedMessage
id={`relay${n}`}
defaultMessage={`Relay ${n}`}
/>
}
/>
);
})}
</Box>
</>
)}
{(device === 3 || device === 4) && (
<>
<Typography variant="h6" sx={{ mt: 3, mb: 1 }}>

View File

@ -536,6 +536,11 @@
"stopDateTime": "Stoppdatum und -zeit (Startzeit < Stoppzeit)",
"installationSetup": "Installationseinrichtung",
"batteryLimits": "Batteriegrenzwerte",
"relayControl": "Relaissteuerung",
"relay1": "Relais 1",
"relay2": "Relais 2",
"relay3": "Relais 3",
"relay4": "Relais 4",
"systemSettings": "Systemeinstellungen",
"pvPerInverter": "PV pro Wechselrichter",
"pvInInverter": "PV in Wechselrichter {number}",

View File

@ -284,6 +284,11 @@
"stopDateTime": "Stop Date and Time (Start Time < Stop Time)",
"installationSetup": "Installation Setup",
"batteryLimits": "Battery Limits",
"relayControl": "Relay Control",
"relay1": "Relay 1",
"relay2": "Relay 2",
"relay3": "Relay 3",
"relay4": "Relay 4",
"systemSettings": "System Settings",
"pvPerInverter": "PV per Inverter",
"pvInInverter": "PV in Inverter {number}",

View File

@ -536,6 +536,11 @@
"stopDateTime": "Date et heure de fin (Début < Fin)",
"installationSetup": "Configuration de l'installation",
"batteryLimits": "Limites de la batterie",
"relayControl": "Commande des relais",
"relay1": "Relais 1",
"relay2": "Relais 2",
"relay3": "Relais 3",
"relay4": "Relais 4",
"systemSettings": "Paramètres système",
"pvPerInverter": "PV par onduleur",
"pvInInverter": "PV dans l'onduleur {number}",

View File

@ -536,6 +536,11 @@
"stopDateTime": "Data e ora di fine (Inizio < Fine)",
"installationSetup": "Configurazione dell'installazione",
"batteryLimits": "Limiti della batteria",
"relayControl": "Controllo relè",
"relay1": "Relè 1",
"relay2": "Relè 2",
"relay3": "Relè 3",
"relay4": "Relè 4",
"systemSettings": "Impostazioni di sistema",
"pvPerInverter": "PV per inverter",
"pvInInverter": "PV nell'inverter {number}",