added yes or no data collection mode
This commit is contained in:
parent
52c9a42e42
commit
5bced9374b
|
|
@ -48,6 +48,7 @@ public class Installation : TreeNode
|
||||||
public String ReadRoleId { get; set; } = "";
|
public String ReadRoleId { get; set; } = "";
|
||||||
public String WriteRoleId { get; set; } = "";
|
public String WriteRoleId { get; set; } = "";
|
||||||
public Boolean TestingMode { get; set; } = false;
|
public Boolean TestingMode { get; set; } = false;
|
||||||
|
public Boolean DataCollectionEnabled { get; set; } = true;
|
||||||
public int Status { get; set; } = -1;
|
public int Status { get; set; } = -1;
|
||||||
public int Product { get; set; } = (int)ProductType.Salimax;
|
public int Product { get; set; } = (int)ProductType.Salimax;
|
||||||
public int Device { get; set; } = 0;
|
public int Device { get; set; } = 0;
|
||||||
|
|
|
||||||
|
|
@ -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 = 'fr' WHERE Language = 'french'");
|
||||||
Connection.Execute("UPDATE User SET Language = 'it' WHERE Language = 'italian'");
|
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
|
// 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 = 'InnovEnergy'");
|
||||||
Connection.Execute("UPDATE Folder SET Name = 'inesco energy' WHERE Name = 'inesco Energy'");
|
Connection.Execute("UPDATE Folder SET Name = 'inesco energy' WHERE Name = 'inesco Energy'");
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ public static class DeleteOldDataFromS3
|
||||||
{
|
{
|
||||||
var cutoffTimestamp = DateTimeOffset.UtcNow.AddYears(-1).ToUnixTimeSeconds();
|
var cutoffTimestamp = DateTimeOffset.UtcNow.AddYears(-1).ToUnixTimeSeconds();
|
||||||
var cutoffKey = cutoffTimestamp.ToString();
|
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}");
|
Console.WriteLine($"[S3Cleanup] Starting cleanup for {installations.Count} installations, cutoff: {cutoffKey}");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ public static class DailyIngestionService
|
||||||
Console.WriteLine($"[DailyIngestion] Starting ingestion for all SodioHome installations...");
|
Console.WriteLine($"[DailyIngestion] Starting ingestion for all SodioHome installations...");
|
||||||
|
|
||||||
var installations = Db.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();
|
.ToList();
|
||||||
|
|
||||||
foreach (var installation in installations)
|
foreach (var installation in installations)
|
||||||
|
|
@ -75,6 +75,13 @@ public static class DailyIngestionService
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static async Task IngestInstallationAsync(Int64 installationId)
|
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);
|
await TryIngestFromJson(installationId);
|
||||||
IngestFromXlsx(installationId);
|
IngestFromXlsx(installationId);
|
||||||
}
|
}
|
||||||
|
|
@ -88,6 +95,11 @@ public static class DailyIngestionService
|
||||||
{
|
{
|
||||||
var installation = Db.GetInstallationById(installationId);
|
var installation = Db.GetInstallationById(installationId);
|
||||||
if (installation is null) return;
|
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 newDaily = 0;
|
||||||
var newHourly = 0;
|
var newHourly = 0;
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@ public static class ReportAggregationService
|
||||||
Console.WriteLine("[ReportAggregation] Running Monday weekly report generation...");
|
Console.WriteLine("[ReportAggregation] Running Monday weekly report generation...");
|
||||||
|
|
||||||
var installations = Db.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();
|
.ToList();
|
||||||
|
|
||||||
var generated = 0;
|
var generated = 0;
|
||||||
|
|
|
||||||
|
|
@ -762,6 +762,28 @@ function InformationSodistorehome(props: InformationSodistorehomeProps) {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<FormControl fullWidth sx={{ marginLeft: 1, marginTop: 1, marginBottom: 1, width: 440 }}>
|
||||||
|
<InputLabel sx={{ fontSize: 14, backgroundColor: 'white' }}>
|
||||||
|
<FormattedMessage id="dataCollectionEnabled" defaultMessage="Data Collection" />
|
||||||
|
</InputLabel>
|
||||||
|
<Select
|
||||||
|
name="dataCollectionEnabled"
|
||||||
|
value={formValues.dataCollectionEnabled === false ? 'no' : 'yes'}
|
||||||
|
onChange={(e) =>
|
||||||
|
setFormValues({
|
||||||
|
...formValues,
|
||||||
|
dataCollectionEnabled: e.target.value === 'yes'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
inputProps={{ readOnly: !canEdit }}
|
||||||
|
>
|
||||||
|
<MenuItem value="yes"><FormattedMessage id="yes" defaultMessage="Yes" /></MenuItem>
|
||||||
|
<MenuItem value="no"><FormattedMessage id="no" defaultMessage="No" /></MenuItem>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<FormControl fullWidth sx={{ marginLeft: 1, marginTop: 1, marginBottom: 1, width: 440 }}>
|
<FormControl fullWidth sx={{ marginLeft: 1, marginTop: 1, marginBottom: 1, width: 440 }}>
|
||||||
<InputLabel sx={{ fontSize: 14, backgroundColor: 'white' }}>
|
<InputLabel sx={{ fontSize: 14, backgroundColor: 'white' }}>
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,18 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
||||||
marginLeft: '15px'
|
marginLeft: '15px'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
{installation.dataCollectionEnabled === false ? (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: '20px',
|
||||||
|
height: '20px',
|
||||||
|
marginLeft: '2px',
|
||||||
|
borderRadius: '50%',
|
||||||
|
backgroundColor: 'grey'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
{status === -1 ? (
|
{status === -1 ? (
|
||||||
<CancelIcon
|
<CancelIcon
|
||||||
style={{
|
style={{
|
||||||
|
|
@ -249,6 +261,8 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
||||||
: 'green'
|
: 'green'
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
{installation.testingMode && (
|
{installation.testingMode && (
|
||||||
<BuildIcon
|
<BuildIcon
|
||||||
style={{
|
style={{
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,7 @@ function SodioHomeInstallation(props: singleInstallationProps) {
|
||||||
const [currentTab, setCurrentTab] = useState<string>(undefined);
|
const [currentTab, setCurrentTab] = useState<string>(undefined);
|
||||||
const [values, setValues] = useState<JSONRecordData | null>(null);
|
const [values, setValues] = useState<JSONRecordData | null>(null);
|
||||||
const status = props.current_installation.status;
|
const status = props.current_installation.status;
|
||||||
|
const dataCollectionDisabled = props.current_installation.dataCollectionEnabled === false;
|
||||||
const [
|
const [
|
||||||
failedToCommunicateWithInstallation,
|
failedToCommunicateWithInstallation,
|
||||||
setFailedToCommunicateWithInstallation
|
setFailedToCommunicateWithInstallation
|
||||||
|
|
@ -417,6 +418,18 @@ function SodioHomeInstallation(props: singleInstallationProps) {
|
||||||
marginTop: '-10px'
|
marginTop: '-10px'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
{dataCollectionDisabled ? (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: '20px',
|
||||||
|
height: '20px',
|
||||||
|
marginLeft: '2px',
|
||||||
|
borderRadius: '50%',
|
||||||
|
backgroundColor: 'grey'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
{status === -1 ? (
|
{status === -1 ? (
|
||||||
<CancelIcon
|
<CancelIcon
|
||||||
style={{
|
style={{
|
||||||
|
|
@ -457,6 +470,8 @@ function SodioHomeInstallation(props: singleInstallationProps) {
|
||||||
: 'green'
|
: 'green'
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
{props.current_installation.testingMode && (
|
{props.current_installation.testingMode && (
|
||||||
<BuildIcon
|
<BuildIcon
|
||||||
|
|
@ -521,7 +536,7 @@ function SodioHomeInstallation(props: singleInstallationProps) {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{currentUser.userType !== UserType.client && (
|
{currentUser.userType !== UserType.client && !dataCollectionDisabled && (
|
||||||
<Route
|
<Route
|
||||||
path={routes.log}
|
path={routes.log}
|
||||||
element={
|
element={
|
||||||
|
|
@ -534,6 +549,7 @@ function SodioHomeInstallation(props: singleInstallationProps) {
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{!dataCollectionDisabled && (
|
||||||
<Route
|
<Route
|
||||||
path={routes.live}
|
path={routes.live}
|
||||||
element={
|
element={
|
||||||
|
|
@ -545,8 +561,9 @@ function SodioHomeInstallation(props: singleInstallationProps) {
|
||||||
></TopologySodistoreHome>
|
></TopologySodistoreHome>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{currentUser.userType !== UserType.client && (
|
{currentUser.userType !== UserType.client && !dataCollectionDisabled && (
|
||||||
<Route
|
<Route
|
||||||
path={routes.batteryview + '/*'}
|
path={routes.batteryview + '/*'}
|
||||||
element={
|
element={
|
||||||
|
|
@ -573,7 +590,7 @@ function SodioHomeInstallation(props: singleInstallationProps) {
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{currentUser.userType == UserType.admin && (
|
{currentUser.userType == UserType.admin && !dataCollectionDisabled && (
|
||||||
<Route
|
<Route
|
||||||
path={routes.configuration}
|
path={routes.configuration}
|
||||||
element={
|
element={
|
||||||
|
|
@ -600,6 +617,7 @@ function SodioHomeInstallation(props: singleInstallationProps) {
|
||||||
/>
|
/>
|
||||||
)} */}
|
)} */}
|
||||||
|
|
||||||
|
{!dataCollectionDisabled && (
|
||||||
<Route
|
<Route
|
||||||
path={routes.overview}
|
path={routes.overview}
|
||||||
element={
|
element={
|
||||||
|
|
@ -613,8 +631,9 @@ function SodioHomeInstallation(props: singleInstallationProps) {
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{props.current_installation.device !== 3 && (
|
{props.current_installation.device !== 3 && !dataCollectionDisabled && (
|
||||||
<Route
|
<Route
|
||||||
path={routes.report}
|
path={routes.report}
|
||||||
element={
|
element={
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ function SodistorehomeInstallationForm(props: SodistorehomeInstallationFormPros)
|
||||||
vpnIp: '',
|
vpnIp: '',
|
||||||
installationModel: '',
|
installationModel: '',
|
||||||
externalEms: 'No',
|
externalEms: 'No',
|
||||||
|
dataCollectionEnabled: true,
|
||||||
...(isSodistorePro ? { device: 4 } : {}),
|
...(isSodistorePro ? { device: 4 } : {}),
|
||||||
});
|
});
|
||||||
const [inverterCount, setInverterCount] = useState('');
|
const [inverterCount, setInverterCount] = useState('');
|
||||||
|
|
@ -249,6 +250,46 @@ function SodistorehomeInstallationForm(props: SodistorehomeInstallationFormPros)
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<FormControl
|
||||||
|
fullWidth
|
||||||
|
sx={{
|
||||||
|
marginTop: 1,
|
||||||
|
marginBottom: 1,
|
||||||
|
width: 390
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<InputLabel
|
||||||
|
sx={{
|
||||||
|
fontSize: 14,
|
||||||
|
backgroundColor: 'white'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id="dataCollectionEnabled"
|
||||||
|
defaultMessage="Data Collection"
|
||||||
|
/>
|
||||||
|
</InputLabel>
|
||||||
|
<Select
|
||||||
|
name="dataCollectionEnabled"
|
||||||
|
value={formValues.dataCollectionEnabled ? 'yes' : 'no'}
|
||||||
|
onChange={(e) =>
|
||||||
|
setFormValues({
|
||||||
|
...formValues,
|
||||||
|
dataCollectionEnabled: e.target.value === 'yes'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<MenuItem value="yes">
|
||||||
|
<FormattedMessage id="yes" defaultMessage="Yes" />
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem value="no">
|
||||||
|
<FormattedMessage id="no" defaultMessage="No" />
|
||||||
|
</MenuItem>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
</div>
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
|
|
|
||||||
|
|
@ -275,6 +275,12 @@ function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) {
|
||||||
const isGrowatt = currentInstallation?.device === 3
|
const isGrowatt = currentInstallation?.device === 3
|
||||||
|| (installations.length === 1 && installations[0].device === 3);
|
|| (installations.length === 1 && installations[0].device === 3);
|
||||||
|
|
||||||
|
// When data collection is disabled, only navigation, info, history, tickets, documents remain.
|
||||||
|
const dataCollectionDisabled =
|
||||||
|
currentInstallation?.dataCollectionEnabled === false
|
||||||
|
|| (installations.length === 1 && installations[0].dataCollectionEnabled === false);
|
||||||
|
const allowedWhenDisabled = ['list', 'tree', 'information', 'history', 'installationTickets', 'documents'];
|
||||||
|
|
||||||
const tabs = inInstallationView && currentUser.userType == UserType.admin
|
const tabs = inInstallationView && currentUser.userType == UserType.admin
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
|
|
@ -471,6 +477,7 @@ function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) {
|
||||||
>
|
>
|
||||||
{tabs
|
{tabs
|
||||||
.filter((tab) => !(isGrowatt && tab.value === 'report'))
|
.filter((tab) => !(isGrowatt && tab.value === 'report'))
|
||||||
|
.filter((tab) => !dataCollectionDisabled || allowedWhenDisabled.includes(tab.value))
|
||||||
.map((tab) => (
|
.map((tab) => (
|
||||||
<Tab
|
<Tab
|
||||||
key={tab.value}
|
key={tab.value}
|
||||||
|
|
@ -544,6 +551,7 @@ function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) {
|
||||||
>
|
>
|
||||||
{singleInstallationTabs
|
{singleInstallationTabs
|
||||||
.filter((tab) => !(isGrowatt && tab.value === 'report'))
|
.filter((tab) => !(isGrowatt && tab.value === 'report'))
|
||||||
|
.filter((tab) => !dataCollectionDisabled || allowedWhenDisabled.includes(tab.value))
|
||||||
.map((tab) => (
|
.map((tab) => (
|
||||||
<Tab
|
<Tab
|
||||||
key={tab.value}
|
key={tab.value}
|
||||||
|
|
|
||||||
|
|
@ -160,6 +160,18 @@ function CustomTreeItem(props: CustomTreeItemProps) {
|
||||||
|
|
||||||
{props.node.type === 'Installation' && (
|
{props.node.type === 'Installation' && (
|
||||||
<div>
|
<div>
|
||||||
|
{(props.node as any).dataCollectionEnabled === false ? (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: '20px',
|
||||||
|
height: '20px',
|
||||||
|
borderRadius: '50%',
|
||||||
|
marginLeft: '17px',
|
||||||
|
backgroundColor: 'grey'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
{status === -1 ? (
|
{status === -1 ? (
|
||||||
<CancelIcon
|
<CancelIcon
|
||||||
style={{
|
style={{
|
||||||
|
|
@ -204,6 +216,8 @@ function CustomTreeItem(props: CustomTreeItemProps) {
|
||||||
: 'green'
|
: 'green'
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ export interface I_Installation extends I_S3Credentials {
|
||||||
product: number;
|
product: number;
|
||||||
device: number;
|
device: number;
|
||||||
testingMode?: boolean;
|
testingMode?: boolean;
|
||||||
|
dataCollectionEnabled?: boolean;
|
||||||
status?: number;
|
status?: number;
|
||||||
serialNumber?: string;
|
serialNumber?: string;
|
||||||
networkProvider: string;
|
networkProvider: string;
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,9 @@
|
||||||
"externalEmsOther": "Externes EMS (angeben)",
|
"externalEmsOther": "Externes EMS (angeben)",
|
||||||
"emsNo": "Nein",
|
"emsNo": "Nein",
|
||||||
"emsOther": "Andere",
|
"emsOther": "Andere",
|
||||||
|
"yes": "Ja",
|
||||||
|
"no": "Nein",
|
||||||
|
"dataCollectionEnabled": "Datenerfassung",
|
||||||
"generalInfo": "Allgemeine Informationen",
|
"generalInfo": "Allgemeine Informationen",
|
||||||
"installationSetup": "Installationseinrichtung",
|
"installationSetup": "Installationseinrichtung",
|
||||||
"couplingType": "AC/DC-Kopplung",
|
"couplingType": "AC/DC-Kopplung",
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,9 @@
|
||||||
"externalEmsOther": "External EMS (specify)",
|
"externalEmsOther": "External EMS (specify)",
|
||||||
"emsNo": "No",
|
"emsNo": "No",
|
||||||
"emsOther": "Other",
|
"emsOther": "Other",
|
||||||
|
"yes": "Yes",
|
||||||
|
"no": "No",
|
||||||
|
"dataCollectionEnabled": "Data Collection",
|
||||||
"generalInfo": "General Info",
|
"generalInfo": "General Info",
|
||||||
"installationSetup": "Installation Setup",
|
"installationSetup": "Installation Setup",
|
||||||
"couplingType": "AC/DC Coupling",
|
"couplingType": "AC/DC Coupling",
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,9 @@
|
||||||
"externalEmsOther": "EMS externe (préciser)",
|
"externalEmsOther": "EMS externe (préciser)",
|
||||||
"emsNo": "Non",
|
"emsNo": "Non",
|
||||||
"emsOther": "Autre",
|
"emsOther": "Autre",
|
||||||
|
"yes": "Oui",
|
||||||
|
"no": "Non",
|
||||||
|
"dataCollectionEnabled": "Collecte de données",
|
||||||
"generalInfo": "Informations générales",
|
"generalInfo": "Informations générales",
|
||||||
"installationSetup": "Configuration de l'installation",
|
"installationSetup": "Configuration de l'installation",
|
||||||
"couplingType": "Couplage AC/DC",
|
"couplingType": "Couplage AC/DC",
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,9 @@
|
||||||
"externalEmsOther": "EMS esterno (specificare)",
|
"externalEmsOther": "EMS esterno (specificare)",
|
||||||
"emsNo": "No",
|
"emsNo": "No",
|
||||||
"emsOther": "Altro",
|
"emsOther": "Altro",
|
||||||
|
"yes": "Sì",
|
||||||
|
"no": "No",
|
||||||
|
"dataCollectionEnabled": "Raccolta dati",
|
||||||
"generalInfo": "Informazioni generali",
|
"generalInfo": "Informazioni generali",
|
||||||
"installationSetup": "Configurazione installazione",
|
"installationSetup": "Configurazione installazione",
|
||||||
"couplingType": "Accoppiamento AC/DC",
|
"couplingType": "Accoppiamento AC/DC",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue