diff --git a/csharp/App/Backend/DataTypes/Installation.cs b/csharp/App/Backend/DataTypes/Installation.cs
index 54327005a..f5d6cbe48 100644
--- a/csharp/App/Backend/DataTypes/Installation.cs
+++ b/csharp/App/Backend/DataTypes/Installation.cs
@@ -48,6 +48,7 @@ public class Installation : TreeNode
public String ReadRoleId { get; set; } = "";
public String WriteRoleId { get; set; } = "";
public Boolean TestingMode { get; set; } = false;
+ public Boolean DataCollectionEnabled { get; set; } = true;
public int Status { get; set; } = -1;
public int Product { get; set; } = (int)ProductType.Salimax;
public int Device { get; set; } = 0;
diff --git a/csharp/App/Backend/Database/Db.cs b/csharp/App/Backend/Database/Db.cs
index 3ee45c6bb..fe5a8770c 100644
--- a/csharp/App/Backend/Database/Db.cs
+++ b/csharp/App/Backend/Database/Db.cs
@@ -91,6 +91,11 @@ public static partial class Db
Connection.Execute("UPDATE User SET Language = 'fr' WHERE Language = 'french'");
Connection.Execute("UPDATE User SET Language = 'it' WHERE Language = 'italian'");
+ // Backfill: SQLite-net adds new bool columns as nullable with NULL for existing rows.
+ // LINQ `.Where(i => i.DataCollectionEnabled)` translates to `WHERE ... = 1` and excludes
+ // NULL rows, which would silently disable ingestion for every pre-existing installation.
+ Connection.Execute("UPDATE Installation SET DataCollectionEnabled = 1 WHERE DataCollectionEnabled IS NULL");
+
// One-time migration: rebrand to inesco energy
Connection.Execute("UPDATE Folder SET Name = 'inesco energy' WHERE Name = 'InnovEnergy'");
Connection.Execute("UPDATE Folder SET Name = 'inesco energy' WHERE Name = 'inesco Energy'");
diff --git a/csharp/App/Backend/DeleteOldData/DeleteOldDataFromS3.cs b/csharp/App/Backend/DeleteOldData/DeleteOldDataFromS3.cs
index d6e104823..6eb53179b 100644
--- a/csharp/App/Backend/DeleteOldData/DeleteOldDataFromS3.cs
+++ b/csharp/App/Backend/DeleteOldData/DeleteOldDataFromS3.cs
@@ -39,7 +39,7 @@ public static class DeleteOldDataFromS3
{
var cutoffTimestamp = DateTimeOffset.UtcNow.AddYears(-1).ToUnixTimeSeconds();
var cutoffKey = cutoffTimestamp.ToString();
- var installations = Db.Installations.ToList();
+ var installations = Db.Installations.Where(i => i.DataCollectionEnabled).ToList();
Console.WriteLine($"[S3Cleanup] Starting cleanup for {installations.Count} installations, cutoff: {cutoffKey}");
diff --git a/csharp/App/Backend/Services/DailyIngestionService.cs b/csharp/App/Backend/Services/DailyIngestionService.cs
index 920b18a52..71981bb7a 100644
--- a/csharp/App/Backend/Services/DailyIngestionService.cs
+++ b/csharp/App/Backend/Services/DailyIngestionService.cs
@@ -50,7 +50,7 @@ public static class DailyIngestionService
Console.WriteLine($"[DailyIngestion] Starting ingestion for all SodioHome installations...");
var installations = Db.Installations
- .Where(i => (i.Product == (Int32)ProductType.SodioHome || i.Product == (Int32)ProductType.SodistorePro) && i.Device != 3) // Skip Growatt (device=3)
+ .Where(i => (i.Product == (Int32)ProductType.SodioHome || i.Product == (Int32)ProductType.SodistorePro) && i.Device != 3 && i.DataCollectionEnabled) // Skip Growatt (device=3) and installations with data collection disabled
.ToList();
foreach (var installation in installations)
@@ -75,6 +75,13 @@ public static class DailyIngestionService
///
public static async Task IngestInstallationAsync(Int64 installationId)
{
+ var installation = Db.GetInstallationById(installationId);
+ if (installation is null || !installation.DataCollectionEnabled)
+ {
+ Console.WriteLine($"[DailyIngestion] Skipping installation {installationId} (data collection disabled).");
+ return;
+ }
+
await TryIngestFromJson(installationId);
IngestFromXlsx(installationId);
}
@@ -88,6 +95,11 @@ public static class DailyIngestionService
{
var installation = Db.GetInstallationById(installationId);
if (installation is null) return;
+ if (!installation.DataCollectionEnabled)
+ {
+ Console.WriteLine($"[DailyIngestion] Skipping date-range ingest for installation {installationId} (data collection disabled).");
+ return;
+ }
var newDaily = 0;
var newHourly = 0;
diff --git a/csharp/App/Backend/Services/ReportAggregationService.cs b/csharp/App/Backend/Services/ReportAggregationService.cs
index ec71be1b2..0bc601ee8 100644
--- a/csharp/App/Backend/Services/ReportAggregationService.cs
+++ b/csharp/App/Backend/Services/ReportAggregationService.cs
@@ -106,7 +106,7 @@ public static class ReportAggregationService
Console.WriteLine("[ReportAggregation] Running Monday weekly report generation...");
var installations = Db.Installations
- .Where(i => (i.Product == (Int32)ProductType.SodioHome || i.Product == (Int32)ProductType.SodistorePro) && i.Device != 3) // Skip Growatt (device=3)
+ .Where(i => (i.Product == (Int32)ProductType.SodioHome || i.Product == (Int32)ProductType.SodistorePro) && i.Device != 3 && i.DataCollectionEnabled) // Skip Growatt (device=3) and installations with data collection disabled
.ToList();
var generated = 0;
diff --git a/typescript/frontend-marios2/src/content/dashboards/Information/InformationSodistoreHome.tsx b/typescript/frontend-marios2/src/content/dashboards/Information/InformationSodistoreHome.tsx
index eaa605f3b..3c87ad13f 100644
--- a/typescript/frontend-marios2/src/content/dashboards/Information/InformationSodistoreHome.tsx
+++ b/typescript/frontend-marios2/src/content/dashboards/Information/InformationSodistoreHome.tsx
@@ -762,6 +762,28 @@ function InformationSodistorehome(props: InformationSodistorehomeProps) {
/>
+
+
+
+
+
+
+
+
+
diff --git a/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/FlatInstallationView.tsx b/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/FlatInstallationView.tsx
index 13ddbb325..374900ece 100644
--- a/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/FlatInstallationView.tsx
+++ b/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/FlatInstallationView.tsx
@@ -209,46 +209,60 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
marginLeft: '15px'
}}
>
- {status === -1 ? (
-
) : (
- ''
- )}
+ <>
+ {status === -1 ? (
+
+ ) : (
+ ''
+ )}
- {status === -2 ? (
-
- ) : (
- ''
- )}
+ {status === -2 ? (
+
+ ) : (
+ ''
+ )}
-
+
+ >
+ )}
{installation.testingMode && (
(undefined);
const [values, setValues] = useState(null);
const status = props.current_installation.status;
+ const dataCollectionDisabled = props.current_installation.dataCollectionEnabled === false;
const [
failedToCommunicateWithInstallation,
setFailedToCommunicateWithInstallation
@@ -417,46 +418,60 @@ function SodioHomeInstallation(props: singleInstallationProps) {
marginTop: '-10px'
}}
>
- {status === -1 ? (
-
) : (
- ''
- )}
+ <>
+ {status === -1 ? (
+
+ ) : (
+ ''
+ )}
- {status === -2 ? (
-
- ) : (
- ''
- )}
+ {status === -2 ? (
+
+ ) : (
+ ''
+ )}
-
+
+ >
+ )}
{props.current_installation.testingMode && (
- {currentUser.userType !== UserType.client && (
+ {currentUser.userType !== UserType.client && !dataCollectionDisabled && (
)}
-
- }
- />
+ {!dataCollectionDisabled && (
+
+ }
+ />
+ )}
- {currentUser.userType !== UserType.client && (
+ {currentUser.userType !== UserType.client && !dataCollectionDisabled && (
)}
- {currentUser.userType == UserType.admin && (
+ {currentUser.userType == UserType.admin && !dataCollectionDisabled && (
)} */}
-
- }
- />
+ {!dataCollectionDisabled && (
+
+ }
+ />
+ )}
- {props.current_installation.device !== 3 && (
+ {props.current_installation.device !== 3 && !dataCollectionDisabled && (
)}
+
+
+
+
+
+
+
+
+
{tabs
.filter((tab) => !(isGrowatt && tab.value === 'report'))
+ .filter((tab) => !dataCollectionDisabled || allowedWhenDisabled.includes(tab.value))
.map((tab) => (
{singleInstallationTabs
.filter((tab) => !(isGrowatt && tab.value === 'report'))
+ .filter((tab) => !dataCollectionDisabled || allowedWhenDisabled.includes(tab.value))
.map((tab) => (
- {status === -1 ? (
-
) : (
- ''
- )}
+ <>
+ {status === -1 ? (
+
+ ) : (
+ ''
+ )}
- {status === -2 ? (
-
- ) : (
- ''
- )}
+ {status === -2 ? (
+
+ ) : (
+ ''
+ )}
-
+
+ >
+ )}
)}
diff --git a/typescript/frontend-marios2/src/interfaces/InstallationTypes.tsx b/typescript/frontend-marios2/src/interfaces/InstallationTypes.tsx
index 1d91e5680..a90fd1c90 100644
--- a/typescript/frontend-marios2/src/interfaces/InstallationTypes.tsx
+++ b/typescript/frontend-marios2/src/interfaces/InstallationTypes.tsx
@@ -36,6 +36,7 @@ export interface I_Installation extends I_S3Credentials {
product: number;
device: number;
testingMode?: boolean;
+ dataCollectionEnabled?: boolean;
status?: number;
serialNumber?: string;
networkProvider: string;
diff --git a/typescript/frontend-marios2/src/lang/de.json b/typescript/frontend-marios2/src/lang/de.json
index d05706670..798a2068a 100644
--- a/typescript/frontend-marios2/src/lang/de.json
+++ b/typescript/frontend-marios2/src/lang/de.json
@@ -86,6 +86,9 @@
"externalEmsOther": "Externes EMS (angeben)",
"emsNo": "Nein",
"emsOther": "Andere",
+ "yes": "Ja",
+ "no": "Nein",
+ "dataCollectionEnabled": "Datenerfassung",
"generalInfo": "Allgemeine Informationen",
"installationSetup": "Installationseinrichtung",
"couplingType": "AC/DC-Kopplung",
diff --git a/typescript/frontend-marios2/src/lang/en.json b/typescript/frontend-marios2/src/lang/en.json
index ddf33dcde..21de73b3a 100644
--- a/typescript/frontend-marios2/src/lang/en.json
+++ b/typescript/frontend-marios2/src/lang/en.json
@@ -68,6 +68,9 @@
"externalEmsOther": "External EMS (specify)",
"emsNo": "No",
"emsOther": "Other",
+ "yes": "Yes",
+ "no": "No",
+ "dataCollectionEnabled": "Data Collection",
"generalInfo": "General Info",
"installationSetup": "Installation Setup",
"couplingType": "AC/DC Coupling",
diff --git a/typescript/frontend-marios2/src/lang/fr.json b/typescript/frontend-marios2/src/lang/fr.json
index 79aa856bd..e99292d0e 100644
--- a/typescript/frontend-marios2/src/lang/fr.json
+++ b/typescript/frontend-marios2/src/lang/fr.json
@@ -80,6 +80,9 @@
"externalEmsOther": "EMS externe (préciser)",
"emsNo": "Non",
"emsOther": "Autre",
+ "yes": "Oui",
+ "no": "Non",
+ "dataCollectionEnabled": "Collecte de données",
"generalInfo": "Informations générales",
"installationSetup": "Configuration de l'installation",
"couplingType": "Couplage AC/DC",
diff --git a/typescript/frontend-marios2/src/lang/it.json b/typescript/frontend-marios2/src/lang/it.json
index 4cbfbdaea..151ba9bf9 100644
--- a/typescript/frontend-marios2/src/lang/it.json
+++ b/typescript/frontend-marios2/src/lang/it.json
@@ -68,6 +68,9 @@
"externalEmsOther": "EMS esterno (specificare)",
"emsNo": "No",
"emsOther": "Altro",
+ "yes": "Sì",
+ "no": "No",
+ "dataCollectionEnabled": "Raccolta dati",
"generalInfo": "Informazioni generali",
"installationSetup": "Configurazione installazione",
"couplingType": "Accoppiamento AC/DC",