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) while (startTimestamp <= endTimestamp)
{ {
string bucketPath = installation.Product==(int)ProductType.Salimax || installation.Product==(int)ProductType.SodiStoreMax? string bucketPath;
"s3://"+installation.S3BucketId + "-3e5b3069-214a-43ee-8d85-57d72000c19d/"+startTimestamp : if (installation.Product == (int)ProductType.Salimax || installation.Product == (int)ProductType.SodiStoreMax)
"s3://"+installation.S3BucketId + "-c0436b6a-d276-4cd8-9c44-1eae86cf5d0e/"+startTimestamp; 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); Console.WriteLine("Fetching data for "+startTimestamp);
try try

View File

@ -118,6 +118,12 @@ function Overview(props: OverviewProps) {
resultPromise resultPromise
.then((result) => { .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) => setDailyDataArray((prevData) =>
prevData.concat({ prevData.concat({
chartData: result.chartData, chartData: result.chartData,
@ -281,6 +287,12 @@ function Overview(props: OverviewProps) {
resultPromise resultPromise
.then((result) => { .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) => setDailyDataArray((prevData) =>
prevData.concat({ prevData.concat({
chartData: result.chartData, chartData: result.chartData,
@ -511,6 +523,7 @@ function Overview(props: OverviewProps) {
> >
<FormattedMessage id="24_hours" defaultMessage="24-hours" /> <FormattedMessage id="24_hours" defaultMessage="24-hours" />
</Button> </Button>
{product !== 2 && (
<Button <Button
variant="contained" variant="contained"
onClick={handleWeekData} onClick={handleWeekData}
@ -525,6 +538,7 @@ function Overview(props: OverviewProps) {
> >
<FormattedMessage id="lastweek" defaultMessage="Last week" /> <FormattedMessage id="lastweek" defaultMessage="Last week" />
</Button> </Button>
)}
{/*{aggregatedData && (*/} {/*{aggregatedData && (*/}
<Button <Button
@ -1013,7 +1027,7 @@ function Overview(props: OverviewProps) {
alignItems="stretch" alignItems="stretch"
spacing={3} spacing={3}
> >
<Grid item md={6} xs={12}> <Grid item md={product === 2 ? 12 : 6} xs={12}>
<Card <Card
sx={{ sx={{
overflow: 'visible', overflow: 'visible',
@ -1074,6 +1088,7 @@ function Overview(props: OverviewProps) {
/> />
</Card> </Card>
</Grid> </Grid>
{product !== 2 && (
<Grid item md={6} xs={12}> <Grid item md={6} xs={12}>
<Card <Card
sx={{ sx={{
@ -1136,6 +1151,7 @@ function Overview(props: OverviewProps) {
/> />
</Card> </Card>
</Grid> </Grid>
)}
</Grid> </Grid>
)} )}
@ -1336,7 +1352,7 @@ function Overview(props: OverviewProps) {
alignItems="stretch" alignItems="stretch"
spacing={3} spacing={3}
> >
<Grid item md={6} xs={12}> <Grid item md={product === 2 ? 12 : 6} xs={12}>
<Card <Card
sx={{ sx={{
overflow: 'visible', overflow: 'visible',
@ -1397,6 +1413,7 @@ function Overview(props: OverviewProps) {
/> />
</Card> </Card>
</Grid> </Grid>
{product !== 2 && (
<Grid item md={6} xs={12}> <Grid item md={6} xs={12}>
<Card <Card
sx={{ sx={{
@ -1458,6 +1475,7 @@ function Overview(props: OverviewProps) {
/> />
</Card> </Card>
</Grid> </Grid>
)}
</Grid> </Grid>
)} )}
</Grid> </Grid>

View File

@ -26,6 +26,7 @@ import { FetchResult } from '../../../dataCache/dataCache';
import BatteryViewSodioHome from '../BatteryView/BatteryViewSodioHome'; import BatteryViewSodioHome from '../BatteryView/BatteryViewSodioHome';
import SodistoreHomeConfiguration from './SodistoreHomeConfiguration'; import SodistoreHomeConfiguration from './SodistoreHomeConfiguration';
import TopologySodistoreHome from '../Topology/TopologySodistoreHome'; import TopologySodistoreHome from '../Topology/TopologySodistoreHome';
import Overview from '../Overview/overview';
interface singleInstallationProps { interface singleInstallationProps {
current_installation?: I_Installation; 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 <Route
path={'*'} path={'*'}
element={<Navigate to={routes.live}></Navigate>} element={<Navigate to={routes.live}></Navigate>}

View File

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

View File

@ -310,15 +310,25 @@ export const transformInputToDailyDataJson = async (
const prefixes = ['', 'k', 'M', 'G', 'T']; const prefixes = ['', 'k', 'M', 'G', 'T'];
const MAX_NUMBER = 9999999; const MAX_NUMBER = 9999999;
const pathsToSearch = [ // 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', 'Battery.Soc',
product == 0 ? 'Battery.Temperature' : 'Battery.TemperatureCell1', product == 0 ? 'Battery.Temperature' : 'Battery.TemperatureCell1',
//'Battery.Temperature' for salimax,
//'Battery.TemperatureCell1',
product == 0 ? 'Battery.Dc.Power' : 'Battery.Power', product == 0 ? 'Battery.Dc.Power' : 'Battery.Power',
//'Battery.Dc.Power' for salimax,
// 'Battery.Power',
'GridMeter.Ac.Power.Active', 'GridMeter.Ac.Power.Active',
'PvOnDc', 'PvOnDc',
'DcDc.Dc.Link.Voltage', 'DcDc.Dc.Link.Voltage',
@ -419,14 +429,55 @@ export const transformInputToDailyDataJson = async (
let category_index = 0; let category_index = 0;
// eslint-disable-next-line @typescript-eslint/no-loop-func // eslint-disable-next-line @typescript-eslint/no-loop-func
pathsToSearch.forEach((path) => { pathsToSearch.forEach((path) => {
if (get(result, path) !== undefined) { if (path === null) {
// Skip unavailable fields (e.g. dcBusVoltage, DCLoad for SodioHome)
category_index++;
return;
}
let value: number | undefined = undefined; let value: number | undefined = undefined;
if (category_index === 4) { if (product === 2) {
// Custom logic for 'PvOnDc.Dc.Power' // 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( value = Object.values(
result.PvOnDc as Record<string, { Dc?: { Power?: number } }> result.PvOnDc as Record<string, { Dc?: { Power?: number } }>
).reduce((sum, device) => sum + (device.Dc?.Power || 0), 0); ).reduce((sum, device) => sum + (device.Dc?.Power || 0), 0);
}
} else if (get(result, path) !== undefined) { } else if (get(result, path) !== undefined) {
// Default path-based extraction // Default path-based extraction
value = path value = path
@ -449,7 +500,7 @@ export const transformInputToDailyDataJson = async (
value value
]); ]);
} }
}
category_index++; category_index++;
}); });
} }