added Overview Page without last week button for SodistoreHome

This commit is contained in:
Yinyin Liu 2026-02-11 13:30:34 +01:00
parent 2895b11efc
commit 1b6d5a5916
5 changed files with 156 additions and 73 deletions

View File

@ -194,9 +194,13 @@ public class Controller : ControllerBase
while (startTimestamp <= endTimestamp)
{
string bucketPath = installation.Product==(int)ProductType.Salimax || installation.Product==(int)ProductType.SodiStoreMax?
"s3://"+installation.S3BucketId + "-3e5b3069-214a-43ee-8d85-57d72000c19d/"+startTimestamp :
"s3://"+installation.S3BucketId + "-c0436b6a-d276-4cd8-9c44-1eae86cf5d0e/"+startTimestamp;
string bucketPath;
if (installation.Product == (int)ProductType.Salimax || installation.Product == (int)ProductType.SodiStoreMax)
bucketPath = "s3://" + installation.S3BucketId + "-3e5b3069-214a-43ee-8d85-57d72000c19d/" + startTimestamp;
else if (installation.Product == (int)ProductType.SodioHome)
bucketPath = "s3://" + installation.S3BucketId + "-e7b9a240-3c5d-4d2e-a019-6d8b1f7b73fa/" + startTimestamp;
else
bucketPath = "s3://" + installation.S3BucketId + "-c0436b6a-d276-4cd8-9c44-1eae86cf5d0e/" + startTimestamp;
Console.WriteLine("Fetching data for "+startTimestamp);
try

View File

@ -118,6 +118,12 @@ function Overview(props: OverviewProps) {
resultPromise
.then((result) => {
if (result.chartData.soc.data.length === 0) {
setDateSelectionError('No data available for the selected date range. Please choose a more recent date.');
setErrorDateModalOpen(true);
setLoading(false);
return;
}
setDailyDataArray((prevData) =>
prevData.concat({
chartData: result.chartData,
@ -281,6 +287,12 @@ function Overview(props: OverviewProps) {
resultPromise
.then((result) => {
if (result.chartData.soc.data.length === 0) {
setDateSelectionError('No data available for the selected date range. Please choose a more recent date.');
setErrorDateModalOpen(true);
setLoading(false);
return;
}
setDailyDataArray((prevData) =>
prevData.concat({
chartData: result.chartData,
@ -511,20 +523,22 @@ function Overview(props: OverviewProps) {
>
<FormattedMessage id="24_hours" defaultMessage="24-hours" />
</Button>
<Button
variant="contained"
onClick={handleWeekData}
disabled={loading}
sx={{
marginTop: '20px',
marginLeft: '10px',
backgroundColor: aggregatedData ? '#808080' : '#ffc04d',
color: '#000000',
'&:hover': { bgcolor: '#f7b34d' }
}}
>
<FormattedMessage id="lastweek" defaultMessage="Last week" />
</Button>
{product !== 2 && (
<Button
variant="contained"
onClick={handleWeekData}
disabled={loading}
sx={{
marginTop: '20px',
marginLeft: '10px',
backgroundColor: aggregatedData ? '#808080' : '#ffc04d',
color: '#000000',
'&:hover': { bgcolor: '#f7b34d' }
}}
>
<FormattedMessage id="lastweek" defaultMessage="Last week" />
</Button>
)}
{/*{aggregatedData && (*/}
<Button
@ -1013,7 +1027,7 @@ function Overview(props: OverviewProps) {
alignItems="stretch"
spacing={3}
>
<Grid item md={6} xs={12}>
<Grid item md={product === 2 ? 12 : 6} xs={12}>
<Card
sx={{
overflow: 'visible',
@ -1074,6 +1088,7 @@ function Overview(props: OverviewProps) {
/>
</Card>
</Grid>
{product !== 2 && (
<Grid item md={6} xs={12}>
<Card
sx={{
@ -1136,6 +1151,7 @@ function Overview(props: OverviewProps) {
/>
</Card>
</Grid>
)}
</Grid>
)}
@ -1336,7 +1352,7 @@ function Overview(props: OverviewProps) {
alignItems="stretch"
spacing={3}
>
<Grid item md={6} xs={12}>
<Grid item md={product === 2 ? 12 : 6} xs={12}>
<Card
sx={{
overflow: 'visible',
@ -1397,6 +1413,7 @@ function Overview(props: OverviewProps) {
/>
</Card>
</Grid>
{product !== 2 && (
<Grid item md={6} xs={12}>
<Card
sx={{
@ -1458,6 +1475,7 @@ function Overview(props: OverviewProps) {
/>
</Card>
</Grid>
)}
</Grid>
)}
</Grid>

View File

@ -26,6 +26,7 @@ import { FetchResult } from '../../../dataCache/dataCache';
import BatteryViewSodioHome from '../BatteryView/BatteryViewSodioHome';
import SodistoreHomeConfiguration from './SodistoreHomeConfiguration';
import TopologySodistoreHome from '../Topology/TopologySodistoreHome';
import Overview from '../Overview/overview';
interface singleInstallationProps {
current_installation?: I_Installation;
@ -541,6 +542,16 @@ function SodioHomeInstallation(props: singleInstallationProps) {
/>
)}
<Route
path={routes.overview}
element={
<Overview
s3Credentials={s3Credentials}
id={props.current_installation.id}
/>
}
/>
<Route
path={'*'}
element={<Navigate to={routes.live}></Navigate>}

View File

@ -24,10 +24,10 @@ function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) {
const { currentUser } = context;
const tabList = [
'live',
'overview',
'batteryview',
'information',
'manage',
'overview',
'log',
'history',
'configuration'
@ -100,6 +100,10 @@ function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) {
value: 'live',
label: <FormattedMessage id="live" defaultMessage="Live" />
},
{
value: 'overview',
label: <FormattedMessage id="overview" defaultMessage="Overview" />
},
{
value: 'batteryview',
label: (
@ -109,10 +113,6 @@ function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) {
/>
)
},
// {
// value: 'overview',
// label: <FormattedMessage id="overview" defaultMessage="Overview" />
// },
{
value: 'log',
label: <FormattedMessage id="log" defaultMessage="Log" />
@ -159,11 +159,10 @@ function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) {
value: 'live',
label: <FormattedMessage id="live" defaultMessage="Live" />
},
// {
// value: 'overview',
// label: <FormattedMessage id="overview" defaultMessage="Overview" />
// },
{
value: 'overview',
label: <FormattedMessage id="overview" defaultMessage="Overview" />
},
{
value: 'information',
label: (
@ -190,6 +189,10 @@ function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) {
value: 'live',
label: <FormattedMessage id="live" defaultMessage="Live" />
},
{
value: 'overview',
label: <FormattedMessage id="overview" defaultMessage="Overview" />
},
{
value: 'batteryview',
label: (
@ -199,10 +202,6 @@ function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) {
/>
)
},
// {
// value: 'overview',
// label: <FormattedMessage id="overview" defaultMessage="Overview" />
// },
{
value: 'log',
label: <FormattedMessage id="log" defaultMessage="Log" />

View File

@ -310,21 +310,31 @@ export const transformInputToDailyDataJson = async (
const prefixes = ['', 'k', 'M', 'G', 'T'];
const MAX_NUMBER = 9999999;
const pathsToSearch = [
'Battery.Soc',
product == 0 ? 'Battery.Temperature' : 'Battery.TemperatureCell1',
//'Battery.Temperature' for salimax,
//'Battery.TemperatureCell1',
product == 0 ? 'Battery.Dc.Power' : 'Battery.Power',
//'Battery.Dc.Power' for salimax,
// 'Battery.Power',
'GridMeter.Ac.Power.Active',
'PvOnDc',
'DcDc.Dc.Link.Voltage',
'LoadOnAcGrid.Power.Active',
'LoadOnDc.Power'
];
// For SodioHome (product=2), paths are placeholders — actual extraction uses
// custom fallback logic to handle differences between Growatt and Sinexcel.
// Growatt has: Battery1AmbientTemperature, GridPower, PvPower
// Sinexcel has: Battery1Temperature, TotalGridPower (meter may be offline), PvPower1-4
const pathsToSearch = product == 2
? [
'SODIOHOME_SOC',
'SODIOHOME_TEMPERATURE',
'SODIOHOME_BATTERY_POWER',
'SODIOHOME_GRID_POWER',
'SODIOHOME_PV_POWER',
null, // dcBusVoltage not available for SodioHome
'SODIOHOME_CONSUMPTION',
null // DCLoad not available for SodioHome
]
: [
'Battery.Soc',
product == 0 ? 'Battery.Temperature' : 'Battery.TemperatureCell1',
product == 0 ? 'Battery.Dc.Power' : 'Battery.Power',
'GridMeter.Ac.Power.Active',
'PvOnDc',
'DcDc.Dc.Link.Voltage',
'LoadOnAcGrid.Power.Active',
'LoadOnDc.Power'
];
const categories = [
'soc',
'temperature',
@ -419,37 +429,78 @@ export const transformInputToDailyDataJson = async (
let category_index = 0;
// eslint-disable-next-line @typescript-eslint/no-loop-func
pathsToSearch.forEach((path) => {
if (get(result, path) !== undefined) {
let value: number | undefined = undefined;
if (path === null) {
// Skip unavailable fields (e.g. dcBusVoltage, DCLoad for SodioHome)
category_index++;
return;
}
if (category_index === 4) {
// Custom logic for 'PvOnDc.Dc.Power'
let value: number | undefined = undefined;
if (product === 2) {
// SodioHome: custom extraction with fallbacks for Growatt/Sinexcel
const inv = result?.InverterRecord;
if (inv) {
switch (category_index) {
case 0: // soc
value = inv.Battery1Soc;
break;
case 1: // temperature
// Growatt: Battery1AmbientTemperature, Sinexcel: Battery1Temperature
value = inv.Battery1AmbientTemperature ?? inv.Battery1Temperature;
break;
case 2: // battery power
value = inv.Battery1Power;
break;
case 3: // grid power
// Growatt: GridPower (always valid), Sinexcel: GridPower may be 0 when
// electric meter is offline, TotalGridPower is the reliable fallback
value = inv.TotalGridPower ?? inv.GridPower;
break;
case 4: // pv production
// Growatt: PvPower (aggregated), Sinexcel: PvTotalPower or sum PvPower1-4
value =
inv.PvPower ??
inv.PvTotalPower ??
['PvPower1', 'PvPower2', 'PvPower3', 'PvPower4']
.map((key) => inv[key] ?? 0)
.reduce((sum, val) => sum + val, 0);
break;
case 6: // consumption
value = inv.ConsumptionPower;
break;
}
}
} else if (category_index === 4) {
// Salimax/Salidomo: Custom logic for 'PvOnDc.Dc.Power'
if (get(result, path) !== undefined) {
value = Object.values(
result.PvOnDc as Record<string, { Dc?: { Power?: number } }>
).reduce((sum, device) => sum + (device.Dc?.Power || 0), 0);
} else if (get(result, path) !== undefined) {
// Default path-based extraction
value = path
.split('.')
.reduce((o, key) => (o ? o[key] : undefined), result);
}
// Only push value if defined
if (value !== undefined) {
if (value < chartOverview[categories[category_index]].min) {
chartOverview[categories[category_index]].min = value;
}
if (value > chartOverview[categories[category_index]].max) {
chartOverview[categories[category_index]].max = value;
}
chartData[categories[category_index]].data.push([
adjustedTimestampArray[i],
value
]);
}
} else if (get(result, path) !== undefined) {
// Default path-based extraction
value = path
.split('.')
.reduce((o, key) => (o ? o[key] : undefined), result);
}
// Only push value if defined
if (value !== undefined) {
if (value < chartOverview[categories[category_index]].min) {
chartOverview[categories[category_index]].min = value;
}
if (value > chartOverview[categories[category_index]].max) {
chartOverview[categories[category_index]].max = value;
}
chartData[categories[category_index]].data.push([
adjustedTimestampArray[i],
value
]);
}
category_index++;
});
}