From 24708e94ea191121163d6cfa6d45577cfa561616 Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Tue, 6 Aug 2024 22:49:06 +0200 Subject: [PATCH 01/55] #762 - First grab of api to test the data from the meticulous, and read + generate data to import --- .../meticulous/meticulousDevice.ts | 50 ++++++++++- ...ew-brewing-preparation-device.component.ts | 84 ++++++++++++++++++- 2 files changed, 132 insertions(+), 2 deletions(-) diff --git a/src/classes/preparationDevice/meticulous/meticulousDevice.ts b/src/classes/preparationDevice/meticulous/meticulousDevice.ts index ee5022de..93dbd744 100644 --- a/src/classes/preparationDevice/meticulous/meticulousDevice.ts +++ b/src/classes/preparationDevice/meticulous/meticulousDevice.ts @@ -1,5 +1,5 @@ import { PreparationDevice } from '../preparationDevice'; -import { HttpClient } from '@angular/common/http'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Preparation } from '../../preparation/preparation'; import { MeticulousShotData } from './meticulousShotData'; import Api, { @@ -10,6 +10,9 @@ import Api, { import { IMeticulousParams } from '../../../interfaces/preparationDevices/meticulous/iMeticulousParams'; import { Profile } from 'meticulous-typescript-profile'; +import { UILog } from '../../../services/uiLog'; +import { catchError, timeout } from 'rxjs/operators'; +import { of } from 'rxjs'; declare var cordova; declare var io; @@ -22,6 +25,8 @@ export class MeticulousDevice extends PreparationDevice { private _profiles: Array = []; + private serverURL: string = ''; + constructor(protected httpClient: HttpClient, _preparation: Preparation) { super(httpClient, _preparation); this.meticulousShotData = undefined; @@ -29,10 +34,53 @@ export class MeticulousDevice extends PreparationDevice { undefined, _preparation.connectedPreparationDevice.url ); + this.serverURL = _preparation.connectedPreparationDevice.url; if (typeof cordova !== 'undefined') { } } + public getHistory() { + const promise = new Promise((resolve, reject) => { + const httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json', + }), + }; + this.httpClient + .post( + this.serverURL + 'api/v1/history', + { + sort: 'desc', + max_results: 20, + }, + httpOptions + ) + .pipe( + timeout(10000), + catchError((e) => { + return of(null); + }) + ) + .toPromise() + .then( + (data: any) => { + console.log(data); + if (data && data.history) { + resolve(data.history); + } + }, + (error) => { + console.log(error); + reject(); + } + ) + .catch((error) => { + console.log(error); + reject(); + }); + }); + return promise; + } public getActualShotData() { return this.meticulousShotData; diff --git a/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.ts b/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.ts index 34998a4d..a420664f 100644 --- a/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.ts +++ b/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.ts @@ -29,6 +29,14 @@ import { UISettingsStorage } from '../../../services/uiSettingsStorage'; import { Settings } from '../../../classes/settings/settings'; import { Preparation } from '../../../classes/preparation/preparation'; import { UIPreparationStorage } from '../../../services/uiPreparationStorage'; +import moment from 'moment'; +import { + BrewFlow, + IBrewPressureFlow, + IBrewRealtimeWaterFlow, + IBrewTemperatureFlow, + IBrewWeightFlow, +} from '../../../classes/brew/brewFlow'; @Component({ selector: 'brew-brewing-preparation-device', templateUrl: './brew-brewing-preparation-device.component.html', @@ -270,10 +278,13 @@ export class BrewBrewingPreparationDeviceComponent implements OnInit { this.data.preparationDeviceBrew.params = new MeticulousParams(); await connectedDevice.connectToSocket().then( - (_connected) => { + async (_connected) => { if (_connected) { this.preparationDevice = connectedDevice as MeticulousDevice; this.preparationDevice.loadProfiles(); + + const history = await this.preparationDevice.getHistory(); + this.readShot(history[0]); } }, () => { @@ -282,6 +293,77 @@ export class BrewBrewingPreparationDeviceComponent implements OnInit { ); } + private readShot(_historyData) { + const newMoment = moment(new Date()).startOf('day'); + + let firstDripTimeSet: boolean = false; + const newBrewFlow = new BrewFlow(); + + let seconds: number = 0; + let milliseconds: number = 0; + for (const entry of _historyData.data as any) { + const shotEntry: any = entry.shot; + const shotEntryTime = newMoment.clone().add('milliseconds', entry.time); + const timestamp = shotEntryTime.format('HH:mm:ss.SSS'); + + seconds = shotEntryTime.diff(newMoment, 'seconds'); + milliseconds = shotEntryTime.get('milliseconds'); + + const realtimeWaterFlow: IBrewRealtimeWaterFlow = + {} as IBrewRealtimeWaterFlow; + + realtimeWaterFlow.brew_time = ''; + realtimeWaterFlow.timestamp = timestamp; + realtimeWaterFlow.smoothed_weight = 0; + realtimeWaterFlow.flow_value = shotEntry.flow; + realtimeWaterFlow.timestampdelta = 0; + + newBrewFlow.realtimeFlow.push(realtimeWaterFlow); + + const brewFlow: IBrewWeightFlow = {} as IBrewWeightFlow; + brewFlow.timestamp = timestamp; + brewFlow.brew_time = ''; + brewFlow.actual_weight = shotEntry.weight; + brewFlow.old_weight = 0; + brewFlow.actual_smoothed_weight = 0; + brewFlow.old_smoothed_weight = 0; + brewFlow.not_mutated_weight = 0; + newBrewFlow.weight.push(brewFlow); + + if (shotEntry.weight > 0 && firstDripTimeSet === false) { + firstDripTimeSet = true; + + this.brewComponent.brewFirstDripTime.setTime(seconds, milliseconds); + this.brewComponent.brewFirstDripTime.changeEvent(); + } + + const pressureFlow: IBrewPressureFlow = {} as IBrewPressureFlow; + pressureFlow.timestamp = timestamp; + pressureFlow.brew_time = ''; + pressureFlow.actual_pressure = shotEntry.pressure; + pressureFlow.old_pressure = 0; + newBrewFlow.pressureFlow.push(pressureFlow); + + const temperatureFlow: IBrewTemperatureFlow = {} as IBrewTemperatureFlow; + temperatureFlow.timestamp = timestamp; + temperatureFlow.brew_time = ''; + temperatureFlow.actual_temperature = shotEntry.temperature; + temperatureFlow.old_temperature = 0; + newBrewFlow.temperatureFlow.push(temperatureFlow); + } + + const lastEntry = newBrewFlow.weight[newBrewFlow.weight.length - 1]; + const lastShotEntryTime = moment(lastEntry.timestamp); + + this.brewComponent.data.brew_beverage_quantity = lastEntry.actual_weight; + + this.brewComponent.timer.setTime(seconds, milliseconds); + this.brewComponent.timer.changeEvent(); + + this.brewComponent.brewBrewingGraphEl.flow_profile_raw = newBrewFlow; + this.brewComponent.brewBrewingGraphEl.initializeFlowChart(true); + } + public getPreparationDeviceType() { if (this.preparationDevice instanceof XeniaDevice) { return PreparationDeviceType.XENIA; From 81d2f8c14da566cebb0fed75f11528cb63dbd2a4 Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Wed, 7 Aug 2024 20:22:56 +0200 Subject: [PATCH 02/55] #762 - First grab of api to test the data from the meticulous, and read + generate data to import --- .../brew-brewing-preparation-device.component.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.ts b/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.ts index a420664f..355f0b64 100644 --- a/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.ts +++ b/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.ts @@ -353,7 +353,6 @@ export class BrewBrewingPreparationDeviceComponent implements OnInit { } const lastEntry = newBrewFlow.weight[newBrewFlow.weight.length - 1]; - const lastShotEntryTime = moment(lastEntry.timestamp); this.brewComponent.data.brew_beverage_quantity = lastEntry.actual_weight; From abca8da598a035caaf4b83e2d9e0b7b385376f56 Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Wed, 7 Aug 2024 21:55:55 +0200 Subject: [PATCH 03/55] #762 - Make shothistory readable and let them be selected --- ...odal-import-shot-meticulous.component.html | 38 ++++++ ...odal-import-shot-meticulous.component.scss | 11 ++ ...l-import-shot-meticulous.component.spec.ts | 24 ++++ ...-modal-import-shot-meticulous.component.ts | 118 ++++++++++++++++++ src/app/shared/shared.module.ts | 3 + ...-brewing-preparation-device.component.html | 5 + ...ew-brewing-preparation-device.component.ts | 87 +++---------- 7 files changed, 214 insertions(+), 72 deletions(-) create mode 100644 src/app/brew/brew-modal-import-shot-meticulous/brew-modal-import-shot-meticulous.component.html create mode 100644 src/app/brew/brew-modal-import-shot-meticulous/brew-modal-import-shot-meticulous.component.scss create mode 100644 src/app/brew/brew-modal-import-shot-meticulous/brew-modal-import-shot-meticulous.component.spec.ts create mode 100644 src/app/brew/brew-modal-import-shot-meticulous/brew-modal-import-shot-meticulous.component.ts diff --git a/src/app/brew/brew-modal-import-shot-meticulous/brew-modal-import-shot-meticulous.component.html b/src/app/brew/brew-modal-import-shot-meticulous/brew-modal-import-shot-meticulous.component.html new file mode 100644 index 00000000..0d9e1902 --- /dev/null +++ b/src/app/brew/brew-modal-import-shot-meticulous/brew-modal-import-shot-meticulous.component.html @@ -0,0 +1,38 @@ + + + {{"CHOOSE_REFERENCE_GRAPH" | translate}} + + + + + + + + + + + + + + + + {{entry.profile.name}} + + + + + + + + + + + {{"RESET" | translate}} + + + {{"CHOOSE" | translate}} + + + diff --git a/src/app/brew/brew-modal-import-shot-meticulous/brew-modal-import-shot-meticulous.component.scss b/src/app/brew/brew-modal-import-shot-meticulous/brew-modal-import-shot-meticulous.component.scss new file mode 100644 index 00000000..cbdca414 --- /dev/null +++ b/src/app/brew/brew-modal-import-shot-meticulous/brew-modal-import-shot-meticulous.component.scss @@ -0,0 +1,11 @@ +:host { + ion-item { + margin-left: 16px; + margin-right: 0px; + line-height: 12px; + font-size: 12px; + } + ion-card-content { + background:#FFFFFF; + } +} diff --git a/src/app/brew/brew-modal-import-shot-meticulous/brew-modal-import-shot-meticulous.component.spec.ts b/src/app/brew/brew-modal-import-shot-meticulous/brew-modal-import-shot-meticulous.component.spec.ts new file mode 100644 index 00000000..b42746a1 --- /dev/null +++ b/src/app/brew/brew-modal-import-shot-meticulous/brew-modal-import-shot-meticulous.component.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { IonicModule } from '@ionic/angular'; + +import { BrewModalImportShotMeticulousComponent } from './brew-modal-import-shot-meticulous.component'; + +describe('BrewModalImportShotMeticulousComponent', () => { + let component: BrewModalImportShotMeticulousComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [BrewModalImportShotMeticulousComponent], + imports: [IonicModule.forRoot()], + }).compileComponents(); + + fixture = TestBed.createComponent(BrewModalImportShotMeticulousComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + })); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/brew/brew-modal-import-shot-meticulous/brew-modal-import-shot-meticulous.component.ts b/src/app/brew/brew-modal-import-shot-meticulous/brew-modal-import-shot-meticulous.component.ts new file mode 100644 index 00000000..cc14aa86 --- /dev/null +++ b/src/app/brew/brew-modal-import-shot-meticulous/brew-modal-import-shot-meticulous.component.ts @@ -0,0 +1,118 @@ +import { Component, Input, OnInit } from '@angular/core'; + +import { MeticulousDevice } from '../../../classes/preparationDevice/meticulous/meticulousDevice'; +import moment from 'moment/moment'; +import { + BrewFlow, + IBrewPressureFlow, + IBrewRealtimeWaterFlow, + IBrewTemperatureFlow, + IBrewWeightFlow, +} from '../../../classes/brew/brewFlow'; +import { ModalController } from '@ionic/angular'; + +@Component({ + selector: 'app-brew-modal-import-shot-meticulous', + templateUrl: './brew-modal-import-shot-meticulous.component.html', + styleUrls: ['./brew-modal-import-shot-meticulous.component.scss'], +}) +export class BrewModalImportShotMeticulousComponent implements OnInit { + public static COMPONENT_ID: string = 'brew-modal-import-shot-meticulous'; + + @Input() public meticulousDevice: MeticulousDevice; + public radioSelection: string; + public history: Array = []; + constructor(private readonly modalController: ModalController) {} + + public ngOnInit() { + this.readHistory(); + } + + private async readHistory() { + this.history = await this.meticulousDevice.getHistory(); + //this.readShot(history[0]); + } + + private readShot(_historyData) { + const newMoment = moment(new Date()).startOf('day'); + + let firstDripTimeSet: boolean = false; + const newBrewFlow = new BrewFlow(); + + let seconds: number = 0; + let milliseconds: number = 0; + for (const entry of _historyData.data as any) { + const shotEntry: any = entry.shot; + const shotEntryTime = newMoment.clone().add('milliseconds', entry.time); + const timestamp = shotEntryTime.format('HH:mm:ss.SSS'); + + seconds = shotEntryTime.diff(newMoment, 'seconds'); + milliseconds = shotEntryTime.get('milliseconds'); + + const realtimeWaterFlow: IBrewRealtimeWaterFlow = + {} as IBrewRealtimeWaterFlow; + + realtimeWaterFlow.brew_time = ''; + realtimeWaterFlow.timestamp = timestamp; + realtimeWaterFlow.smoothed_weight = 0; + realtimeWaterFlow.flow_value = shotEntry.flow; + realtimeWaterFlow.timestampdelta = 0; + + newBrewFlow.realtimeFlow.push(realtimeWaterFlow); + + const brewFlow: IBrewWeightFlow = {} as IBrewWeightFlow; + brewFlow.timestamp = timestamp; + brewFlow.brew_time = ''; + brewFlow.actual_weight = shotEntry.weight; + brewFlow.old_weight = 0; + brewFlow.actual_smoothed_weight = 0; + brewFlow.old_smoothed_weight = 0; + brewFlow.not_mutated_weight = 0; + newBrewFlow.weight.push(brewFlow); + + if (shotEntry.weight > 0 && firstDripTimeSet === false) { + firstDripTimeSet = true; + + //this.brewComponent.brewFirstDripTime.setTime(seconds, milliseconds); + //this.brewComponent.brewFirstDripTime.changeEvent(); + } + + const pressureFlow: IBrewPressureFlow = {} as IBrewPressureFlow; + pressureFlow.timestamp = timestamp; + pressureFlow.brew_time = ''; + pressureFlow.actual_pressure = shotEntry.pressure; + pressureFlow.old_pressure = 0; + newBrewFlow.pressureFlow.push(pressureFlow); + + const temperatureFlow: IBrewTemperatureFlow = {} as IBrewTemperatureFlow; + temperatureFlow.timestamp = timestamp; + temperatureFlow.brew_time = ''; + temperatureFlow.actual_temperature = shotEntry.temperature; + temperatureFlow.old_temperature = 0; + newBrewFlow.temperatureFlow.push(temperatureFlow); + } + + const lastEntry = newBrewFlow.weight[newBrewFlow.weight.length - 1]; + + console.log(newBrewFlow); + } + + public dismiss(): void { + this.modalController.dismiss( + { + dismissed: true, + }, + undefined, + BrewModalImportShotMeticulousComponent.COMPONENT_ID + ); + } + public choose(): void { + this.modalController.dismiss( + { + dismissed: true, + }, + undefined, + BrewModalImportShotMeticulousComponent.COMPONENT_ID + ); + } +} diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 79005eab..267d1887 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -191,6 +191,7 @@ import { MeticulousHelpPopoverComponent } from '../../popover/meticulous-help-po import { BeanPopoverFreezeComponent } from '../beans/bean-popover-freeze/bean-popover-freeze.component'; import { BeanFreezeInformationComponent } from '../../components/beans/bean-freeze-information/bean-freeze-information.component'; import { BeanPopoverFrozenListComponent } from '../beans/bean-popover-frozen-list/bean-popover-frozen-list.component'; +import { BrewModalImportShotMeticulousComponent } from '../brew/brew-modal-import-shot-meticulous/brew-modal-import-shot-meticulous.component'; @NgModule({ declarations: [ @@ -216,6 +217,7 @@ import { BeanPopoverFrozenListComponent } from '../beans/bean-popover-frozen-lis BeansAddComponent, BrewFlowComponent, BrewChooseGraphReferenceComponent, + BrewModalImportShotMeticulousComponent, BrewMaximizeControlsComponent, BeansEditComponent, BeansDetailComponent, @@ -427,6 +429,7 @@ import { BeanPopoverFrozenListComponent } from '../beans/bean-popover-frozen-lis BeansAddComponent, BrewFlowComponent, BrewChooseGraphReferenceComponent, + BrewModalImportShotMeticulousComponent, BrewMaximizeControlsComponent, BeansEditComponent, BrewRatingComponent, diff --git a/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.html b/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.html index 3e04427a..e052d021 100644 --- a/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.html +++ b/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.html @@ -73,6 +73,11 @@ +
+ + + {{"IMPORT_SHOT_FROM_METICULOUS" | translate}} + diff --git a/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.ts b/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.ts index 355f0b64..c1346992 100644 --- a/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.ts +++ b/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.ts @@ -37,6 +37,9 @@ import { IBrewTemperatureFlow, IBrewWeightFlow, } from '../../../classes/brew/brewFlow'; +import { BrewChooseGraphReferenceComponent } from '../../../app/brew/brew-choose-graph-reference/brew-choose-graph-reference.component'; +import { BrewModalImportShotMeticulousComponent } from '../../../app/brew/brew-modal-import-shot-meticulous/brew-modal-import-shot-meticulous.component'; +import { ModalController } from '@ionic/angular'; @Component({ selector: 'brew-brewing-preparation-device', templateUrl: './brew-brewing-preparation-device.component.html', @@ -63,10 +66,10 @@ export class BrewBrewingPreparationDeviceComponent implements OnInit { private readonly uiBrewStorage: UIBrewStorage, private readonly uiHelper: UIHelper, private readonly uiToast: UIToast, - private readonly uiBrewHelper: UIBrewHelper, private readonly uiSettingsStorage: UISettingsStorage, private readonly uiPreparationStorage: UIPreparationStorage, - private readonly changeDetectorRef: ChangeDetectorRef + private readonly changeDetectorRef: ChangeDetectorRef, + private readonly modalController: ModalController ) {} public ngOnInit() { @@ -282,9 +285,6 @@ export class BrewBrewingPreparationDeviceComponent implements OnInit { if (_connected) { this.preparationDevice = connectedDevice as MeticulousDevice; this.preparationDevice.loadProfiles(); - - const history = await this.preparationDevice.getHistory(); - this.readShot(history[0]); } }, () => { @@ -293,74 +293,17 @@ export class BrewBrewingPreparationDeviceComponent implements OnInit { ); } - private readShot(_historyData) { - const newMoment = moment(new Date()).startOf('day'); - - let firstDripTimeSet: boolean = false; - const newBrewFlow = new BrewFlow(); - - let seconds: number = 0; - let milliseconds: number = 0; - for (const entry of _historyData.data as any) { - const shotEntry: any = entry.shot; - const shotEntryTime = newMoment.clone().add('milliseconds', entry.time); - const timestamp = shotEntryTime.format('HH:mm:ss.SSS'); - - seconds = shotEntryTime.diff(newMoment, 'seconds'); - milliseconds = shotEntryTime.get('milliseconds'); - - const realtimeWaterFlow: IBrewRealtimeWaterFlow = - {} as IBrewRealtimeWaterFlow; - - realtimeWaterFlow.brew_time = ''; - realtimeWaterFlow.timestamp = timestamp; - realtimeWaterFlow.smoothed_weight = 0; - realtimeWaterFlow.flow_value = shotEntry.flow; - realtimeWaterFlow.timestampdelta = 0; - - newBrewFlow.realtimeFlow.push(realtimeWaterFlow); - - const brewFlow: IBrewWeightFlow = {} as IBrewWeightFlow; - brewFlow.timestamp = timestamp; - brewFlow.brew_time = ''; - brewFlow.actual_weight = shotEntry.weight; - brewFlow.old_weight = 0; - brewFlow.actual_smoothed_weight = 0; - brewFlow.old_smoothed_weight = 0; - brewFlow.not_mutated_weight = 0; - newBrewFlow.weight.push(brewFlow); - - if (shotEntry.weight > 0 && firstDripTimeSet === false) { - firstDripTimeSet = true; - - this.brewComponent.brewFirstDripTime.setTime(seconds, milliseconds); - this.brewComponent.brewFirstDripTime.changeEvent(); - } - - const pressureFlow: IBrewPressureFlow = {} as IBrewPressureFlow; - pressureFlow.timestamp = timestamp; - pressureFlow.brew_time = ''; - pressureFlow.actual_pressure = shotEntry.pressure; - pressureFlow.old_pressure = 0; - newBrewFlow.pressureFlow.push(pressureFlow); - - const temperatureFlow: IBrewTemperatureFlow = {} as IBrewTemperatureFlow; - temperatureFlow.timestamp = timestamp; - temperatureFlow.brew_time = ''; - temperatureFlow.actual_temperature = shotEntry.temperature; - temperatureFlow.old_temperature = 0; - newBrewFlow.temperatureFlow.push(temperatureFlow); - } - - const lastEntry = newBrewFlow.weight[newBrewFlow.weight.length - 1]; - - this.brewComponent.data.brew_beverage_quantity = lastEntry.actual_weight; - - this.brewComponent.timer.setTime(seconds, milliseconds); - this.brewComponent.timer.changeEvent(); + public async importShotFromMeticulous() { + const modal = await this.modalController.create({ + component: BrewModalImportShotMeticulousComponent, + id: BrewModalImportShotMeticulousComponent.COMPONENT_ID, + componentProps: { + meticulousDevice: this.preparationDevice as MeticulousDevice, + }, + }); - this.brewComponent.brewBrewingGraphEl.flow_profile_raw = newBrewFlow; - this.brewComponent.brewBrewingGraphEl.initializeFlowChart(true); + await modal.present(); + const rData = await modal.onWillDismiss(); } public getPreparationDeviceType() { From 96b9bf789bf214d7195f38999aac54bbfab01b4b Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Tue, 13 Aug 2024 17:40:29 +0200 Subject: [PATCH 04/55] #765 - First implementation for backup check --- src/services/uiExportImportHelper.ts | 165 ++++++++++++++------------- src/services/uiStorage.ts | 46 +++++--- 2 files changed, 118 insertions(+), 93 deletions(-) diff --git a/src/services/uiExportImportHelper.ts b/src/services/uiExportImportHelper.ts index 07a6a274..964f5ac9 100644 --- a/src/services/uiExportImportHelper.ts +++ b/src/services/uiExportImportHelper.ts @@ -248,90 +248,36 @@ export class UIExportImportHelper { this.uiLog.log('Check Backup'); const hasData = await this.uiStorage.hasData(); - let hasCorruptedData: boolean = false; + let corruptedDataObjCheck: any; if (hasData) { - hasCorruptedData = await this.uiStorage.hasCorruptedData(); + corruptedDataObjCheck = await this.uiStorage.hasCorruptedData(); } this.uiLog.log('Check Backup - Has data ' + hasData); - if (!hasData || hasCorruptedData) { - this.uiLog.log( - 'Check Backup - No data are stored yet inside the app, so we try to find a backup file' - ); - // If we don't got any data, we check now if there is a Beanconqueror.zip saved. - this.uiFileHelper.getZIPFile('Beanconqueror.zip').then( - async (_arrayBuffer) => { - await this.uiAlert.showLoadingSpinner(); - try { - this.uiLog.log(' We found a backup, try to import'); - const parsedJSON = - await this.getJSONFromZIPArrayBufferContent(_arrayBuffer); - this.uiStorage.import(parsedJSON).then( - async () => { - this.uiLog.log('Sucessfully imported Backup'); - setTimeout(() => { - this.uiAlert.hideLoadingSpinner(); - }, 150); - resolve(null); - }, - () => { - this.uiLog.error('Could not import Backup'); - setTimeout(() => { - this.uiAlert.hideLoadingSpinner(); - }, 150); - resolve(null); - } - ); - } catch (ex) { - setTimeout(() => { - this.uiAlert.hideLoadingSpinner(); - }, 150); - } - }, - () => { - this.uiLog.log( - 'Check Backup - We couldnt retrieve any zip file - try the old JSON Way.' - ); + if (!hasData || corruptedDataObjCheck.CORRUPTED) { + if (!hasData) { + this.uiLog.log( + 'Check Backup - We didnt found any data inside the app, so try to find a backup and import it' + ); + } else { + this.uiLog.log( + 'Check Backup - We found data but they where corrupted, so try to import a backup' + ); + } - this.uiFileHelper.getJSONFile('Beanconqueror.json').then( - async (_json) => { - await this.uiAlert.showLoadingSpinner(); - try { - this.uiLog.log('We found an backup, try to import'); - this.uiStorage.import(_json).then( - async () => { - this.uiLog.log('Sucessfully imported Backup'); - setTimeout(() => { - this.uiAlert.hideLoadingSpinner(); - }, 150); - resolve(null); - }, - () => { - this.uiLog.error('Could not import Backup'); - setTimeout(() => { - this.uiAlert.hideLoadingSpinner(); - }, 150); - resolve(null); - } - ); - } catch (ex) { - setTimeout(() => { - this.uiAlert.hideLoadingSpinner(); - }, 150); - } - }, - () => { - setTimeout(() => { - this.uiAlert.hideLoadingSpinner(); - }, 150); - this.uiLog.log( - 'Check Backup - We couldnt retrieve any JSON file' - ); - resolve(null); - } - ); - } - ); + const parsedJSON = await this.readBackupZIPFile(); + if (parsedJSON) { + await this.importBackupJSON(parsedJSON); + } } else { + /** + * BREWS: number, + * MILL: number, + * PREPARATION: number, + * BEANS: number, + */ + const parsedJSON = await this.readBackupZIPFile(); + console.log('BLAAA'); + console.log(parsedJSON); resolve(null); } } else { @@ -345,6 +291,67 @@ export class UIExportImportHelper { } catch (ex) {} } + private importBackupJSON(_parsedJSON) { + const promise = new Promise(async (resolve, reject) => { + await this.uiAlert.showLoadingSpinner(); + + this.uiStorage.import(_parsedJSON).then( + async () => { + this.uiLog.log('Sucessfully imported Backup'); + setTimeout(() => { + this.uiAlert.hideLoadingSpinner(); + }, 150); + resolve(null); + }, + () => { + this.uiLog.error('Could not import Backup'); + setTimeout(() => { + this.uiAlert.hideLoadingSpinner(); + }, 150); + resolve(null); + } + ); + }); + return promise; + } + + private readBackupZIPFile() { + // If we don't got any data, we check now if there is a Beanconqueror.zip saved. + const promise = new Promise(async (resolve, reject) => { + this.uiFileHelper.getZIPFile('Beanconqueror.zip').then( + async (_arrayBuffer) => { + try { + this.uiLog.log('Read ZIP-File, we found an zip-file'); + const parsedJSON = await this.getJSONFromZIPArrayBufferContent( + _arrayBuffer + ); + resolve(parsedJSON); + } catch (ex) { + resolve(null); + } + }, + () => { + this.uiLog.log( + 'Read ZIP-FILE failed, try to read an old Beanconqueror.json' + ); + this.uiFileHelper.getJSONFile('Beanconqueror.json').then( + async (_json) => { + this.uiLog.log('Read ZIP-File, we found an json-file'); + resolve(_json); + }, + () => { + this.uiLog.log( + 'Check Backup - We couldnt retrieve any JSON file' + ); + resolve(null); + } + ); + } + ); + }); + return promise; + } + private getAutomatedBackupFilename(): string { return moment().format('DD_MM_YYYY').toString(); } diff --git a/src/services/uiStorage.ts b/src/services/uiStorage.ts index 92eb75fd..9bea4e0b 100755 --- a/src/services/uiStorage.ts +++ b/src/services/uiStorage.ts @@ -240,13 +240,29 @@ export class UIStorage { return promise; } - public async hasCorruptedData(): Promise { - const promise: Promise = new Promise((resolve, reject) => { + public async hasCorruptedData(): Promise<{ + CORRUPTED: boolean; + DATA: { + BREWS: number; + MILL: number; + PREPARATION: number; + BEANS: number; + }; + }> { + const promise: Promise<{ + CORRUPTED: boolean; + DATA: { + BREWS: number; + MILL: number; + PREPARATION: number; + BEANS: number; + }; + }> = new Promise((resolve, reject) => { const hasDataObj = { - BREWS: false, - MILL: false, - PREPARATION: false, - BEANS: false, + BREWS: 0, + MILL: 0, + PREPARATION: 0, + BEANS: 0, }; this._storage .forEach((_value, _key, _index) => { @@ -258,7 +274,9 @@ export class UIStorage { ) { try { if (_value?.length > 0) { - hasDataObj[_key] = true; + hasDataObj[_key] = _value?.length; + } else { + hasDataObj[_key] = 0; } } catch (ex) {} } @@ -266,22 +284,22 @@ export class UIStorage { .then( () => { if ( - hasDataObj.BREWS === true && - (hasDataObj.MILL === false || - hasDataObj.PREPARATION === false || - hasDataObj.BEANS === false) + hasDataObj.BREWS > 0 && + (hasDataObj.MILL <= 0 || + hasDataObj.PREPARATION <= 0 || + hasDataObj.BEANS <= 0) ) { /** * If we got brews but not a mill / preparation / or bean something broke hard. * We saw this issue on android that a user got brews but no beans anymore, they where lost */ - resolve(true); + resolve({ CORRUPTED: true, DATA: hasDataObj }); } else { - resolve(false); + resolve({ CORRUPTED: false, DATA: hasDataObj }); } }, () => { - resolve(false); + resolve({ CORRUPTED: false, DATA: hasDataObj }); } ); }); From f4ac75290c343312f92895e60a9b0be31024d732 Mon Sep 17 00:00:00 2001 From: Juan Silveira Date: Sat, 24 Aug 2024 16:01:44 +0900 Subject: [PATCH 05/55] Fix for BeanBeanPopoverFrozenListComponent test. --- .../bean-popover-frozen-list.component.spec.ts | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/app/beans/bean-popover-frozen-list/bean-popover-frozen-list.component.spec.ts b/src/app/beans/bean-popover-frozen-list/bean-popover-frozen-list.component.spec.ts index 4526042a..604d9322 100644 --- a/src/app/beans/bean-popover-frozen-list/bean-popover-frozen-list.component.spec.ts +++ b/src/app/beans/bean-popover-frozen-list/bean-popover-frozen-list.component.spec.ts @@ -1,7 +1,11 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { IonicModule } from '@ionic/angular'; import { BeanPopoverFrozenListComponent } from './bean-popover-frozen-list.component'; +import { IonicStorageModule } from '@ionic/storage-angular'; +import { TranslateModule, TranslateService } from '@ngx-translate/core'; +import { UIHelperMock } from 'src/classes/mock'; +import { UIHelper } from 'src/services/uiHelper'; +import { IonicModule } from '@ionic/angular'; describe('BeanPopoverFrozenListComponent', () => { let component: BeanPopoverFrozenListComponent; @@ -10,13 +14,21 @@ describe('BeanPopoverFrozenListComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ declarations: [BeanPopoverFrozenListComponent], - imports: [IonicModule.forRoot()], + imports: [ + IonicModule.forRoot(), + IonicStorageModule.forRoot(), + TranslateModule.forChild(), + TranslateModule.forRoot(), + ], + providers: [{ provide: UIHelper, useClass: UIHelperMock }], }).compileComponents(); + })); + beforeEach(() => { fixture = TestBed.createComponent(BeanPopoverFrozenListComponent); component = fixture.componentInstance; fixture.detectChanges(); - })); + }); it('should create', () => { expect(component).toBeTruthy(); From 8c23f8429e1b8e4f96a2c4ec5429a41ddeda0fb2 Mon Sep 17 00:00:00 2001 From: Juan Silveira Date: Sat, 24 Aug 2024 16:27:07 +0900 Subject: [PATCH 06/55] Fix PreparationConnectedDeviceComponent test. Split beforeEach into async and sync. --- .../preparation-connected-device.component.spec.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/app/preparation/preparation-connected-device/preparation-connected-device.component.spec.ts b/src/app/preparation/preparation-connected-device/preparation-connected-device.component.spec.ts index 1fa68204..b85168cd 100644 --- a/src/app/preparation/preparation-connected-device/preparation-connected-device.component.spec.ts +++ b/src/app/preparation/preparation-connected-device/preparation-connected-device.component.spec.ts @@ -32,7 +32,9 @@ describe('PreparationConnectedDeviceComponent', () => { }, ], }).compileComponents(); + })); + beforeEach(() => { fixture = TestBed.createComponent(PreparationConnectedDeviceComponent); component = fixture.componentInstance; component.preparation = { @@ -41,7 +43,7 @@ describe('PreparationConnectedDeviceComponent', () => { }, } as Preparation; fixture.detectChanges(); - })); + }); it('should create', () => { expect(component).toBeTruthy(); From dc76fefaee2be128584c11a4ee9ac9747226feac Mon Sep 17 00:00:00 2001 From: Juan Silveira Date: Sat, 24 Aug 2024 16:39:14 +0900 Subject: [PATCH 07/55] Add PipesModule for app-specific pipes. This module allows importing e.g. "keys" pipe in tests without having to import SharedModule. Use this in all the tests instead of declaring the Pipes in the testing module, which should only declare the component under test. --- .../bean-filter/bean-filter.component.spec.ts | 13 +++++++++---- .../beans/beans-add/beans-add.component.spec.ts | 5 +++-- .../beans/beans-edit/beans-edit.component.spec.ts | 5 +++-- src/app/beans/beans.page.spec.ts | 6 +++--- src/app/brew/brew-add/brew-add.component.spec.ts | 12 +++--------- .../brew/brew-detail/brew-detail.component.spec.ts | 5 +++-- src/app/brew/brew-edit/brew-edit.component.spec.ts | 11 +++-------- .../brew-popover-actions.component.spec.ts | 5 +++-- src/app/brew/brew.page.spec.ts | 5 +++-- src/app/home/home.page.spec.ts | 5 +++-- src/app/info/about/about.component.spec.ts | 5 +++-- src/app/info/contact/contact.component.spec.ts | 5 +++-- src/app/info/credits/credits.component.spec.ts | 5 +++-- src/app/info/licences/licences.component.spec.ts | 5 +++-- src/app/info/log/log.component.spec.ts | 5 +++-- src/app/info/privacy/privacy.component.spec.ts | 5 +++-- src/app/info/terms/terms.component.spec.ts | 5 +++-- src/app/info/thanks/thanks.component.spec.ts | 5 +++-- src/app/mill/mill-add/mill-add.component.spec.ts | 5 +++-- src/app/mill/mill-edit/mill-edit.component.spec.ts | 5 +++-- src/app/mill/mill.page.spec.ts | 5 +++-- .../preparation-add.component.spec.ts | 6 +++--- .../preparation-edit.component.spec.ts | 5 +++-- src/app/preparation/preparation.page.spec.ts | 5 +++-- src/app/settings/settings.page.spec.ts | 5 +++-- src/app/shared/shared.module.ts | 14 ++------------ src/app/statistic/statistic.page.spec.ts | 5 +++-- .../water/water-add/water-add.component.spec.ts | 11 ++++++++--- .../water/water-edit/water-edit.component.spec.ts | 11 ++++++++--- .../bean-general-information.component.spec.ts | 11 ++++++++--- .../brew-graph-reference-card.component.spec.ts | 6 +++--- .../brew-information.component.spec.ts | 5 +++-- .../brew-brewing/brew-brewing.component.spec.ts | 4 ++-- src/pipes/pipes.module.ts | 10 ++++++++++ 34 files changed, 128 insertions(+), 97 deletions(-) create mode 100644 src/pipes/pipes.module.ts diff --git a/src/app/beans/bean-filter/bean-filter.component.spec.ts b/src/app/beans/bean-filter/bean-filter.component.spec.ts index 1bcc8a17..786f380e 100644 --- a/src/app/beans/bean-filter/bean-filter.component.spec.ts +++ b/src/app/beans/bean-filter/bean-filter.component.spec.ts @@ -9,10 +9,10 @@ import { UIPreparationStorage } from '../../../services/uiPreparationStorage'; import { UIBeanStorage } from '../../../services/uiBeanStorage'; import { UIMillStorage } from '../../../services/uiMillStorage'; import { IBeanPageFilter } from '../../../interfaces/bean/iBeanPageFilter'; -import { TranslatePipe, TranslateService } from '@ngx-translate/core'; +import { TranslateModule, TranslateService } from '@ngx-translate/core'; import { TranslateServiceMock } from '../../../classes/mock'; import { FormsModule } from '@angular/forms'; -import { KeysPipe } from '../../../pipes/keys'; +import { PipesModule } from 'src/pipes/pipes.module'; describe('BeanFilterComponent', () => { let component: BeanFilterComponent; @@ -39,8 +39,13 @@ describe('BeanFilterComponent', () => { }, } as Settings; TestBed.configureTestingModule({ - declarations: [BeanFilterComponent, TranslatePipe, KeysPipe], - imports: [IonicModule.forRoot(), FormsModule], + declarations: [BeanFilterComponent], + imports: [ + IonicModule.forRoot(), + FormsModule, + PipesModule, + TranslateModule.forChild(), + ], providers: [ { provide: UIHelper, diff --git a/src/app/beans/beans-add/beans-add.component.spec.ts b/src/app/beans/beans-add/beans-add.component.spec.ts index d5028fa3..c9edc96b 100644 --- a/src/app/beans/beans-add/beans-add.component.spec.ts +++ b/src/app/beans/beans-add/beans-add.component.spec.ts @@ -4,7 +4,6 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { BeansAddComponent } from './beans-add.component'; import { TranslateModule } from '@ngx-translate/core'; -import { KeysPipe } from '../../../pipes/keys'; import { FormsModule } from '@angular/forms'; import { IonicModule, ModalController } from '@ionic/angular'; import { InAppBrowser } from '@awesome-cordova-plugins/in-app-browser/ngx'; @@ -21,6 +20,7 @@ import { UIAnalytics } from '../../../services/uiAnalytics'; import { UIBeanHelper } from '../../../services/uiBeanHelper'; import { UISettingsStorage } from '../../../services/uiSettingsStorage'; import { Settings } from '../../../classes/settings/settings'; +import { PipesModule } from 'src/pipes/pipes.module'; describe('BeansAddComponent', () => { let component: BeansAddComponent; @@ -38,8 +38,9 @@ describe('BeansAddComponent', () => { FormsModule, CommonModule, IonicModule, + PipesModule, ], - declarations: [BeansAddComponent, KeysPipe], + declarations: [BeansAddComponent], providers: [ { provide: InAppBrowser }, { provide: ModalController }, diff --git a/src/app/beans/beans-edit/beans-edit.component.spec.ts b/src/app/beans/beans-edit/beans-edit.component.spec.ts index 6a3bcfa0..76ff6d18 100644 --- a/src/app/beans/beans-edit/beans-edit.component.spec.ts +++ b/src/app/beans/beans-edit/beans-edit.component.spec.ts @@ -6,7 +6,6 @@ import { FormsModule } from '@angular/forms'; import { Storage } from '@ionic/storage'; import { CommonModule } from '@angular/common'; import { IonicModule, ModalController } from '@ionic/angular'; -import { KeysPipe } from '../../../pipes/keys'; import { InAppBrowser } from '@awesome-cordova-plugins/in-app-browser/ngx'; import { File } from '@awesome-cordova-plugins/file/ngx'; import { Camera } from '@awesome-cordova-plugins/camera/ngx'; @@ -19,6 +18,7 @@ import { UIAnalytics } from '../../../services/uiAnalytics'; import { UIBeanHelper } from '../../../services/uiBeanHelper'; import { UISettingsStorage } from '../../../services/uiSettingsStorage'; import { Settings } from '../../../classes/settings/settings'; +import { PipesModule } from 'src/pipes/pipes.module'; describe('BeansEditComponent', () => { let component: BeansEditComponent; @@ -32,8 +32,9 @@ describe('BeansEditComponent', () => { FormsModule, CommonModule, IonicModule, + PipesModule, ], - declarations: [BeansEditComponent, KeysPipe, AsyncImageComponent], + declarations: [BeansEditComponent, AsyncImageComponent], providers: [ { provide: InAppBrowser }, { provide: ModalController }, diff --git a/src/app/beans/beans.page.spec.ts b/src/app/beans/beans.page.spec.ts index 0029fe0e..6ee5db82 100644 --- a/src/app/beans/beans.page.spec.ts +++ b/src/app/beans/beans.page.spec.ts @@ -6,7 +6,6 @@ import { FormsModule } from '@angular/forms'; import { Storage } from '@ionic/storage'; import { CommonModule } from '@angular/common'; import { IonicModule, ModalController } from '@ionic/angular'; -import { KeysPipe } from '../../pipes/keys'; import { InAppBrowser } from '@awesome-cordova-plugins/in-app-browser/ngx'; import { File } from '@awesome-cordova-plugins/file/ngx'; import { Camera } from '@awesome-cordova-plugins/camera/ngx'; @@ -14,12 +13,12 @@ import { ImagePicker } from '@awesome-cordova-plugins/image-picker/ngx'; import { AndroidPermissions } from '@awesome-cordova-plugins/android-permissions/ngx'; import { Router } from '@angular/router'; import { AsyncImageComponent } from '../../components/async-image/async-image.component'; -import { FormatDatePipe } from '../../pipes/formatDate'; import { UIBeanStorage } from '../../services/uiBeanStorage'; import { UISettingsStorage } from '../../services/uiSettingsStorage'; import { UIAnalytics } from '../../services/uiAnalytics'; import { IntentHandlerService } from '../../services/intentHandler/intent-handler.service'; import { UIBeanHelper } from '../../services/uiBeanHelper'; +import { PipesModule } from 'src/pipes/pipes.module'; describe('BeansPage', () => { let component: BeansPage; @@ -32,8 +31,9 @@ describe('BeansPage', () => { FormsModule, CommonModule, IonicModule, + PipesModule, ], - declarations: [BeansPage, KeysPipe, AsyncImageComponent, FormatDatePipe], + declarations: [BeansPage, AsyncImageComponent], providers: [ { provide: InAppBrowser }, { provide: ModalController }, diff --git a/src/app/brew/brew-add/brew-add.component.spec.ts b/src/app/brew/brew-add/brew-add.component.spec.ts index 8353394c..130225fa 100644 --- a/src/app/brew/brew-add/brew-add.component.spec.ts +++ b/src/app/brew/brew-add/brew-add.component.spec.ts @@ -6,7 +6,6 @@ import { FormsModule } from '@angular/forms'; import { Storage } from '@ionic/storage'; import { CommonModule } from '@angular/common'; import { IonicModule, ModalController, NavParams } from '@ionic/angular'; -import { KeysPipe } from '../../../pipes/keys'; import { InAppBrowser } from '@awesome-cordova-plugins/in-app-browser/ngx'; import { NavParamsMock, UIHelperMock } from '../../../classes/mock'; import { File } from '@awesome-cordova-plugins/file/ngx'; @@ -14,7 +13,6 @@ import { Camera } from '@awesome-cordova-plugins/camera/ngx'; import { ImagePicker } from '@awesome-cordova-plugins/image-picker/ngx'; import { AndroidPermissions } from '@awesome-cordova-plugins/android-permissions/ngx'; import { Router } from '@angular/router'; -import { FormatDatePipe } from '../../../pipes/formatDate'; import { BrewTimerComponent } from '../../../components/brew-timer/brew-timer.component'; import { AsyncImageComponent } from '../../../components/async-image/async-image.component'; import { UIBeanStorage } from '../../../services/uiBeanStorage'; @@ -34,6 +32,7 @@ import { Preparation } from '../../../classes/preparation/preparation'; import { Mill } from '../../../classes/mill/mill'; import { SocialSharing } from '@awesome-cordova-plugins/social-sharing/ngx'; import { UIHelper } from '../../../services/uiHelper'; +import { PipesModule } from 'src/pipes/pipes.module'; describe('BrewAddComponent', () => { let component: BrewAddComponent; @@ -47,14 +46,9 @@ describe('BrewAddComponent', () => { FormsModule, CommonModule, IonicModule, + PipesModule, ], - declarations: [ - BrewAddComponent, - KeysPipe, - FormatDatePipe, - BrewTimerComponent, - AsyncImageComponent, - ], + declarations: [BrewAddComponent, BrewTimerComponent, AsyncImageComponent], providers: [ { provide: InAppBrowser }, { provide: ModalController }, diff --git a/src/app/brew/brew-detail/brew-detail.component.spec.ts b/src/app/brew/brew-detail/brew-detail.component.spec.ts index f923ee53..ebb68450 100644 --- a/src/app/brew/brew-detail/brew-detail.component.spec.ts +++ b/src/app/brew/brew-detail/brew-detail.component.spec.ts @@ -6,7 +6,6 @@ import { FormsModule } from '@angular/forms'; import { Storage } from '@ionic/storage'; import { CommonModule } from '@angular/common'; import { IonicModule, ModalController, NavParams } from '@ionic/angular'; -import { KeysPipe } from '../../../pipes/keys'; import { InAppBrowser } from '@awesome-cordova-plugins/in-app-browser/ngx'; import { NavParamsMock } from '../../../classes/mock/NavParamsMock'; import { File } from '@awesome-cordova-plugins/file/ngx'; @@ -18,6 +17,7 @@ import { SocialSharing } from '@awesome-cordova-plugins/social-sharing/ngx'; import { FileTransfer } from '@awesome-cordova-plugins/file-transfer/ngx'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { ScreenOrientation } from '@awesome-cordova-plugins/screen-orientation/ngx'; +import { PipesModule } from 'src/pipes/pipes.module'; describe('BrewDetailComponent', () => { let component: BrewDetailComponent; @@ -31,8 +31,9 @@ describe('BrewDetailComponent', () => { CommonModule, IonicModule, HttpClientTestingModule, + PipesModule, ], - declarations: [BrewDetailComponent, KeysPipe], + declarations: [BrewDetailComponent], providers: [ { provide: InAppBrowser }, { provide: ModalController }, diff --git a/src/app/brew/brew-edit/brew-edit.component.spec.ts b/src/app/brew/brew-edit/brew-edit.component.spec.ts index 620d0f00..981c0bb1 100644 --- a/src/app/brew/brew-edit/brew-edit.component.spec.ts +++ b/src/app/brew/brew-edit/brew-edit.component.spec.ts @@ -6,7 +6,6 @@ import { FormsModule } from '@angular/forms'; import { Storage } from '@ionic/storage'; import { CommonModule } from '@angular/common'; import { IonicModule, ModalController, NavParams } from '@ionic/angular'; -import { KeysPipe } from '../../../pipes/keys'; import { InAppBrowser } from '@awesome-cordova-plugins/in-app-browser/ngx'; import { NavParamsMock } from '../../../classes/mock'; import { File } from '@awesome-cordova-plugins/file/ngx'; @@ -15,11 +14,11 @@ import { ImagePicker } from '@awesome-cordova-plugins/image-picker/ngx'; import { AndroidPermissions } from '@awesome-cordova-plugins/android-permissions/ngx'; import { Router } from '@angular/router'; import { AsyncImageComponent } from '../../../components/async-image/async-image.component'; -import { FormatDatePipe } from '../../../pipes/formatDate'; import { SocialSharing } from '@awesome-cordova-plugins/social-sharing/ngx'; import { FileTransfer } from '@awesome-cordova-plugins/file-transfer/ngx'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { Insomnia } from '@awesome-cordova-plugins/insomnia/ngx'; +import { PipesModule } from 'src/pipes/pipes.module'; describe('BrewEditComponent', () => { let component: BrewEditComponent; @@ -34,13 +33,9 @@ describe('BrewEditComponent', () => { CommonModule, IonicModule, HttpClientTestingModule, + PipesModule, ], - declarations: [ - BrewEditComponent, - KeysPipe, - AsyncImageComponent, - FormatDatePipe, - ], + declarations: [BrewEditComponent, AsyncImageComponent], providers: [ { provide: InAppBrowser }, { provide: ModalController }, diff --git a/src/app/brew/brew-popover-actions/brew-popover-actions.component.spec.ts b/src/app/brew/brew-popover-actions/brew-popover-actions.component.spec.ts index e47f74e5..b0065aa5 100644 --- a/src/app/brew/brew-popover-actions/brew-popover-actions.component.spec.ts +++ b/src/app/brew/brew-popover-actions/brew-popover-actions.component.spec.ts @@ -6,7 +6,6 @@ import { FormsModule } from '@angular/forms'; import { Storage } from '@ionic/storage'; import { CommonModule } from '@angular/common'; import { IonicModule, ModalController, NavParams } from '@ionic/angular'; -import { KeysPipe } from '../../../pipes/keys'; import { InAppBrowser } from '@awesome-cordova-plugins/in-app-browser/ngx'; import { NavParamsMock } from '../../../classes/mock/NavParamsMock'; import { File } from '@awesome-cordova-plugins/file/ngx'; @@ -18,6 +17,7 @@ import { UIHelper } from '../../../services/uiHelper'; import { UIPreparationStorage } from '../../../services/uiPreparationStorage'; import { Brew } from '../../../classes/brew/brew'; import { Preparation } from '../../../classes/preparation/preparation'; +import { PipesModule } from 'src/pipes/pipes.module'; describe('BrewPopoverActionsComponent', () => { let component: BrewPopoverActionsComponent; @@ -30,8 +30,9 @@ describe('BrewPopoverActionsComponent', () => { FormsModule, CommonModule, IonicModule, + PipesModule, ], - declarations: [BrewPopoverActionsComponent, KeysPipe], + declarations: [BrewPopoverActionsComponent], providers: [ { provide: InAppBrowser }, { provide: ModalController }, diff --git a/src/app/brew/brew.page.spec.ts b/src/app/brew/brew.page.spec.ts index 15afed97..72e94b0a 100644 --- a/src/app/brew/brew.page.spec.ts +++ b/src/app/brew/brew.page.spec.ts @@ -6,7 +6,6 @@ import { FormsModule } from '@angular/forms'; import { Storage } from '@ionic/storage'; import { CommonModule } from '@angular/common'; import { IonicModule, ModalController, NavParams } from '@ionic/angular'; -import { KeysPipe } from '../../pipes/keys'; import { InAppBrowser } from '@awesome-cordova-plugins/in-app-browser/ngx'; import { NavParamsMock } from '../../classes/mock/NavParamsMock'; import { File } from '@awesome-cordova-plugins/file/ngx'; @@ -18,6 +17,7 @@ import { BrewInformationComponent } from '../../components/brew-information/brew import { SocialSharing } from '@awesome-cordova-plugins/social-sharing/ngx'; import { UIHelper } from '../../services/uiHelper'; import { UIHelperMock } from '../../classes/mock'; +import { PipesModule } from 'src/pipes/pipes.module'; describe('BrewPage', () => { let component: BrewPage; @@ -30,8 +30,9 @@ describe('BrewPage', () => { FormsModule, CommonModule, IonicModule, + PipesModule, ], - declarations: [BrewPage, KeysPipe, BrewInformationComponent], + declarations: [BrewPage, BrewInformationComponent], providers: [ { provide: InAppBrowser }, { provide: ModalController }, diff --git a/src/app/home/home.page.spec.ts b/src/app/home/home.page.spec.ts index 6549281c..b0c7e16b 100644 --- a/src/app/home/home.page.spec.ts +++ b/src/app/home/home.page.spec.ts @@ -6,7 +6,6 @@ import { FormsModule } from '@angular/forms'; import { Storage } from '@ionic/storage'; import { CommonModule } from '@angular/common'; import { IonicModule, ModalController, NavParams } from '@ionic/angular'; -import { KeysPipe } from '../../pipes/keys'; import { InAppBrowser } from '@awesome-cordova-plugins/in-app-browser/ngx'; import { File } from '@awesome-cordova-plugins/file/ngx'; import { Camera } from '@awesome-cordova-plugins/camera/ngx'; @@ -15,6 +14,7 @@ import { AndroidPermissions } from '@awesome-cordova-plugins/android-permissions import { UIHelper } from '../../services/uiHelper'; import { NavParamsMock, UIHelperMock } from '../../classes/mock'; import { RouterTestingModule } from '@angular/router/testing'; +import { PipesModule } from 'src/pipes/pipes.module'; describe('HomePage', () => { let component: HomePage; @@ -28,8 +28,9 @@ describe('HomePage', () => { CommonModule, IonicModule, RouterTestingModule, + PipesModule, ], - declarations: [HomePage, KeysPipe], + declarations: [HomePage], providers: [ { provide: InAppBrowser }, { provide: ModalController }, diff --git a/src/app/info/about/about.component.spec.ts b/src/app/info/about/about.component.spec.ts index 79d81019..31ac21c8 100644 --- a/src/app/info/about/about.component.spec.ts +++ b/src/app/info/about/about.component.spec.ts @@ -6,7 +6,6 @@ import { FormsModule } from '@angular/forms'; import { Storage } from '@ionic/storage'; import { CommonModule } from '@angular/common'; import { IonicModule, ModalController, NavParams } from '@ionic/angular'; -import { KeysPipe } from '../../../pipes/keys'; import { InAppBrowser } from '@awesome-cordova-plugins/in-app-browser/ngx'; import { NavParamsMock } from '../../../classes/mock/NavParamsMock'; import { File } from '@awesome-cordova-plugins/file/ngx'; @@ -15,6 +14,7 @@ import { ImagePicker } from '@awesome-cordova-plugins/image-picker/ngx'; import { AndroidPermissions } from '@awesome-cordova-plugins/android-permissions/ngx'; import { Router } from '@angular/router'; import { AppVersion } from '@awesome-cordova-plugins/app-version/ngx'; +import { PipesModule } from 'src/pipes/pipes.module'; describe('AboutComponent', () => { let component: AboutComponent; @@ -27,8 +27,9 @@ describe('AboutComponent', () => { FormsModule, CommonModule, IonicModule, + PipesModule, ], - declarations: [AboutComponent, KeysPipe], + declarations: [AboutComponent], providers: [ { provide: InAppBrowser }, { provide: ModalController }, diff --git a/src/app/info/contact/contact.component.spec.ts b/src/app/info/contact/contact.component.spec.ts index eb61aae7..725db0b8 100644 --- a/src/app/info/contact/contact.component.spec.ts +++ b/src/app/info/contact/contact.component.spec.ts @@ -6,7 +6,6 @@ import { FormsModule } from '@angular/forms'; import { Storage } from '@ionic/storage'; import { CommonModule } from '@angular/common'; import { IonicModule, ModalController, NavParams } from '@ionic/angular'; -import { KeysPipe } from '../../../pipes/keys'; import { InAppBrowser } from '@awesome-cordova-plugins/in-app-browser/ngx'; import { NavParamsMock } from '../../../classes/mock/NavParamsMock'; import { File } from '@awesome-cordova-plugins/file/ngx'; @@ -16,6 +15,7 @@ import { AndroidPermissions } from '@awesome-cordova-plugins/android-permissions import { Router } from '@angular/router'; import { UIHelper } from '../../../services/uiHelper'; import { UIHelperMock } from '../../../classes/mock'; +import { PipesModule } from 'src/pipes/pipes.module'; describe('ContactComponent', () => { let component: ContactComponent; @@ -28,8 +28,9 @@ describe('ContactComponent', () => { FormsModule, CommonModule, IonicModule, + PipesModule, ], - declarations: [ContactComponent, KeysPipe], + declarations: [ContactComponent], providers: [ { provide: InAppBrowser }, { provide: ModalController }, diff --git a/src/app/info/credits/credits.component.spec.ts b/src/app/info/credits/credits.component.spec.ts index 486acbe6..c5927903 100644 --- a/src/app/info/credits/credits.component.spec.ts +++ b/src/app/info/credits/credits.component.spec.ts @@ -6,7 +6,6 @@ import { FormsModule } from '@angular/forms'; import { Storage } from '@ionic/storage'; import { CommonModule } from '@angular/common'; import { IonicModule, ModalController, NavParams } from '@ionic/angular'; -import { KeysPipe } from '../../../pipes/keys'; import { InAppBrowser } from '@awesome-cordova-plugins/in-app-browser/ngx'; import { NavParamsMock } from '../../../classes/mock/NavParamsMock'; import { File } from '@awesome-cordova-plugins/file/ngx'; @@ -16,6 +15,7 @@ import { AndroidPermissions } from '@awesome-cordova-plugins/android-permissions import { Router } from '@angular/router'; import { SocialSharing } from '@awesome-cordova-plugins/social-sharing/ngx'; import { FileTransfer } from '@awesome-cordova-plugins/file-transfer/ngx'; +import { PipesModule } from 'src/pipes/pipes.module'; describe('CreditsComponent', () => { let component: CreditsComponent; @@ -28,8 +28,9 @@ describe('CreditsComponent', () => { FormsModule, CommonModule, IonicModule, + PipesModule, ], - declarations: [CreditsComponent, KeysPipe], + declarations: [CreditsComponent], providers: [ { provide: InAppBrowser }, { provide: ModalController }, diff --git a/src/app/info/licences/licences.component.spec.ts b/src/app/info/licences/licences.component.spec.ts index ae68b560..139cf5ef 100644 --- a/src/app/info/licences/licences.component.spec.ts +++ b/src/app/info/licences/licences.component.spec.ts @@ -6,7 +6,6 @@ import { FormsModule } from '@angular/forms'; import { Storage } from '@ionic/storage'; import { CommonModule } from '@angular/common'; import { IonicModule, ModalController, NavParams } from '@ionic/angular'; -import { KeysPipe } from '../../../pipes/keys'; import { InAppBrowser } from '@awesome-cordova-plugins/in-app-browser/ngx'; import { NavParamsMock } from '../../../classes/mock/NavParamsMock'; import { File } from '@awesome-cordova-plugins/file/ngx'; @@ -16,6 +15,7 @@ import { AndroidPermissions } from '@awesome-cordova-plugins/android-permissions import { Router } from '@angular/router'; import { UIHelper } from '../../../services/uiHelper'; import { UIHelperMock } from '../../../classes/mock'; +import { PipesModule } from 'src/pipes/pipes.module'; describe('LicencesComponent', () => { let component: LicencesComponent; @@ -28,8 +28,9 @@ describe('LicencesComponent', () => { FormsModule, CommonModule, IonicModule, + PipesModule, ], - declarations: [LicencesComponent, KeysPipe], + declarations: [LicencesComponent], providers: [ { provide: InAppBrowser }, { provide: ModalController }, diff --git a/src/app/info/log/log.component.spec.ts b/src/app/info/log/log.component.spec.ts index a9a28a66..d3cd3986 100644 --- a/src/app/info/log/log.component.spec.ts +++ b/src/app/info/log/log.component.spec.ts @@ -6,7 +6,6 @@ import { FormsModule } from '@angular/forms'; import { Storage } from '@ionic/storage'; import { CommonModule } from '@angular/common'; import { IonicModule, ModalController, NavParams } from '@ionic/angular'; -import { KeysPipe } from '../../../pipes/keys'; import { InAppBrowser } from '@awesome-cordova-plugins/in-app-browser/ngx'; import { NavParamsMock, UIHelperMock } from '../../../classes/mock'; import { File } from '@awesome-cordova-plugins/file/ngx'; @@ -16,6 +15,7 @@ import { AndroidPermissions } from '@awesome-cordova-plugins/android-permissions import { Router } from '@angular/router'; import { SocialSharing } from '@awesome-cordova-plugins/social-sharing/ngx'; import { UIHelper } from '../../../services/uiHelper'; +import { PipesModule } from 'src/pipes/pipes.module'; describe('LogComponent', () => { let component: LogComponent; @@ -28,8 +28,9 @@ describe('LogComponent', () => { TranslateModule.forRoot(), CommonModule, IonicModule, + PipesModule, ], - declarations: [LogComponent, KeysPipe], + declarations: [LogComponent], providers: [ { provide: InAppBrowser }, { provide: ModalController }, diff --git a/src/app/info/privacy/privacy.component.spec.ts b/src/app/info/privacy/privacy.component.spec.ts index 13363204..19563a69 100644 --- a/src/app/info/privacy/privacy.component.spec.ts +++ b/src/app/info/privacy/privacy.component.spec.ts @@ -6,7 +6,6 @@ import { FormsModule } from '@angular/forms'; import { Storage } from '@ionic/storage'; import { CommonModule } from '@angular/common'; import { IonicModule, ModalController, NavParams } from '@ionic/angular'; -import { KeysPipe } from '../../../pipes/keys'; import { InAppBrowser } from '@awesome-cordova-plugins/in-app-browser/ngx'; import { NavParamsMock } from '../../../classes/mock/NavParamsMock'; import { File } from '@awesome-cordova-plugins/file/ngx'; @@ -16,6 +15,7 @@ import { AndroidPermissions } from '@awesome-cordova-plugins/android-permissions import { Router } from '@angular/router'; import { UIHelper } from '../../../services/uiHelper'; import { UIHelperMock } from '../../../classes/mock'; +import { PipesModule } from 'src/pipes/pipes.module'; describe('PrivacyComponent', () => { let component: PrivacyComponent; @@ -28,8 +28,9 @@ describe('PrivacyComponent', () => { FormsModule, CommonModule, IonicModule, + PipesModule, ], - declarations: [PrivacyComponent, KeysPipe], + declarations: [PrivacyComponent], providers: [ { provide: InAppBrowser }, { provide: ModalController }, diff --git a/src/app/info/terms/terms.component.spec.ts b/src/app/info/terms/terms.component.spec.ts index efa39129..0101c4a0 100644 --- a/src/app/info/terms/terms.component.spec.ts +++ b/src/app/info/terms/terms.component.spec.ts @@ -6,7 +6,6 @@ import { FormsModule } from '@angular/forms'; import { Storage } from '@ionic/storage'; import { CommonModule } from '@angular/common'; import { IonicModule, ModalController, NavParams } from '@ionic/angular'; -import { KeysPipe } from '../../../pipes/keys'; import { InAppBrowser } from '@awesome-cordova-plugins/in-app-browser/ngx'; import { NavParamsMock } from '../../../classes/mock/NavParamsMock'; import { File } from '@awesome-cordova-plugins/file/ngx'; @@ -16,6 +15,7 @@ import { AndroidPermissions } from '@awesome-cordova-plugins/android-permissions import { RouterTestingModule } from '@angular/router/testing'; import { UIHelper } from '../../../services/uiHelper'; import { UIHelperMock } from '../../../classes/mock'; +import { PipesModule } from 'src/pipes/pipes.module'; describe('TermsComponent', () => { let component: TermsComponent; @@ -29,8 +29,9 @@ describe('TermsComponent', () => { CommonModule, IonicModule, RouterTestingModule, + PipesModule, ], - declarations: [TermsComponent, KeysPipe], + declarations: [TermsComponent], providers: [ { provide: InAppBrowser }, { provide: ModalController }, diff --git a/src/app/info/thanks/thanks.component.spec.ts b/src/app/info/thanks/thanks.component.spec.ts index 67b33a37..4c5744f3 100644 --- a/src/app/info/thanks/thanks.component.spec.ts +++ b/src/app/info/thanks/thanks.component.spec.ts @@ -6,7 +6,6 @@ import { FormsModule } from '@angular/forms'; import { Storage } from '@ionic/storage'; import { CommonModule } from '@angular/common'; import { IonicModule, ModalController, NavParams } from '@ionic/angular'; -import { KeysPipe } from '../../../pipes/keys'; import { InAppBrowser } from '@awesome-cordova-plugins/in-app-browser/ngx'; import { NavParamsMock } from '../../../classes/mock/NavParamsMock'; import { File } from '@awesome-cordova-plugins/file/ngx'; @@ -14,6 +13,7 @@ import { Camera } from '@awesome-cordova-plugins/camera/ngx'; import { ImagePicker } from '@awesome-cordova-plugins/image-picker/ngx'; import { AndroidPermissions } from '@awesome-cordova-plugins/android-permissions/ngx'; import { Router } from '@angular/router'; +import { PipesModule } from 'src/pipes/pipes.module'; describe('ThanksComponent', () => { let component: ThanksComponent; @@ -26,8 +26,9 @@ describe('ThanksComponent', () => { FormsModule, CommonModule, IonicModule, + PipesModule, ], - declarations: [ThanksComponent, KeysPipe], + declarations: [ThanksComponent], providers: [ { provide: InAppBrowser }, { provide: ModalController }, diff --git a/src/app/mill/mill-add/mill-add.component.spec.ts b/src/app/mill/mill-add/mill-add.component.spec.ts index 35ffd874..be7c7bb8 100644 --- a/src/app/mill/mill-add/mill-add.component.spec.ts +++ b/src/app/mill/mill-add/mill-add.component.spec.ts @@ -6,7 +6,6 @@ import { FormsModule } from '@angular/forms'; import { Storage } from '@ionic/storage'; import { CommonModule } from '@angular/common'; import { IonicModule, ModalController, NavParams } from '@ionic/angular'; -import { KeysPipe } from '../../../pipes/keys'; import { InAppBrowser } from '@awesome-cordova-plugins/in-app-browser/ngx'; import { NavParamsMock } from '../../../classes/mock/NavParamsMock'; import { File } from '@awesome-cordova-plugins/file/ngx'; @@ -16,6 +15,7 @@ import { AndroidPermissions } from '@awesome-cordova-plugins/android-permissions import { Router } from '@angular/router'; import { UIHelper } from '../../../services/uiHelper'; import { UIHelperMock } from '../../../classes/mock'; +import { PipesModule } from 'src/pipes/pipes.module'; describe('MillAddComponent', () => { let component: MillAddComponent; @@ -28,8 +28,9 @@ describe('MillAddComponent', () => { FormsModule, CommonModule, IonicModule, + PipesModule, ], - declarations: [MillAddComponent, KeysPipe], + declarations: [MillAddComponent], providers: [ { provide: InAppBrowser }, { provide: ModalController }, diff --git a/src/app/mill/mill-edit/mill-edit.component.spec.ts b/src/app/mill/mill-edit/mill-edit.component.spec.ts index bf0df4c7..9752510f 100644 --- a/src/app/mill/mill-edit/mill-edit.component.spec.ts +++ b/src/app/mill/mill-edit/mill-edit.component.spec.ts @@ -6,7 +6,6 @@ import { FormsModule } from '@angular/forms'; import { Storage } from '@ionic/storage'; import { CommonModule } from '@angular/common'; import { IonicModule, ModalController, NavParams } from '@ionic/angular'; -import { KeysPipe } from '../../../pipes/keys'; import { InAppBrowser } from '@awesome-cordova-plugins/in-app-browser/ngx'; import { NavParamsMock, UIHelperMock } from '../../../classes/mock'; import { File } from '@awesome-cordova-plugins/file/ngx'; @@ -15,6 +14,7 @@ import { ImagePicker } from '@awesome-cordova-plugins/image-picker/ngx'; import { AndroidPermissions } from '@awesome-cordova-plugins/android-permissions/ngx'; import { Router } from '@angular/router'; import { UIHelper } from '../../../services/uiHelper'; +import { PipesModule } from 'src/pipes/pipes.module'; describe('MillEditComponent', () => { let component: MillEditComponent; @@ -27,8 +27,9 @@ describe('MillEditComponent', () => { FormsModule, CommonModule, IonicModule, + PipesModule, ], - declarations: [MillEditComponent, KeysPipe], + declarations: [MillEditComponent], providers: [ { provide: InAppBrowser }, { provide: ModalController }, diff --git a/src/app/mill/mill.page.spec.ts b/src/app/mill/mill.page.spec.ts index a5d7fdd4..47c3b9e7 100644 --- a/src/app/mill/mill.page.spec.ts +++ b/src/app/mill/mill.page.spec.ts @@ -6,7 +6,6 @@ import { FormsModule } from '@angular/forms'; import { Storage } from '@ionic/storage'; import { CommonModule } from '@angular/common'; import { IonicModule, ModalController, NavParams } from '@ionic/angular'; -import { KeysPipe } from '../../pipes/keys'; import { InAppBrowser } from '@awesome-cordova-plugins/in-app-browser/ngx'; import { NavParamsMock } from '../../classes/mock/NavParamsMock'; import { File } from '@awesome-cordova-plugins/file/ngx'; @@ -16,6 +15,7 @@ import { AndroidPermissions } from '@awesome-cordova-plugins/android-permissions import { Router } from '@angular/router'; import { UIHelper } from '../../services/uiHelper'; import { UIHelperMock } from '../../classes/mock'; +import { PipesModule } from 'src/pipes/pipes.module'; describe('MillPage', () => { let component: MillPage; @@ -28,8 +28,9 @@ describe('MillPage', () => { FormsModule, CommonModule, IonicModule, + PipesModule, ], - declarations: [MillPage, KeysPipe], + declarations: [MillPage], providers: [ { provide: InAppBrowser }, { provide: ModalController }, diff --git a/src/app/preparation/preparation-add/preparation-add.component.spec.ts b/src/app/preparation/preparation-add/preparation-add.component.spec.ts index 209b893d..10fe5ca3 100644 --- a/src/app/preparation/preparation-add/preparation-add.component.spec.ts +++ b/src/app/preparation/preparation-add/preparation-add.component.spec.ts @@ -3,8 +3,8 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { PreparationAddComponent } from './preparation-add.component'; import { TranslateModule } from '@ngx-translate/core'; import { ModalController } from '@ionic/angular'; -import { KeysPipe } from '../../../pipes/keys'; import { UIAnalytics } from '../../../services/uiAnalytics'; +import { PipesModule } from 'src/pipes/pipes.module'; describe('PreparationAddComponent', () => { let component: PreparationAddComponent; @@ -12,8 +12,8 @@ describe('PreparationAddComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - imports: [TranslateModule.forRoot()], - declarations: [PreparationAddComponent, KeysPipe], + imports: [TranslateModule.forRoot(), PipesModule], + declarations: [PreparationAddComponent], providers: [ { provide: UIAnalytics, diff --git a/src/app/preparation/preparation-edit/preparation-edit.component.spec.ts b/src/app/preparation/preparation-edit/preparation-edit.component.spec.ts index 79e65692..e384490b 100644 --- a/src/app/preparation/preparation-edit/preparation-edit.component.spec.ts +++ b/src/app/preparation/preparation-edit/preparation-edit.component.spec.ts @@ -6,7 +6,6 @@ import { FormsModule } from '@angular/forms'; import { Storage } from '@ionic/storage'; import { CommonModule } from '@angular/common'; import { IonicModule, ModalController, NavParams } from '@ionic/angular'; -import { KeysPipe } from '../../../pipes/keys'; import { InAppBrowser } from '@awesome-cordova-plugins/in-app-browser/ngx'; import { NavParamsMock, UIHelperMock } from '../../../classes/mock'; import { File } from '@awesome-cordova-plugins/file/ngx'; @@ -16,6 +15,7 @@ import { AndroidPermissions } from '@awesome-cordova-plugins/android-permissions import { Router } from '@angular/router'; import { UIHelper } from '../../../services/uiHelper'; import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { PipesModule } from 'src/pipes/pipes.module'; describe('PreparationEditComponent', () => { let component: PreparationEditComponent; @@ -29,8 +29,9 @@ describe('PreparationEditComponent', () => { CommonModule, IonicModule, HttpClientTestingModule, + PipesModule, ], - declarations: [PreparationEditComponent, KeysPipe], + declarations: [PreparationEditComponent], providers: [ { provide: InAppBrowser }, { provide: ModalController }, diff --git a/src/app/preparation/preparation.page.spec.ts b/src/app/preparation/preparation.page.spec.ts index aaf44a40..6a3b223e 100644 --- a/src/app/preparation/preparation.page.spec.ts +++ b/src/app/preparation/preparation.page.spec.ts @@ -6,7 +6,6 @@ import { FormsModule } from '@angular/forms'; import { Storage } from '@ionic/storage'; import { CommonModule } from '@angular/common'; import { IonicModule, ModalController, NavParams } from '@ionic/angular'; -import { KeysPipe } from '../../pipes/keys'; import { InAppBrowser } from '@awesome-cordova-plugins/in-app-browser/ngx'; import { NavParamsMock } from '../../classes/mock/NavParamsMock'; import { File } from '@awesome-cordova-plugins/file/ngx'; @@ -17,6 +16,7 @@ import { Router } from '@angular/router'; import { UIHelper } from '../../services/uiHelper'; import { UIHelperMock } from '../../classes/mock'; import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { PipesModule } from 'src/pipes/pipes.module'; describe('PreparationPage', () => { let component: PreparationPage; @@ -30,8 +30,9 @@ describe('PreparationPage', () => { CommonModule, IonicModule, HttpClientTestingModule, + PipesModule, ], - declarations: [PreparationPage, KeysPipe], + declarations: [PreparationPage], providers: [ { provide: InAppBrowser }, { provide: ModalController }, diff --git a/src/app/settings/settings.page.spec.ts b/src/app/settings/settings.page.spec.ts index efcc0ea4..85debc00 100644 --- a/src/app/settings/settings.page.spec.ts +++ b/src/app/settings/settings.page.spec.ts @@ -6,7 +6,6 @@ import { FormsModule } from '@angular/forms'; import { Storage } from '@ionic/storage'; import { CommonModule } from '@angular/common'; import { IonicModule, ModalController, NavParams } from '@ionic/angular'; -import { KeysPipe } from '../../pipes/keys'; import { InAppBrowser } from '@awesome-cordova-plugins/in-app-browser/ngx'; import { NavParamsMock } from '../../classes/mock/NavParamsMock'; import { File } from '@awesome-cordova-plugins/file/ngx'; @@ -21,6 +20,7 @@ import { UIHelperMock } from '../../classes/mock'; import { UIHelper } from '../../services/uiHelper'; import { AppVersion } from '@awesome-cordova-plugins/app-version/ngx'; import { FileTransfer } from '@awesome-cordova-plugins/file-transfer/ngx'; +import { PipesModule } from 'src/pipes/pipes.module'; describe('SettingsPage', () => { let component: SettingsPage; @@ -33,8 +33,9 @@ describe('SettingsPage', () => { FormsModule, CommonModule, IonicModule, + PipesModule, ], - declarations: [SettingsPage, KeysPipe], + declarations: [SettingsPage], providers: [ { provide: InAppBrowser }, { provide: ModalController }, diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 79005eab..d8155572 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -1,7 +1,5 @@ import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; -import { FormatDatePipe } from '../../pipes/formatDate'; -import { KeysPipe } from '../../pipes/keys'; import { CommonModule } from '@angular/common'; import { IonicModule } from '@ionic/angular'; import { AsyncImageComponent } from '../../components/async-image/async-image.component'; @@ -52,7 +50,6 @@ import { LogTextComponent } from '../info/log/log-text/log-text.component'; import { TranslateModule } from '@ngx-translate/core'; import { Globalization } from '@awesome-cordova-plugins/globalization/ngx'; import { AppVersion } from '@awesome-cordova-plugins/app-version/ngx'; -import { EnumToArrayPipe } from '../../pipes/enumToArray'; import { HelperPage } from '../helper/helper.page'; import { BrewInformationComponent } from '../../components/brew-information/brew-information.component'; import { CuppingRadarComponent } from '../../components/cupping-radar/cupping-radar.component'; @@ -191,6 +188,7 @@ import { MeticulousHelpPopoverComponent } from '../../popover/meticulous-help-po import { BeanPopoverFreezeComponent } from '../beans/bean-popover-freeze/bean-popover-freeze.component'; import { BeanFreezeInformationComponent } from '../../components/beans/bean-freeze-information/bean-freeze-information.component'; import { BeanPopoverFrozenListComponent } from '../beans/bean-popover-frozen-list/bean-popover-frozen-list.component'; +import { PipesModule } from 'src/pipes/pipes.module'; @NgModule({ declarations: [ @@ -273,9 +271,6 @@ import { BeanPopoverFrozenListComponent } from '../beans/bean-popover-frozen-lis BrewChoosePreparationToBrewComponent, BrewFlavorPickerComponent, BrewBeverageQuantityCalculatorComponent, - FormatDatePipe, - KeysPipe, - EnumToArrayPipe, AsyncImageComponent, BrewInformationComponent, BrewGraphReferenceCardComponent, @@ -368,6 +363,7 @@ import { BeanPopoverFrozenListComponent } from '../beans/bean-popover-frozen-lis RouterModule, NgxStarsModule, AgVirtualScrollModule, + PipesModule, ], providers: [ AppVersion, @@ -387,9 +383,6 @@ import { BeanPopoverFrozenListComponent } from '../beans/bean-popover-frozen-lis TooltipDirective, TransformDateDirective, DisableDoubleClickDirective, - FormatDatePipe, - KeysPipe, - EnumToArrayPipe, InAppBrowser, File, Device, @@ -487,9 +480,6 @@ import { BeanPopoverFrozenListComponent } from '../beans/bean-popover-frozen-lis BrewChoosePreparationToBrewComponent, BrewFlavorPickerComponent, BrewBeverageQuantityCalculatorComponent, - FormatDatePipe, - KeysPipe, - EnumToArrayPipe, AsyncImageComponent, BrewInformationComponent, BrewGraphReferenceCardComponent, diff --git a/src/app/statistic/statistic.page.spec.ts b/src/app/statistic/statistic.page.spec.ts index 4cf3429b..e8dda4eb 100644 --- a/src/app/statistic/statistic.page.spec.ts +++ b/src/app/statistic/statistic.page.spec.ts @@ -6,7 +6,6 @@ import { FormsModule } from '@angular/forms'; import { Storage } from '@ionic/storage'; import { CommonModule } from '@angular/common'; import { IonicModule, ModalController, NavParams } from '@ionic/angular'; -import { KeysPipe } from '../../pipes/keys'; import { InAppBrowser } from '@awesome-cordova-plugins/in-app-browser/ngx'; import { NavParamsMock } from '../../classes/mock/NavParamsMock'; import { File } from '@awesome-cordova-plugins/file/ngx'; @@ -16,6 +15,7 @@ import { AndroidPermissions } from '@awesome-cordova-plugins/android-permissions import { Router } from '@angular/router'; import { UIHelper } from '../../services/uiHelper'; import { UIHelperMock } from '../../classes/mock'; +import { PipesModule } from 'src/pipes/pipes.module'; describe('StatisticPage', () => { let component: StatisticPage; @@ -28,8 +28,9 @@ describe('StatisticPage', () => { FormsModule, CommonModule, IonicModule, + PipesModule, ], - declarations: [StatisticPage, KeysPipe], + declarations: [StatisticPage], providers: [ { provide: InAppBrowser }, { provide: ModalController }, diff --git a/src/app/water-section/water/water-add/water-add.component.spec.ts b/src/app/water-section/water/water-add/water-add.component.spec.ts index a1c9053c..ec6af628 100644 --- a/src/app/water-section/water/water-add/water-add.component.spec.ts +++ b/src/app/water-section/water/water-add/water-add.component.spec.ts @@ -7,7 +7,7 @@ import { TranslateModule } from '@ngx-translate/core'; import { UIHelper } from '../../../../services/uiHelper'; import { UIHelperMock } from '../../../../classes/mock'; import { FormsModule } from '@angular/forms'; -import { KeysPipe } from 'src/pipes/keys'; +import { PipesModule } from 'src/pipes/pipes.module'; describe('WaterAddComponent', () => { let component: WaterAddComponent; @@ -15,8 +15,13 @@ describe('WaterAddComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - declarations: [WaterAddComponent, KeysPipe], - imports: [IonicModule.forRoot(), TranslateModule.forRoot(), FormsModule], + declarations: [WaterAddComponent], + imports: [ + IonicModule.forRoot(), + TranslateModule.forRoot(), + FormsModule, + PipesModule, + ], providers: [ { provide: Storage }, { provide: UIHelper, useClass: UIHelperMock }, diff --git a/src/app/water-section/water/water-edit/water-edit.component.spec.ts b/src/app/water-section/water/water-edit/water-edit.component.spec.ts index f6c73e39..2bbbdafb 100644 --- a/src/app/water-section/water/water-edit/water-edit.component.spec.ts +++ b/src/app/water-section/water/water-edit/water-edit.component.spec.ts @@ -7,7 +7,7 @@ import { TranslateModule } from '@ngx-translate/core'; import { UIHelper } from '../../../../services/uiHelper'; import { UIHelperMock } from '../../../../classes/mock'; import { FormsModule } from '@angular/forms'; -import { KeysPipe } from '../../../../pipes/keys'; +import { PipesModule } from 'src/pipes/pipes.module'; describe('WaterEditComponent', () => { let component: WaterEditComponent; @@ -15,8 +15,13 @@ describe('WaterEditComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - declarations: [WaterEditComponent, KeysPipe], - imports: [IonicModule.forRoot(), TranslateModule.forRoot(), FormsModule], + declarations: [WaterEditComponent], + imports: [ + IonicModule.forRoot(), + TranslateModule.forRoot(), + FormsModule, + PipesModule, + ], providers: [ { provide: Storage }, { provide: UIHelper, useClass: UIHelperMock }, diff --git a/src/components/beans/bean-general-information/bean-general-information.component.spec.ts b/src/components/beans/bean-general-information/bean-general-information.component.spec.ts index 3dd74c0a..7394397b 100644 --- a/src/components/beans/bean-general-information/bean-general-information.component.spec.ts +++ b/src/components/beans/bean-general-information/bean-general-information.component.spec.ts @@ -8,8 +8,8 @@ import { UIHelper } from '../../../services/uiHelper'; import { UIHelperMock } from '../../../classes/mock'; import { Bean } from '../../../classes/bean/bean'; import { FormsModule } from '@angular/forms'; -import { KeysPipe } from '../../../pipes/keys'; import { Config } from '../../../classes/objectConfig/objectConfig'; +import { PipesModule } from 'src/pipes/pipes.module'; describe('BeanGeneralInformationComponent', () => { let component: BeanGeneralInformationComponent; @@ -17,8 +17,13 @@ describe('BeanGeneralInformationComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - declarations: [BeanGeneralInformationComponent, KeysPipe], - imports: [IonicModule.forRoot(), TranslateModule.forRoot(), FormsModule], + declarations: [BeanGeneralInformationComponent], + imports: [ + IonicModule.forRoot(), + TranslateModule.forRoot(), + FormsModule, + PipesModule, + ], providers: [ { provide: Storage }, { provide: UIHelper, useClass: UIHelperMock }, diff --git a/src/components/brew-graph-reference-card/brew-graph-reference-card.component.spec.ts b/src/components/brew-graph-reference-card/brew-graph-reference-card.component.spec.ts index 0c859cc1..63b39160 100644 --- a/src/components/brew-graph-reference-card/brew-graph-reference-card.component.spec.ts +++ b/src/components/brew-graph-reference-card/brew-graph-reference-card.component.spec.ts @@ -7,11 +7,11 @@ import { TranslateModule } from '@ngx-translate/core'; import { UIHelper } from '../../services/uiHelper'; import { UIHelperMock } from '../../classes/mock'; import { Brew } from '../../classes/brew/brew'; -import { FormatDatePipe } from '../../pipes/formatDate'; import { IBrew } from '../../interfaces/brew/iBrew'; import { Bean } from '../../classes/bean/bean'; import { Preparation } from '../../classes/preparation/preparation'; import { Mill } from '../../classes/mill/mill'; +import { PipesModule } from 'src/pipes/pipes.module'; describe('BrewGraphReferenceCardComponent', () => { let component: BrewGraphReferenceCardComponent; @@ -19,8 +19,8 @@ describe('BrewGraphReferenceCardComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - declarations: [BrewGraphReferenceCardComponent, FormatDatePipe], - imports: [IonicModule.forRoot(), TranslateModule.forRoot()], + declarations: [BrewGraphReferenceCardComponent], + imports: [IonicModule.forRoot(), TranslateModule.forRoot(), PipesModule], providers: [ { provide: Storage }, { provide: UIHelper, useClass: UIHelperMock }, diff --git a/src/components/brew-information/brew-information.component.spec.ts b/src/components/brew-information/brew-information.component.spec.ts index 0ea528ee..eb87c076 100644 --- a/src/components/brew-information/brew-information.component.spec.ts +++ b/src/components/brew-information/brew-information.component.spec.ts @@ -13,12 +13,12 @@ import { UIImage } from '../../services/uiImage'; import { SocialSharing } from '@awesome-cordova-plugins/social-sharing/ngx'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { FileTransfer } from '@awesome-cordova-plugins/file-transfer/ngx'; -import { FormatDatePipe } from '../../pipes/formatDate'; import { Brew } from '../../classes/brew/brew'; import { IBrew } from '../../interfaces/brew/iBrew'; import { Bean } from '../../classes/bean/bean'; import { Preparation } from '../../classes/preparation/preparation'; import { Mill } from '../../classes/mill/mill'; +import { PipesModule } from 'src/pipes/pipes.module'; describe('BrewInformationComponent', () => { let component: BrewInformationComponent; @@ -26,11 +26,12 @@ describe('BrewInformationComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - declarations: [BrewInformationComponent, FormatDatePipe], + declarations: [BrewInformationComponent], imports: [ IonicModule.forRoot(), TranslateModule.forRoot(), HttpClientTestingModule, + PipesModule, ], schemas: [CUSTOM_ELEMENTS_SCHEMA], providers: [ diff --git a/src/components/brews/brew-brewing/brew-brewing.component.spec.ts b/src/components/brews/brew-brewing/brew-brewing.component.spec.ts index a9484214..8bea215c 100644 --- a/src/components/brews/brew-brewing/brew-brewing.component.spec.ts +++ b/src/components/brews/brew-brewing/brew-brewing.component.spec.ts @@ -13,8 +13,8 @@ import { ScreenOrientation } from '@awesome-cordova-plugins/screen-orientation/n import { HttpClientTestingModule } from '@angular/common/http/testing'; import { BrewBrewingGraphComponent } from '../brew-brewing-graph/brew-brewing-graph.component'; import { FormsModule } from '@angular/forms'; -import { KeysPipe } from 'src/pipes/keys'; import { BrewBrewingPreparationDeviceComponent } from '../brew-brewing-preparation-device/brew-brewing-preparation-device.component'; +import { PipesModule } from 'src/pipes/pipes.module'; describe('BrewBrewingComponent', () => { let component: BrewBrewingComponent; @@ -26,13 +26,13 @@ describe('BrewBrewingComponent', () => { BrewBrewingComponent, BrewBrewingGraphComponent, BrewBrewingPreparationDeviceComponent, - KeysPipe, ], imports: [ IonicModule.forRoot(), TranslateModule.forRoot(), HttpClientTestingModule, FormsModule, + PipesModule, ], providers: [ { provide: Storage }, diff --git a/src/pipes/pipes.module.ts b/src/pipes/pipes.module.ts new file mode 100644 index 00000000..e8246313 --- /dev/null +++ b/src/pipes/pipes.module.ts @@ -0,0 +1,10 @@ +import { NgModule } from '@angular/core'; +import { KeysPipe } from './keys'; +import { EnumToArrayPipe } from './enumToArray'; +import { FormatDatePipe } from './formatDate'; + +@NgModule({ + declarations: [EnumToArrayPipe, FormatDatePipe, KeysPipe], + exports: [EnumToArrayPipe, FormatDatePipe, KeysPipe], +}) +export class PipesModule {} From deed78389a08429c0c707417e46162fb97adb1af Mon Sep 17 00:00:00 2001 From: Juan Silveira Date: Sat, 24 Aug 2024 16:44:32 +0900 Subject: [PATCH 08/55] Fix BeanPopoverFreezeComponent test. --- .../bean-popover-freeze.component.spec.ts | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/app/beans/bean-popover-freeze/bean-popover-freeze.component.spec.ts b/src/app/beans/bean-popover-freeze/bean-popover-freeze.component.spec.ts index 4a7219ed..55aeb490 100644 --- a/src/app/beans/bean-popover-freeze/bean-popover-freeze.component.spec.ts +++ b/src/app/beans/bean-popover-freeze/bean-popover-freeze.component.spec.ts @@ -2,6 +2,12 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { IonicModule } from '@ionic/angular'; import { BeanPopoverFreezeComponent } from './bean-popover-freeze.component'; +import { IonicStorageModule } from '@ionic/storage-angular'; +import { TranslateModule } from '@ngx-translate/core'; +import { UIHelper } from 'src/services/uiHelper'; +import { UIHelperMock } from 'src/classes/mock'; +import { PipesModule } from 'src/pipes/PipesModule'; +import { Bean } from 'src/classes/bean/bean'; describe('BeanPopoverFreezeComponent', () => { let component: BeanPopoverFreezeComponent; @@ -10,13 +16,23 @@ describe('BeanPopoverFreezeComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ declarations: [BeanPopoverFreezeComponent], - imports: [IonicModule.forRoot()], + imports: [ + IonicModule.forRoot(), + IonicStorageModule.forRoot(), + TranslateModule.forChild(), + TranslateModule.forRoot(), + PipesModule, + ], + providers: [{ provide: UIHelper, useClass: UIHelperMock }], }).compileComponents(); + })); + beforeEach(() => { fixture = TestBed.createComponent(BeanPopoverFreezeComponent); component = fixture.componentInstance; + component.bean = new Bean(); fixture.detectChanges(); - })); + }); it('should create', () => { expect(component).toBeTruthy(); From 3e320474f89df656b92973ecc13ad24bddd004db Mon Sep 17 00:00:00 2001 From: Juan Silveira Date: Sat, 24 Aug 2024 16:48:46 +0900 Subject: [PATCH 09/55] Fix PreparationSortToolsComponent test. --- .../preparation-sort-tools.component.spec.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/app/preparation/preparation-sort-tools/preparation-sort-tools.component.spec.ts b/src/app/preparation/preparation-sort-tools/preparation-sort-tools.component.spec.ts index d24f5651..e8acfd72 100644 --- a/src/app/preparation/preparation-sort-tools/preparation-sort-tools.component.spec.ts +++ b/src/app/preparation/preparation-sort-tools/preparation-sort-tools.component.spec.ts @@ -2,6 +2,8 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { IonicModule } from '@ionic/angular'; import { PreparationSortToolsComponent } from './preparation-sort-tools.component'; +import { TranslateModule } from '@ngx-translate/core'; +import { Preparation } from 'src/classes/preparation/preparation'; describe('PreparationSortToolsComponent', () => { let component: PreparationSortToolsComponent; @@ -10,13 +12,20 @@ describe('PreparationSortToolsComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ declarations: [PreparationSortToolsComponent], - imports: [IonicModule.forRoot()], + imports: [ + IonicModule.forRoot(), + TranslateModule.forChild(), + TranslateModule.forRoot(), + ], }).compileComponents(); + })); + beforeEach(() => { fixture = TestBed.createComponent(PreparationSortToolsComponent); component = fixture.componentInstance; + component.preparation = new Preparation(); fixture.detectChanges(); - })); + }); it('should create', () => { expect(component).toBeTruthy(); From 168f5ee1ee1297e954012d6e9c6836315862fe14 Mon Sep 17 00:00:00 2001 From: Juan Silveira Date: Sat, 24 Aug 2024 16:50:55 +0900 Subject: [PATCH 10/55] Fix BrewTimerComponent test. New use of Device. --- src/components/brew-timer/brew-timer.component.spec.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/brew-timer/brew-timer.component.spec.ts b/src/components/brew-timer/brew-timer.component.spec.ts index 1ec52629..f80d3373 100644 --- a/src/components/brew-timer/brew-timer.component.spec.ts +++ b/src/components/brew-timer/brew-timer.component.spec.ts @@ -6,6 +6,7 @@ import { ModalController } from '@ionic/angular'; import { CoffeeBluetoothDevicesService } from '../../services/coffeeBluetoothDevices/coffee-bluetooth-devices.service'; import { UISettingsStorage } from '../../services/uiSettingsStorage'; import { Settings } from '../../classes/settings/settings'; +import { Device } from '@awesome-cordova-plugins/device/ngx'; describe('BrewTimerComponent', () => { let component: BrewTimerComponent; @@ -32,6 +33,9 @@ describe('BrewTimerComponent', () => { }, }, }, + { + provide: Device, + }, ], }).compileComponents(); })); From 50589ad937b2a7dc9b804155a59b8ac30f94ee3a Mon Sep 17 00:00:00 2001 From: Juan Silveira Date: Sat, 24 Aug 2024 16:54:48 +0900 Subject: [PATCH 11/55] Fix BeanArchivePopoverComponent test. New use of ngModel, add FormsModule import. While here, replace TranslatePipe declaration and use of TranslateServiceMock with TranslateModule imports. --- .../bean-archive-popover.component.spec.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/app/beans/bean-archive-popover/bean-archive-popover.component.spec.ts b/src/app/beans/bean-archive-popover/bean-archive-popover.component.spec.ts index 6f2c425d..c2da9614 100644 --- a/src/app/beans/bean-archive-popover/bean-archive-popover.component.spec.ts +++ b/src/app/beans/bean-archive-popover/bean-archive-popover.component.spec.ts @@ -7,8 +7,8 @@ import { UIToast } from '../../../services/uiToast'; import { UISettingsStorage } from '../../../services/uiSettingsStorage'; import { Settings } from '../../../classes/settings/settings'; import { UIHelper } from '../../../services/uiHelper'; -import { TranslatePipe, TranslateService } from '@ngx-translate/core'; -import { TranslateServiceMock } from '../../../classes/mock'; +import { TranslateModule } from '@ngx-translate/core'; +import { FormsModule } from '@angular/forms'; describe('BeanArchivePopoverComponent', () => { let component: BeanArchivePopoverComponent; @@ -18,8 +18,13 @@ describe('BeanArchivePopoverComponent', () => { const settingsMock = {} as Settings; TestBed.configureTestingModule({ - declarations: [BeanArchivePopoverComponent, TranslatePipe], - imports: [IonicModule.forRoot()], + declarations: [BeanArchivePopoverComponent], + imports: [ + IonicModule.forRoot(), + TranslateModule.forRoot(), + TranslateModule.forChild(), + FormsModule, + ], providers: [ { provide: UIBeanStorage, @@ -43,10 +48,6 @@ describe('BeanArchivePopoverComponent', () => { toFixedIfNecessary: (_value, _dp): number => 0, } as UIHelper, }, - { - provide: TranslateService, - useValue: TranslateServiceMock, - }, ], }).compileComponents(); From 14938fee612f73cc630ee366ea2d44875755fb8a Mon Sep 17 00:00:00 2001 From: Juan Silveira Date: Sat, 24 Aug 2024 16:59:06 +0900 Subject: [PATCH 12/55] Fix BeanFreezeInformationComponent test. --- .../bean-freeze-information.component.spec.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/components/beans/bean-freeze-information/bean-freeze-information.component.spec.ts b/src/components/beans/bean-freeze-information/bean-freeze-information.component.spec.ts index 0ee01a54..90c415b3 100644 --- a/src/components/beans/bean-freeze-information/bean-freeze-information.component.spec.ts +++ b/src/components/beans/bean-freeze-information/bean-freeze-information.component.spec.ts @@ -2,6 +2,12 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { IonicModule } from '@ionic/angular'; import { BeanFreezeInformationComponent } from './bean-freeze-information.component'; +import { IonicStorageModule } from '@ionic/storage-angular'; +import { TranslateModule } from '@ngx-translate/core'; +import { UIHelper } from 'src/services/uiHelper'; +import { UIHelperMock } from 'src/classes/mock'; +import { PipesModule } from 'src/pipes/PipesModule'; +import { Bean } from 'src/classes/bean/bean'; describe('BeanFreezeInformationComponent', () => { let component: BeanFreezeInformationComponent; @@ -10,13 +16,22 @@ describe('BeanFreezeInformationComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ declarations: [BeanFreezeInformationComponent], - imports: [IonicModule.forRoot()], + imports: [ + IonicModule.forRoot(), + IonicStorageModule.forRoot(), + TranslateModule.forRoot(), + PipesModule, + ], + providers: [{ provide: UIHelper, useClass: UIHelperMock }], }).compileComponents(); + })); + beforeEach(() => { fixture = TestBed.createComponent(BeanFreezeInformationComponent); component = fixture.componentInstance; + component.data = new Bean(); fixture.detectChanges(); - })); + }); it('should create', () => { expect(component).toBeTruthy(); From e4f7f1ad56ea7572ce0616382bc53e8cb7527010 Mon Sep 17 00:00:00 2001 From: Juan Silveira Date: Sat, 24 Aug 2024 17:00:44 +0900 Subject: [PATCH 13/55] Fix TimerComponent test. New use of Device. --- src/components/timer/timer.component.spec.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/timer/timer.component.spec.ts b/src/components/timer/timer.component.spec.ts index 3657bdc7..2febeb6a 100644 --- a/src/components/timer/timer.component.spec.ts +++ b/src/components/timer/timer.component.spec.ts @@ -6,6 +6,7 @@ import { ModalController } from '@ionic/angular'; import { CoffeeBluetoothDevicesService } from '../../services/coffeeBluetoothDevices/coffee-bluetooth-devices.service'; import { UISettingsStorage } from '../../services/uiSettingsStorage'; import { Settings } from '../../classes/settings/settings'; +import { Device } from '@awesome-cordova-plugins/device/ngx'; describe('TimerComponent', () => { let component: TimerComponent; @@ -32,6 +33,9 @@ describe('TimerComponent', () => { }, }, }, + { + provide: Device, + }, ], }).compileComponents(); })); From c19b5eda126158f40aedeae4a52ddf9638c789bc Mon Sep 17 00:00:00 2001 From: Juan Silveira Date: Sat, 24 Aug 2024 17:02:17 +0900 Subject: [PATCH 14/55] Fix BeanPopoverActionsComponent test. New use of storage. While here, replace TranslatePipe declaration and use of TranslateServiceMock with TranslateModule imports. --- .../bean-popover-actions.component.spec.ts | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/app/beans/bean-popover-actions/bean-popover-actions.component.spec.ts b/src/app/beans/bean-popover-actions/bean-popover-actions.component.spec.ts index 6cddb6c5..4dab5c1e 100644 --- a/src/app/beans/bean-popover-actions/bean-popover-actions.component.spec.ts +++ b/src/app/beans/bean-popover-actions/bean-popover-actions.component.spec.ts @@ -5,8 +5,8 @@ import { BeanPopoverActionsComponent } from './bean-popover-actions.component'; import { UIHelper } from '../../../services/uiHelper'; import { UIBeanHelper } from '../../../services/uiBeanHelper'; import { IBean } from '../../../interfaces/bean/iBean'; -import { TranslatePipe, TranslateService } from '@ngx-translate/core'; -import { TranslateServiceMock } from '../../../classes/mock'; +import { IonicStorageModule } from '@ionic/storage-angular'; +import { TranslateModule } from '@ngx-translate/core'; describe('BeanPopoverActionsComponent', () => { let component: BeanPopoverActionsComponent; @@ -14,8 +14,13 @@ describe('BeanPopoverActionsComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - declarations: [BeanPopoverActionsComponent, TranslatePipe], - imports: [IonicModule.forRoot()], + declarations: [BeanPopoverActionsComponent], + imports: [ + IonicModule.forRoot(), + IonicStorageModule.forRoot(), + TranslateModule.forRoot(), + TranslateModule.forChild(), + ], providers: [ { provide: NavParams, @@ -39,17 +44,15 @@ describe('BeanPopoverActionsComponent', () => { provide: UIBeanHelper, useValue: {}, }, - { - provide: TranslateService, - useValue: TranslateServiceMock, - }, ], }).compileComponents(); + })); + beforeEach(() => { fixture = TestBed.createComponent(BeanPopoverActionsComponent); component = fixture.componentInstance; fixture.detectChanges(); - })); + }); it('should create', () => { expect(component).toBeTruthy(); From 71f4450c573d0a50acd2b475f4ca7097acfdb8f5 Mon Sep 17 00:00:00 2001 From: Juan Silveira Date: Sat, 24 Aug 2024 17:07:45 +0900 Subject: [PATCH 15/55] Fix for MeticulousHelpPopoverComponent test. --- .../meticulous-help-popover.component.spec.ts | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/popover/meticulous-help-popover/meticulous-help-popover.component.spec.ts b/src/popover/meticulous-help-popover/meticulous-help-popover.component.spec.ts index f4cd9045..dd6f7c19 100644 --- a/src/popover/meticulous-help-popover/meticulous-help-popover.component.spec.ts +++ b/src/popover/meticulous-help-popover/meticulous-help-popover.component.spec.ts @@ -2,21 +2,33 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { IonicModule } from '@ionic/angular'; import { MeticulousHelpPopoverComponent } from './meticulous-help-popover.component'; +import { IonicStorageModule } from '@ionic/storage-angular'; +import { TranslateModule } from '@ngx-translate/core'; +import { UIHelper } from 'src/services/uiHelper'; +import { UIHelperMock } from 'src/classes/mock'; -describe('MeticulousHelpPopoverComponent', () => { +describe('', () => { let component: MeticulousHelpPopoverComponent; let fixture: ComponentFixture; beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ declarations: [MeticulousHelpPopoverComponent], - imports: [IonicModule.forRoot()], + imports: [ + IonicModule.forRoot(), + IonicStorageModule.forRoot(), + // TranslateModule.forChild(), + TranslateModule.forRoot(), + ], + providers: [{ provide: UIHelper, useClass: UIHelperMock }], }).compileComponents(); + })); + beforeEach(() => { fixture = TestBed.createComponent(MeticulousHelpPopoverComponent); component = fixture.componentInstance; fixture.detectChanges(); - })); + }); it('should create', () => { expect(component).toBeTruthy(); From 2172b1aa022612b4ecc3e4bdaa7f4f41506360bf Mon Sep 17 00:00:00 2001 From: Juan Silveira Date: Sat, 24 Aug 2024 19:14:27 +0900 Subject: [PATCH 16/55] Fix flaky BrewBrewingGraphComponent test. During the tests, `this.settings` is not set. This was causing a failure in onDestroy and prevent the component from being cleaned up properly. This resulted in random failures in unrelated tests with the error: ``` Uncaught Error: Uncaught (in promise): TypeError: _this5.brewComponent.timer?.isTimerRunning is not a function TypeError: _this5.brewComponent.timer?.isTimerRunning is not a function at http://localhost:9876/_karma_webpack_/webpack:/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts:1319:35 ``` --- .../brews/brew-brewing-graph/brew-brewing-graph.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts b/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts index cd4f5096..7aa9f234 100644 --- a/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts +++ b/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts @@ -2941,7 +2941,7 @@ export class BrewBrewingGraphComponent implements OnInit { this.stopFetchingAndSettingDataFromXenia(); this.stopFetchingDataFromMeticulous(); - if (this.settings.text_to_speech_active) { + if (this.settings?.text_to_speech_active) { this.textToSpeech.end(); } } From 92464758f48c2470ca32a026cb8966d5291ab4f9 Mon Sep 17 00:00:00 2001 From: Juan Silveira Date: Sat, 24 Aug 2024 19:42:30 +0900 Subject: [PATCH 17/55] Fix PipesModule imports broken after rename. --- .../bean-popover-freeze/bean-popover-freeze.component.spec.ts | 2 +- .../bean-freeze-information.component.spec.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/beans/bean-popover-freeze/bean-popover-freeze.component.spec.ts b/src/app/beans/bean-popover-freeze/bean-popover-freeze.component.spec.ts index 55aeb490..b9887205 100644 --- a/src/app/beans/bean-popover-freeze/bean-popover-freeze.component.spec.ts +++ b/src/app/beans/bean-popover-freeze/bean-popover-freeze.component.spec.ts @@ -6,7 +6,7 @@ import { IonicStorageModule } from '@ionic/storage-angular'; import { TranslateModule } from '@ngx-translate/core'; import { UIHelper } from 'src/services/uiHelper'; import { UIHelperMock } from 'src/classes/mock'; -import { PipesModule } from 'src/pipes/PipesModule'; +import { PipesModule } from 'src/pipes/pipes.module'; import { Bean } from 'src/classes/bean/bean'; describe('BeanPopoverFreezeComponent', () => { diff --git a/src/components/beans/bean-freeze-information/bean-freeze-information.component.spec.ts b/src/components/beans/bean-freeze-information/bean-freeze-information.component.spec.ts index 90c415b3..6b5b1621 100644 --- a/src/components/beans/bean-freeze-information/bean-freeze-information.component.spec.ts +++ b/src/components/beans/bean-freeze-information/bean-freeze-information.component.spec.ts @@ -6,7 +6,7 @@ import { IonicStorageModule } from '@ionic/storage-angular'; import { TranslateModule } from '@ngx-translate/core'; import { UIHelper } from 'src/services/uiHelper'; import { UIHelperMock } from 'src/classes/mock'; -import { PipesModule } from 'src/pipes/PipesModule'; +import { PipesModule } from 'src/pipes/pipes.module'; import { Bean } from 'src/classes/bean/bean'; describe('BeanFreezeInformationComponent', () => { From e90406e46deb441c4a0e81945933b5bcca3f7f1e Mon Sep 17 00:00:00 2001 From: Juan Silveira Date: Thu, 27 Jun 2024 19:28:42 +0900 Subject: [PATCH 18/55] Create node.js.yml for npm test CI. It checks out the code, sets up node, builds and runs the tests. Run tests in ChromeHeadless and do a single run. --- .github/workflows/node.js.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/node.js.yml diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml new file mode 100644 index 00000000..8552fed9 --- /dev/null +++ b/.github/workflows/node.js.yml @@ -0,0 +1,26 @@ +# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs + +name: npm test CI + +on: + push: + branches: [ "master", "develop" ] + pull_request: + branches: [ "master", "develop" ] + workflow_dispatch: + +jobs: + build-and-test: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 22.x + cache: 'npm' + - run: npm ci + - run: npm run build --if-present + - run: npm test -- --browsers=ChromeHeadless --watch=false From a44424e19e8d019ee4997838058601458700f6e0 Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Tue, 27 Aug 2024 20:56:23 +0200 Subject: [PATCH 19/55] #767 - Frozen beans sort order is now saved #765 - Show popup if data base is corrupted #762 - Read a history shot of the meticulous to import #768 - Add ean search article number #769 - Make brew/bean toggleable #438 - Import beans via excel #772 - Display best brew / favorite / frozen etc. #773 - Show frozen icon for beans on brew-list #775 - Fixing wrong labels #776 - Sorting bean for bean age #629 - Import green beans via excel #777 - grow plausible var from 5 to 6 #778 - Showing bean images on selection #623 - Showing a popup that more informations are there but not visible. --- config.xml | 2 +- package-lock.json | 52 +- package.json | 23 +- src/app/app.component.ts | 4 + src/app/app.scss | 1 + .../bean-modal-select.component.html | 32 +- .../bean-modal-select.component.ts | 3 +- .../bean-popover-freeze.component.html | 4 +- .../bean-popover-freeze.component.ts | 45 +- .../bean-popover-frozen-list.component.ts | 2 - .../bean-popover-list.component.html | 19 + .../bean-popover-list.component.scss | 0 .../bean-popover-list.component.spec.ts | 32 + .../bean-popover-list.component.ts | 65 ++ .../beans/bean-sort/bean-sort.component.html | 4 + .../beans/beans-add/beans-add.component.ts | 79 +- src/app/beans/beans.page.html | 24 +- src/app/beans/beans.page.ts | 57 +- src/app/brew/brew-add/brew-add.component.html | 10 + ...-choose-preparation-to-brew.component.html | 3 +- .../brew/brew-edit/brew-edit.component.html | 10 + ...odal-import-shot-meticulous.component.html | 26 +- ...l-import-shot-meticulous.component.spec.ts | 19 +- ...-modal-import-shot-meticulous.component.ts | 123 ++- src/app/brew/brew.page.html | 18 +- src/app/brew/brew.page.ts | 39 +- .../preparation-add-type.component.ts | 11 +- .../preparation-add.component.html | 39 +- .../preparation-add.component.ts | 13 + ...reparation-connected-device.component.html | 61 +- .../preparation-connected-device.component.ts | 53 +- .../preparation-modal-select.component.html | 12 +- src/app/settings/settings.page.html | 12 +- src/app/settings/settings.page.ts | 23 +- src/app/shared/shared.module.ts | 6 + .../beanconqueror-expand-active.svg | 1 + .../beanconqueror-expand-inactive.svg | 1 + .../beanconqueror-sanremo-you-logo.svg | 35 + src/assets/i18n/en.json | 24 +- src/classes/bean/bean.ts | 7 + src/classes/devices/argosThermometer.ts | 72 ++ src/classes/devices/index.ts | 4 + src/classes/preparation/preparation.ts | 12 + src/classes/preparationDevice/index.ts | 4 + .../meticulous/meticulousDevice.ts | 114 ++- .../sanremo/sanremoYOUDevice.ts | 187 ++++ .../preparationDevice/xenia/xeniaDevice.ts | 1 + src/classes/settings/settings.ts | 44 +- .../bean-information.component.html | 7 +- .../bean-information.component.ts | 4 +- .../bean-freeze-information.component.html | 4 +- .../brew-information.component.html | 493 ++++++----- .../brew-information.component.ts | 4 +- .../brew-brewing-graph.component.ts | 815 +++++++++++------- ...-brewing-preparation-device.component.html | 9 + ...ew-brewing-preparation-device.component.ts | 106 ++- .../graph-display-card.component.ts | 8 +- .../photo-add/photo-add.component.ts | 10 +- .../preparation-information-card.component.ts | 12 +- src/enums/beans/beanSortAfter.ts | 4 +- src/enums/preparations/preparationTypes.ts | 4 +- src/environments/environment.prod.ts | 5 +- src/environments/environment.ts | 3 + .../sanremoYOU/iSanremoYOUParams.ts | 5 + src/interfaces/settings/iSettings.ts | 10 + .../data-corruption-found.component.html | 69 ++ .../data-corruption-found.component.scss | 6 + .../data-corruption-found.component.spec.ts | 24 + .../data-corruption-found.component.ts | 68 ++ .../coffee-bluetooth-devices.service.ts | 25 +- src/services/uiBeanHelper.ts | 12 + src/services/uiExcel.ts | 504 ++++++++++- src/services/uiExportImportHelper.ts | 83 +- src/services/uiFileHelper.ts | 9 +- src/services/uiHelper.ts | 10 + src/services/uiPreparationHelper.ts | 25 +- src/services/uiUpdate.ts | 9 + 77 files changed, 2933 insertions(+), 846 deletions(-) create mode 100644 src/app/beans/bean-popover-list/bean-popover-list.component.html create mode 100644 src/app/beans/bean-popover-list/bean-popover-list.component.scss create mode 100644 src/app/beans/bean-popover-list/bean-popover-list.component.spec.ts create mode 100644 src/app/beans/bean-popover-list/bean-popover-list.component.ts create mode 100644 src/assets/custom-ion-icons/beanconqueror-expand-active.svg create mode 100644 src/assets/custom-ion-icons/beanconqueror-expand-inactive.svg create mode 100644 src/assets/custom-ion-icons/beanconqueror-sanremo-you-logo.svg create mode 100644 src/classes/devices/argosThermometer.ts create mode 100644 src/classes/preparationDevice/sanremo/sanremoYOUDevice.ts create mode 100644 src/interfaces/preparationDevices/sanremoYOU/iSanremoYOUParams.ts create mode 100644 src/popover/data-corruption-found/data-corruption-found.component.html create mode 100644 src/popover/data-corruption-found/data-corruption-found.component.scss create mode 100644 src/popover/data-corruption-found/data-corruption-found.component.spec.ts create mode 100644 src/popover/data-corruption-found/data-corruption-found.component.ts diff --git a/config.xml b/config.xml index ce93948c..cbc79bf9 100644 --- a/config.xml +++ b/config.xml @@ -1,5 +1,5 @@ - + Beanconqueror Lars Saalbach diff --git a/package-lock.json b/package-lock.json index 280f8555..7ba5d102 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,6 +40,8 @@ "@ionic/cordova-builders": "^8.0.0", "@ionic/storage": "^4.0.0", "@ionic/storage-angular": "^4.0.0", + "@meticulous-home/espresso-api": "^0.3.1", + "@meticulous-home/espresso-profile": "^0.4.0", "@ngx-translate/core": "^15.0.0", "@ngx-translate/http-loader": "^4.0.0", "@zip.js/zip.js": "^2.7.14", @@ -61,7 +63,6 @@ "lodash": "^4.17.21", "long": "^5.2.0", "luxon": "^2.3.0", - "meticulous-api": "github:FFFuego/meticulous-typescript-api#dist", "moment": "^2.29.4", "ngx-stars": "^1.6.4", "postcss": "^8.3.5", @@ -91,7 +92,7 @@ "@wisdomgarden/cordova-plugin-filepath": "git+https://github.com/wisdom-garden/cordova-plugin-filepath.git", "cordova-android": "^13.0.0", "cordova-clipboard": "^1.3.0", - "cordova-ios": "^7.1.0", + "cordova-ios": "^7.1.1", "cordova-plugin-add-swift-support": "^2.0.2", "cordova-plugin-advanced-http": "^3.3.1", "cordova-plugin-android-permissions": "^1.1.5", @@ -6294,6 +6295,23 @@ "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" }, + "node_modules/@meticulous-home/espresso-api": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@meticulous-home/espresso-api/-/espresso-api-0.3.1.tgz", + "integrity": "sha512-KeZuK0RDDc9HhMNxyYj0BRfEgGvNOUKu5zyn8RYI4ezUCnn61PP4FbykiB7YxzBvsr8PT+5BOPyXSvqkDaC1FA==", + "license": "GPLv3", + "dependencies": { + "axios": "^1.6.8", + "meticulous-typescript-profile": "github:MeticulousHome/meticulous-typescript-profile#dist", + "socket.io-client": "^4.7.5" + } + }, + "node_modules/@meticulous-home/espresso-profile": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@meticulous-home/espresso-profile/-/espresso-profile-0.4.0.tgz", + "integrity": "sha512-jHG2y6F6mi5YTFE0nmyc5NKUGmyDxorLiix1h8CB5ch/Plae+fvD7RsxetA6nh3lAn8Zfz9U9am2kolokY0Zyg==", + "license": "GPL-3.0" + }, "node_modules/@netflix/nerror": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@netflix/nerror/-/nerror-1.1.3.tgz", @@ -9262,10 +9280,11 @@ } }, "node_modules/cordova-ios": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/cordova-ios/-/cordova-ios-7.1.0.tgz", - "integrity": "sha512-9/vPU+GWRdfxNIkAc9Gq6yejMIgpy59ycP8WyVJJ7HfuPSTBUQT8AS2h5ZJOeN4Y/URyEwxQCrAqSADDBVCESA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/cordova-ios/-/cordova-ios-7.1.1.tgz", + "integrity": "sha512-JwTyPxWcAZlbIOR5QO6TaJzkoSzfrp7jrlX01bWZ7Sxp0PYXejAJbA6J0W4u11M+atrQRNimNltZDyAlSBW2tw==", "dev": true, + "license": "Apache-2.0", "dependencies": { "cordova-common": "^5.0.0", "elementtree": "^0.1.7", @@ -9286,6 +9305,7 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", "dev": true, + "license": "ISC", "engines": { "node": ">=16" } @@ -9295,6 +9315,7 @@ "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", "dev": true, + "license": "ISC", "dependencies": { "isexe": "^3.1.1" }, @@ -12750,6 +12771,7 @@ "resolved": "https://registry.npmjs.org/ios-sim/-/ios-sim-8.0.2.tgz", "integrity": "sha512-P7nEG771bfd+JoMRjnis1gpZOkjTUUxu+4Ek1Z+eoaEEoT9byllU9pxfQ8Df7hL3gSkIQxNwTSLhos2I8tWUQA==", "dev": true, + "license": "MIT", "dependencies": { "bplist-parser": "^0.0.6", "nopt": "1.0.9", @@ -12767,13 +12789,15 @@ "version": "0.0.6", "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.0.6.tgz", "integrity": "sha512-fGeghPEH4Eytvf+Mi446aKcDqvkA/+eh6r7QGiZWMQG6TzqrnsToLP379XFfqRSZ41+676hhGIm++maNST1Apw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/ios-sim/node_modules/nopt": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.9.tgz", "integrity": "sha512-CmUZ3rzN0/4kRHum5pGRiGkhmBMzgtEDxrZVHqRJDSv8qK6s+wzaig/xeyB22Due5aZQeTiEZg/nrmMH2tapDQ==", "dev": true, + "license": "MIT", "dependencies": { "abbrev": "1" }, @@ -15075,16 +15099,6 @@ "node": ">= 0.6" } }, - "node_modules/meticulous-api": { - "version": "0.0.2", - "resolved": "git+ssh://git@github.com/FFFuego/meticulous-typescript-api.git#c1ae8038e22db7ff9547f88038b9566b4f1f9718", - "license": "GPLv3", - "dependencies": { - "axios": "^1.6.8", - "meticulous-typescript-profile": "github:MeticulousHome/meticulous-typescript-profile#dist", - "socket.io-client": "^4.7.5" - } - }, "node_modules/meticulous-typescript-profile": { "version": "0.1.0", "resolved": "git+ssh://git@github.com/MeticulousHome/meticulous-typescript-profile.git#c4520f2238770d8432272e2fbc5d29f90a78720d", @@ -18915,6 +18929,7 @@ "resolved": "https://registry.npmjs.org/simctl/-/simctl-2.0.3.tgz", "integrity": "sha512-kKCak0yszxHae5eVWcmrjV3ouUGac3sjlhjdLWpyPu4eiQcWoHsCrqS34kkgzHN8Ystqkh/LFjzrldk/g3BYJg==", "dev": true, + "license": "MIT", "dependencies": { "shelljs": "^0.8.5", "tail": "^0.4.0" @@ -21824,6 +21839,7 @@ "resolved": "https://registry.npmjs.org/xcode/-/xcode-3.0.1.tgz", "integrity": "sha512-kCz5k7J7XbJtjABOvkc5lJmkiDh8VhjVCGNiqdKCscmVpdVUpEAyXv1xmCLkQJ5dsHqx3IPO4XW+NTDhU/fatA==", "dev": true, + "license": "Apache-2.0", "dependencies": { "simple-plist": "^1.1.0", "uuid": "^7.0.3" @@ -21837,6 +21853,7 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==", "dev": true, + "license": "MIT", "bin": { "uuid": "dist/bin/uuid" } @@ -21865,7 +21882,8 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/xml-escape/-/xml-escape-1.1.0.tgz", "integrity": "sha512-B/T4sDK8Z6aUh/qNr7mjKAwwncIljFuUP+DO/D5hloYFj+90O88z8Wf7oSucZTHxBAsC1/CTP4rtx/x1Uf72Mg==", - "dev": true + "dev": true, + "license": "MIT License" }, "node_modules/xml-name-validator": { "version": "3.0.0", diff --git a/package.json b/package.json index 316b14af..457cd34b 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,8 @@ "@ionic/cordova-builders": "^8.0.0", "@ionic/storage": "^4.0.0", "@ionic/storage-angular": "^4.0.0", + "@meticulous-home/espresso-api": "^0.3.1", + "@meticulous-home/espresso-profile": "^0.4.0", "@ngx-translate/core": "^15.0.0", "@ngx-translate/http-loader": "^4.0.0", "@zip.js/zip.js": "^2.7.14", @@ -68,7 +70,6 @@ "lodash": "^4.17.21", "long": "^5.2.0", "luxon": "^2.3.0", - "meticulous-api": "github:FFFuego/meticulous-typescript-api#dist", "moment": "^2.29.4", "ngx-stars": "^1.6.4", "postcss": "^8.3.5", @@ -98,7 +99,7 @@ "@wisdomgarden/cordova-plugin-filepath": "git+https://github.com/wisdom-garden/cordova-plugin-filepath.git", "cordova-android": "^13.0.0", "cordova-clipboard": "^1.3.0", - "cordova-ios": "^7.1.0", + "cordova-ios": "^7.1.1", "cordova-plugin-add-swift-support": "^2.0.2", "cordova-plugin-advanced-http": "^3.3.1", "cordova-plugin-android-permissions": "^1.1.5", @@ -210,22 +211,22 @@ "cordova-plugin-nativestorage": {}, "cordova-plugin-androidx": {}, "cordova-plugin-androidx-adapter": {}, - "cordova-plugin-ble-central": { - "BLUETOOTH_USAGE_DESCRIPTION": "Bluetooth access needed to connect smartscales", - "IOS_INIT_ON_LOAD": "false", - "ACCESS_BACKGROUND_LOCATION": "false", - "BLUETOOTH_RESTORE_STATE": "false" - }, "cordova-plugin-inappbrowser": {}, "skwas-cordova-plugin-datetimepicker": {}, "cordova-plugin-statusbar": {}, "cordova-plugin-file": { "ANDROIDX_WEBKIT_VERSION": "1.4.0" + }, + "cordova-plugin-ble-central": { + "BLUETOOTH_USAGE_DESCRIPTION": "Bluetooth access needed to connect smartscales", + "IOS_INIT_ON_LOAD": "false", + "ACCESS_BACKGROUND_LOCATION": "false", + "BLUETOOTH_RESTORE_STATE": "false" } }, "platforms": [ - "ios", - "android" + "android", + "ios" ] }, "platforms": [ @@ -240,4 +241,4 @@ "*.css": "stylelint --fix", "*.{ts,js,css,md}": "prettier --write" } -} +} \ No newline at end of file diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 5c9e5762..891080a4 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -324,6 +324,7 @@ export class AppComponent implements AfterViewInit { }); } catch (ex) {} + // Okay, so the platform is ready and our plugins are available. // Here you can do any higher level native things you might need. // #7 @@ -366,6 +367,7 @@ export class AppComponent implements AfterViewInit { }); } + if (this.platform.is('cordova')) { // Just support deeplinks on devices. this.intentHandlerService.attachOnHandleOpenUrl(); @@ -374,6 +376,8 @@ export class AppComponent implements AfterViewInit { this._translate.setDefaultLang('en'); await this._translate.use('en').toPromise(); + + if (this.platform.is('cordova')) { try { await this.uiExportImportHelper.checkBackup(); diff --git a/src/app/app.scss b/src/app/app.scss index 5888a116..a2c6f01c 100644 --- a/src/app/app.scss +++ b/src/app/app.scss @@ -739,6 +739,7 @@ ion-menu:host .menu-inner { font-weight: bold; } + .button-top-absolute { position: absolute; top: -20px; diff --git a/src/app/beans/bean-modal-select/bean-modal-select.component.html b/src/app/beans/bean-modal-select/bean-modal-select.component.html index 28499557..45fa1b39 100644 --- a/src/app/beans/bean-modal-select/bean-modal-select.component.html +++ b/src/app/beans/bean-modal-select/bean-modal-select.component.html @@ -101,6 +101,11 @@
+
+ +
+
@@ -178,6 +183,11 @@
+
+ +
+ @@ -260,6 +270,11 @@
( +
+ +
+
@@ -348,7 +363,11 @@
( +
+ +
+
@@ -430,6 +449,11 @@
+
+ +
+
@@ -511,7 +535,11 @@
( +
+ +
+
diff --git a/src/app/beans/bean-modal-select/bean-modal-select.component.ts b/src/app/beans/bean-modal-select/bean-modal-select.component.ts index a862ad99..f04058ae 100644 --- a/src/app/beans/bean-modal-select/bean-modal-select.component.ts +++ b/src/app/beans/bean-modal-select/bean-modal-select.component.ts @@ -409,7 +409,8 @@ export class BeanModalSelectComponent implements OnInit { e.name?.toLowerCase().includes(searchText) || e.roaster?.toLowerCase().includes(searchText) || e.aromatics?.toLowerCase().includes(searchText) || - e.frozenId?.toLowerCase().includes(searchText) + e.frozenId?.toLowerCase().includes(searchText) || + e.ean_article_number?.toLowerCase().includes(searchText) ); } if (_type === 'open') { diff --git a/src/app/beans/bean-popover-freeze/bean-popover-freeze.component.html b/src/app/beans/bean-popover-freeze/bean-popover-freeze.component.html index 3d977ecb..773cd6f8 100644 --- a/src/app/beans/bean-popover-freeze/bean-popover-freeze.component.html +++ b/src/app/beans/bean-popover-freeze/bean-popover-freeze.component.html @@ -43,11 +43,11 @@ prevent-characters remove-empty-number spellcheck="false" type="text" tabIndex="2"> - - diff --git a/src/app/beans/bean-popover-freeze/bean-popover-freeze.component.ts b/src/app/beans/bean-popover-freeze/bean-popover-freeze.component.ts index d7a006e2..9b429b3a 100644 --- a/src/app/beans/bean-popover-freeze/bean-popover-freeze.component.ts +++ b/src/app/beans/bean-popover-freeze/bean-popover-freeze.component.ts @@ -59,7 +59,7 @@ export class BeanPopoverFreezeComponent implements OnInit { public ngOnInit() { // cant be done in constructor, else the bean object is not known - this.leftOverBeanBagWeight = this.bean.weight - this.getUsedWeightCount(); + this.leftOverBeanBagWeight = this.uiHelper.toFixedIfNecessary(this.bean.weight - this.getUsedWeightCount(),1); } public getUsedWeightCount(): number { @@ -87,7 +87,7 @@ export class BeanPopoverFreezeComponent implements OnInit { public async save() { const spillOver = - this.leftOverBeanBagWeight - this.getActualFreezingQuantity(); + this.uiHelper.toFixedIfNecessary(this.leftOverBeanBagWeight - this.getActualFreezingQuantity(),1); let index = 1; @@ -109,10 +109,10 @@ export class BeanPopoverFreezeComponent implements OnInit { this.bean.config.uuid ); if (brews.length > 0) { - const oldWeight = this.bean.weight; - this.bean.weight = this.bean.weight - this.getActualFreezingQuantity(); + const oldWeight = this.uiHelper.toFixedIfNecessary(this.bean.weight,1) + this.bean.weight = this.uiHelper.toFixedIfNecessary(this.bean.weight - this.getActualFreezingQuantity(),1); try { - const newCost = (this.bean.cost * this.bean.weight) / oldWeight; + const newCost = this.uiHelper.toFixedIfNecessary((this.bean.cost * this.bean.weight) / oldWeight,2) this.bean.cost = newCost; } catch (ex) { this.bean.cost = 0; @@ -148,10 +148,10 @@ export class BeanPopoverFreezeComponent implements OnInit { /** * Because we had already maybe some brews, we take the bean weight, and subtract it with the spill over, because just using the spill over would not take previus brews into account */ - const oldWeight = this.bean.weight; - this.bean.weight = this.bean.weight - this.getActualFreezingQuantity(); + const oldWeight = this.uiHelper.toFixedIfNecessary(this.bean.weight,1); + this.bean.weight = this.uiHelper.toFixedIfNecessary(this.bean.weight - this.getActualFreezingQuantity(),1); try { - const newCost = (this.bean.cost * this.bean.weight) / oldWeight; + const newCost = this.uiHelper.toFixedIfNecessary((this.bean.cost * this.bean.weight) / oldWeight,2); this.bean.cost = newCost; } catch (ex) { this.bean.cost = 0; @@ -189,13 +189,13 @@ export class BeanPopoverFreezeComponent implements OnInit { if (this.bean.cost !== 0) { try { - const newCost = (this.bean.cost * _freezingWeight) / this.bean.weight; + const newCost = this.uiHelper.toFixedIfNecessary((this.bean.cost * _freezingWeight) / this.bean.weight,2); clonedBean.cost = newCost; } catch (ex) { clonedBean.cost = 0; } } - clonedBean.weight = _freezingWeight; + clonedBean.weight = this.uiHelper.toFixedIfNecessary(_freezingWeight,1); clonedBean.config = new Config(); const newClonedBean = await this.uiBeanStorage.add(clonedBean); const newBean: Bean = new Bean(); @@ -244,18 +244,29 @@ export class BeanPopoverFreezeComponent implements OnInit { return quantity; } + public isAddingBagDisabled() { + + if (this.freezePartialBagGrams <=0) { + return true; + } + if (this.uiHelper.toFixedIfNecessary(Number(this.freezePartialBagGrams) + this.getActualFreezingQuantity(),1) > this.leftOverBeanBagWeight) { + return true; + } + return false; + } + public addOnePartialBag() { this.addedBags.push({ - weight: this.freezePartialBagGrams, + weight: Number(this.freezePartialBagGrams), type: this.frozenStorage, }); const leftFreezingCount = - this.leftOverBeanBagWeight - this.getActualFreezingQuantity(); + this.uiHelper.toFixedIfNecessary(this.leftOverBeanBagWeight - this.getActualFreezingQuantity(),1); if (leftFreezingCount < this.freezePartialBagGrams) { this.freezePartialBagGrams = this.uiHelper.toFixedIfNecessary( leftFreezingCount, - 2 + 1 ); } } @@ -263,16 +274,16 @@ export class BeanPopoverFreezeComponent implements OnInit { public addMaxPartialBags() { while (true) { this.addedBags.push({ - weight: this.freezePartialBagGrams, + weight: Number(this.freezePartialBagGrams), type: this.frozenStorage, }); const leftFreezingCount = - this.leftOverBeanBagWeight - this.getActualFreezingQuantity(); + this.uiHelper.toFixedIfNecessary(this.leftOverBeanBagWeight - this.getActualFreezingQuantity(),1); if (leftFreezingCount < this.freezePartialBagGrams) { this.freezePartialBagGrams = this.uiHelper.toFixedIfNecessary( leftFreezingCount, - 2 + 1 ); break; } @@ -282,7 +293,7 @@ export class BeanPopoverFreezeComponent implements OnInit { public deleteBag(_index) { this.addedBags.splice(_index, 1); const leftFreezingCount = - this.leftOverBeanBagWeight - this.getActualFreezingQuantity(); + this.uiHelper.toFixedIfNecessary(this.leftOverBeanBagWeight - this.getActualFreezingQuantity(),1); if (leftFreezingCount < this.freezePartialBagGrams) { this.freezePartialBagGrams = leftFreezingCount; } diff --git a/src/app/beans/bean-popover-frozen-list/bean-popover-frozen-list.component.ts b/src/app/beans/bean-popover-frozen-list/bean-popover-frozen-list.component.ts index 29c022e9..ebd9e99e 100644 --- a/src/app/beans/bean-popover-frozen-list/bean-popover-frozen-list.component.ts +++ b/src/app/beans/bean-popover-frozen-list/bean-popover-frozen-list.component.ts @@ -7,11 +7,9 @@ import { ViewChild, } from '@angular/core'; import { Bean } from '../../../classes/bean/bean'; -import { Brew } from '../../../classes/brew/brew'; import { AgVirtualSrollComponent } from 'ag-virtual-scroll'; import { ModalController } from '@ionic/angular'; import { UIBeanHelper } from '../../../services/uiBeanHelper'; -import { UIBrewHelper } from '../../../services/uiBrewHelper'; @Component({ selector: 'app-bean-popover-frozen-list', diff --git a/src/app/beans/bean-popover-list/bean-popover-list.component.html b/src/app/beans/bean-popover-list/bean-popover-list.component.html new file mode 100644 index 00000000..08d289c2 --- /dev/null +++ b/src/app/beans/bean-popover-list/bean-popover-list.component.html @@ -0,0 +1,19 @@ + + + {{"BEAN_LIST" | translate}} + + + + + + + + + + + + + + + diff --git a/src/app/beans/bean-popover-list/bean-popover-list.component.scss b/src/app/beans/bean-popover-list/bean-popover-list.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/app/beans/bean-popover-list/bean-popover-list.component.spec.ts b/src/app/beans/bean-popover-list/bean-popover-list.component.spec.ts new file mode 100644 index 00000000..34d578e6 --- /dev/null +++ b/src/app/beans/bean-popover-list/bean-popover-list.component.spec.ts @@ -0,0 +1,32 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { IonicModule } from '@ionic/angular'; + +import { BeanPopoverListComponent } from './bean-popover-list.component'; +import { TranslateModule, TranslateService } from '@ngx-translate/core'; +import { TranslateServiceMock } from '../../../classes/mock'; + +describe('BeanPopoverListComponent', () => { + let component: BeanPopoverListComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [ BeanPopoverListComponent ], + imports: [IonicModule.forRoot(), TranslateModule.forRoot()], + providers: [ + { + provide: TranslateService, + useValue: TranslateServiceMock, + }, + ] + }).compileComponents(); + + fixture = TestBed.createComponent(BeanPopoverListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + })); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/beans/bean-popover-list/bean-popover-list.component.ts b/src/app/beans/bean-popover-list/bean-popover-list.component.ts new file mode 100644 index 00000000..6ae0948d --- /dev/null +++ b/src/app/beans/bean-popover-list/bean-popover-list.component.ts @@ -0,0 +1,65 @@ +import { Component, ElementRef, HostListener, Input, OnInit, ViewChild } from '@angular/core'; +import { Bean } from '../../../classes/bean/bean'; +import { AgVirtualSrollComponent } from 'ag-virtual-scroll'; +import { ModalController } from '@ionic/angular'; + +@Component({ + selector: 'app-bean-popover-list', + templateUrl: './bean-popover-list.component.html', + styleUrls: ['./bean-popover-list.component.scss'], +}) +export class BeanPopoverListComponent { + + public static readonly COMPONENT_ID = 'bean-popover-list'; + + @Input() public beansList: Array = undefined; + + @ViewChild('openScroll', { read: AgVirtualSrollComponent, static: false }) + public openScroll: AgVirtualSrollComponent; + + @ViewChild('beanContent', { read: ElementRef }) + public beanContent: ElementRef; + + constructor( + private readonly modalController: ModalController, + ) {} + + public async ionViewWillEnter() { + this.loadBrews(); + } + + @HostListener('window:resize') + @HostListener('window:orientationchange', ['$event']) + public onOrientationChange(_event: any) { + this.retriggerScroll(); + } + + private retriggerScroll() { + setTimeout(async () => { + const el = this.beanContent.nativeElement; + let scrollComponent: AgVirtualSrollComponent; + scrollComponent = this.openScroll; + scrollComponent.el.style.height = + el.offsetHeight - scrollComponent.el.offsetTop + 'px'; + }, 150); + } + + public loadBrews() { + this.retriggerScroll(); + } + + public async brewAction(): Promise { + this.loadBrews(); + } + + public dismiss(): void { + this.modalController.dismiss( + { + dismissed: true, + }, + undefined, + BeanPopoverListComponent.COMPONENT_ID + ); + } + +} diff --git a/src/app/beans/bean-sort/bean-sort.component.html b/src/app/beans/bean-sort/bean-sort.component.html index a5db09b4..d7c7ed4d 100644 --- a/src/app/beans/bean-sort/bean-sort.component.html +++ b/src/app/beans/bean-sort/bean-sort.component.html @@ -43,6 +43,10 @@ {{"BREW_DATA_RATING" | translate}} + + + {{"BEAN_SORT_BEAN_AGE" | translate}} + diff --git a/src/app/beans/beans-add/beans-add.component.ts b/src/app/beans/beans-add/beans-add.component.ts index 83094636..fbad06f2 100644 --- a/src/app/beans/beans-add/beans-add.component.ts +++ b/src/app/beans/beans-add/beans-add.component.ts @@ -20,7 +20,7 @@ import { Settings } from '../../../classes/settings/settings'; @Component({ selector: 'beans-add', templateUrl: './beans-add.component.html', - styleUrls: ['./beans-add.component.scss'], + styleUrls: ['./beans-add.component.scss'] }) export class BeansAddComponent implements OnInit { public static readonly COMPONENT_ID = 'bean-add'; @@ -49,7 +49,8 @@ export class BeansAddComponent implements OnInit { private readonly platform: Platform, public readonly uiBeanHelper: UIBeanHelper, private readonly uiSettingsStorage: UISettingsStorage - ) {} + ) { + } public ngOnInit() { this.settings = this.uiSettingsStorage.getSettings(); @@ -64,6 +65,71 @@ export class BeansAddComponent implements OnInit { } } + private async checkIfInformationAreSetButNotDisplayed() { + try { + + + const params = this.settings.bean_manage_parameters; + if (this.data.bean_information.length > 0) { + const info: IBeanInformation = this.data.bean_information[0]; + let hasDataSet: boolean = false; + if (info.country && info.country !== '') { + hasDataSet = true; + } + if (info.region && info.region !== '') { + hasDataSet = true; + } + if (info.farm && info.farm !== '') { + hasDataSet = true; + } + if (info.farmer && info.farmer !== '') { + hasDataSet = true; + } + if (info.elevation && info.elevation !== '') { + hasDataSet = true; + } + if (info.harvest_time && info.harvest_time !== '') { + hasDataSet = true; + } + if (info.variety && info.variety !== '') { + hasDataSet = true; + } + if (info.processing && info.processing !== '') { + hasDataSet = true; + } + if (info.certification && info.certification !== '') { + hasDataSet = true; + } + if (info.percentage && info.percentage > 0) { + hasDataSet = true; + } + if (info.purchasing_price && info.purchasing_price > 0) { + hasDataSet = true; + } + if (info.fob_price && info.fob_price > 0) { + hasDataSet = true; + } + + if (params.bean_information === false && hasDataSet === true) { + //Woopsi doopsi, user hasn't enabled the bean_information, lets display him a popover + //#623 + try { + const yes = await this.uiAlert.showConfirm('BEAN_POPUP_YOU_DONT_SEE_EVERYTHING_DESCRIPTION', 'INFORMATION', true); + this.settings.bean_manage_parameters.bean_information = true; + await this.uiSettingsStorage.update(this.settings); + //Activate + } catch (ex) { + // Don't activate + } + + } + + } + } catch (ex) { + + } + } + public async ionViewWillEnter() { this.uiAnalytics.trackEvent(BEAN_TRACKING.TITLE, BEAN_TRACKING.ACTIONS.ADD); @@ -71,6 +137,7 @@ export class BeansAddComponent implements OnInit { // TODO how to handle roasting beans which wil be repeated? if (this.bean_template) { await this.__loadBean(this.bean_template); + await this.checkIfInformationAreSetButNotDisplayed(); } // Download images after loading the bean, else they would be copied :O @@ -161,10 +228,11 @@ export class BeansAddComponent implements OnInit { if (this.settings.security_check_when_going_back === true) { this.disableHardwareBack.unsubscribe(); } - } catch (ex) {} + } catch (ex) { + } this.modalController.dismiss( { - dismissed: true, + dismissed: true }, undefined, BeansAddComponent.COMPONENT_ID @@ -202,7 +270,8 @@ export class BeansAddComponent implements OnInit { try { const newPath: string = await this.uiFileHelper.copyFile(attachment); copyAttachments.push(newPath); - } catch (ex) {} + } catch (ex) { + } } this.data.attachments = copyAttachments; if (_bean.cupped_flavor) { diff --git a/src/app/beans/beans.page.html b/src/app/beans/beans.page.html index 73bb7d49..97fb274c 100644 --- a/src/app/beans/beans.page.html +++ b/src/app/beans/beans.page.html @@ -74,6 +74,10 @@ + + + +
@@ -85,8 +89,8 @@
- - + @@ -109,6 +113,10 @@ + + + +
@@ -118,8 +126,8 @@ {{"PAGE_BEANS_LIST_YOU_GOT_NO_FROZEN_BEANS" | translate}}
- - + @@ -142,6 +150,10 @@ + + + +
@@ -151,8 +163,8 @@ {{"PAGE_BEANS_LIST_YOU_GOT_NO_FINISHED_BEANS" | translate}}
- - + diff --git a/src/app/beans/beans.page.ts b/src/app/beans/beans.page.ts index c47a41e5..a9331db4 100644 --- a/src/app/beans/beans.page.ts +++ b/src/app/beans/beans.page.ts @@ -74,6 +74,10 @@ export class BeansPage implements OnDestroy { sort_order: BEAN_SORT_ORDER.UNKOWN, }; + public openBeansCollapsed: boolean = false; + public archivedBeansCollapsed: boolean = false; + public frozenBeansCollapsed: boolean = false; + public archivedBeansFilterText: string = ''; public openBeansFilterText: string = ''; public frozenBeansFilterText: string = ''; @@ -100,10 +104,15 @@ export class BeansPage implements OnDestroy { this.settings = this.uiSettingsStorage.getSettings(); this.archivedBeansSort = this.settings.bean_sort.ARCHIVED; this.openBeansSort = this.settings.bean_sort.OPEN; + this.frozenBeansSort = this.settings.bean_sort.FROZEN; this.archivedBeansFilter = this.settings.bean_filter.ARCHIVED; this.frozenBeansFilter = this.settings.bean_filter.FROZEN; this.openBeansFilter = this.settings.bean_filter.OPEN; + + this.openBeansCollapsed = this.settings.bean_collapsed.OPEN; + this.archivedBeansCollapsed = this.settings.bean_collapsed.ARCHIVED; + this.frozenBeansCollapsed = this.settings.bean_collapsed.FROZEN; this.loadBeans(); this.beanStorageChangeSubscription = this.uiBeanStorage @@ -119,6 +128,37 @@ export class BeansPage implements OnDestroy { this.beanStorageChangeSubscription = undefined; } } + public isCollapseActive() { + let collapsed: boolean =false; + if (this.bean_segment === 'open') { + collapsed = this.openBeansCollapsed; + } else if (this.bean_segment === 'archive') { + collapsed = this.archivedBeansCollapsed; + } else if (this.bean_segment === 'frozen') { + collapsed = this.frozenBeansCollapsed; + } + return collapsed; + } + + public toggleCollapseBeans() { + if (this.bean_segment === 'open') { + this.openBeansCollapsed = !this.openBeansCollapsed; + } else if (this.bean_segment === 'archive') { + this.archivedBeansCollapsed = !this.archivedBeansCollapsed; + } else if (this.bean_segment === 'frozen') { + this.frozenBeansCollapsed = !this.frozenBeansCollapsed; + } + this.__saveCollapseFilter(); + this.research(); + } + private async __saveCollapseFilter() { + this.settings.bean_collapsed.OPEN = this.openBeansCollapsed; + this.settings.bean_collapsed.ARCHIVED = this.archivedBeansCollapsed; + this.settings.bean_collapsed.FROZEN = this.frozenBeansCollapsed; + await this.uiSettingsStorage.saveSettings(this.settings); + } + + public loadBeans(): void { this.__initializeBeans(); this.changeDetectorRef.detectChanges(); @@ -495,10 +535,10 @@ export class BeansPage implements OnDestroy { break; case BEAN_SORT_AFTER.ROASTING_DATE: filterBeans = filterBeans.sort((a, b) => { - if (a.roastingDate > b.roastingDate) { + if (a.roastingDate < b.roastingDate) { return -1; } - if (a.roastingDate < b.roastingDate) { + if (a.roastingDate > b.roastingDate) { return 1; } return 0; @@ -506,10 +546,21 @@ export class BeansPage implements OnDestroy { break; case BEAN_SORT_AFTER.RATING: filterBeans = filterBeans.sort((a, b) => { + if (a.rating < b.rating) { + return -1; + } if (a.rating > b.rating) { + return 1; + } + return 0; + }); + break; + case BEAN_SORT_AFTER.BEAN_AGE: + filterBeans = filterBeans.sort((a, b) => { + if (a.beanAgeInDays() < b.beanAgeInDays()) { return -1; } - if (a.rating < b.rating) { + if (a.beanAgeInDays() > b.beanAgeInDays()) { return 1; } return 0; diff --git a/src/app/brew/brew-add/brew-add.component.html b/src/app/brew/brew-add/brew-add.component.html index 879e5da1..b74ee3c5 100644 --- a/src/app/brew/brew-add/brew-add.component.html +++ b/src/app/brew/brew-add/brew-add.component.html @@ -18,6 +18,16 @@ + + + + + + + + + + diff --git a/src/app/brew/brew-choose-preparation-to-brew/brew-choose-preparation-to-brew.component.html b/src/app/brew/brew-choose-preparation-to-brew/brew-choose-preparation-to-brew.component.html index f7715077..b2f76929 100644 --- a/src/app/brew/brew-choose-preparation-to-brew/brew-choose-preparation-to-brew.component.html +++ b/src/app/brew/brew-choose-preparation-to-brew/brew-choose-preparation-to-brew.component.html @@ -8,7 +8,8 @@ - {{prep.name}} ({{"LAST_USE" | translate}}: + {{prep.name}} ({{"LAST_USE" | translate}}: {{lastUsed(prep) | formatDate:[settings?.date_format]}} -) diff --git a/src/app/brew/brew-edit/brew-edit.component.html b/src/app/brew/brew-edit/brew-edit.component.html index 72bb081f..462b5e87 100644 --- a/src/app/brew/brew-edit/brew-edit.component.html +++ b/src/app/brew/brew-edit/brew-edit.component.html @@ -18,6 +18,16 @@ + + + + + + + + + + diff --git a/src/app/brew/brew-modal-import-shot-meticulous/brew-modal-import-shot-meticulous.component.html b/src/app/brew/brew-modal-import-shot-meticulous/brew-modal-import-shot-meticulous.component.html index 0d9e1902..9025f316 100644 --- a/src/app/brew/brew-modal-import-shot-meticulous/brew-modal-import-shot-meticulous.component.html +++ b/src/app/brew/brew-modal-import-shot-meticulous/brew-modal-import-shot-meticulous.component.html @@ -1,6 +1,6 @@ - {{"CHOOSE_REFERENCE_GRAPH" | translate}} + {{"IMPORT_SHOT_FROM_METICULOUS" | translate}} @@ -9,16 +9,20 @@ - + - - - - - {{entry.profile.name}} - - + + + + +

{{entry.profile.name}}

+
{{this.uiHelper.formatTimeNumber(entry.time * 1000)}}
+
+ +
+
+
@@ -27,8 +31,8 @@ - {{"RESET" | translate}} + {{"CANCEL" | translate}} { let component: BrewModalImportShotMeticulousComponent; let fixture: ComponentFixture; @@ -10,7 +15,17 @@ describe('BrewModalImportShotMeticulousComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ declarations: [BrewModalImportShotMeticulousComponent], - imports: [IonicModule.forRoot()], + imports: [IonicModule.forRoot(), TranslateModule.forRoot()], + providers: [ + { provide: InAppBrowser }, + { provide: File }, + { provide: SocialSharing }, + { provide: FileTransfer }, + { + provide: TranslateService, + useValue: TranslateServiceMock, + }, + ] }).compileComponents(); fixture = TestBed.createComponent(BrewModalImportShotMeticulousComponent); diff --git a/src/app/brew/brew-modal-import-shot-meticulous/brew-modal-import-shot-meticulous.component.ts b/src/app/brew/brew-modal-import-shot-meticulous/brew-modal-import-shot-meticulous.component.ts index cc14aa86..c98b7487 100644 --- a/src/app/brew/brew-modal-import-shot-meticulous/brew-modal-import-shot-meticulous.component.ts +++ b/src/app/brew/brew-modal-import-shot-meticulous/brew-modal-import-shot-meticulous.component.ts @@ -1,15 +1,10 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, ElementRef, HostListener, Input, OnInit, ViewChild } from '@angular/core'; import { MeticulousDevice } from '../../../classes/preparationDevice/meticulous/meticulousDevice'; -import moment from 'moment/moment'; -import { - BrewFlow, - IBrewPressureFlow, - IBrewRealtimeWaterFlow, - IBrewTemperatureFlow, - IBrewWeightFlow, -} from '../../../classes/brew/brewFlow'; import { ModalController } from '@ionic/angular'; +import { HistoryListingEntry } from '@meticulous-home/espresso-api/dist/types'; +import { UIHelper } from '../../../services/uiHelper'; +import { AgVirtualSrollComponent } from 'ag-virtual-scroll'; @Component({ selector: 'app-brew-modal-import-shot-meticulous', @@ -21,81 +16,64 @@ export class BrewModalImportShotMeticulousComponent implements OnInit { @Input() public meticulousDevice: MeticulousDevice; public radioSelection: string; - public history: Array = []; - constructor(private readonly modalController: ModalController) {} + public history: Array = []; - public ngOnInit() { - this.readHistory(); - } + @ViewChild('ionItemEl', { read: ElementRef, static: false }) + public ionItemEl: ElementRef; - private async readHistory() { - this.history = await this.meticulousDevice.getHistory(); - //this.readShot(history[0]); - } + @ViewChild('historyShotContent', { read: ElementRef }) + public historyShotContent: ElementRef; - private readShot(_historyData) { - const newMoment = moment(new Date()).startOf('day'); + @ViewChild('shotDataScroll', { + read: AgVirtualSrollComponent, + static: false, + }) + public shotDataScroll: AgVirtualSrollComponent; - let firstDripTimeSet: boolean = false; - const newBrewFlow = new BrewFlow(); + @ViewChild('footerContent', { read: ElementRef }) + public footerContent: ElementRef; - let seconds: number = 0; - let milliseconds: number = 0; - for (const entry of _historyData.data as any) { - const shotEntry: any = entry.shot; - const shotEntryTime = newMoment.clone().add('milliseconds', entry.time); - const timestamp = shotEntryTime.format('HH:mm:ss.SSS'); + constructor(private readonly modalController: ModalController, + public readonly uiHelper: UIHelper) {} - seconds = shotEntryTime.diff(newMoment, 'seconds'); - milliseconds = shotEntryTime.get('milliseconds'); - - const realtimeWaterFlow: IBrewRealtimeWaterFlow = - {} as IBrewRealtimeWaterFlow; + public ngOnInit() { + this.readHistory(); + } - realtimeWaterFlow.brew_time = ''; - realtimeWaterFlow.timestamp = timestamp; - realtimeWaterFlow.smoothed_weight = 0; - realtimeWaterFlow.flow_value = shotEntry.flow; - realtimeWaterFlow.timestampdelta = 0; + private async readHistory() { + this.history = await this.meticulousDevice?.getHistory(); + this.retriggerScroll(); - newBrewFlow.realtimeFlow.push(realtimeWaterFlow); + } + @HostListener('window:resize') + @HostListener('window:orientationchange', ['$event']) + public onOrientationChange(event) { + this.retriggerScroll(); + } - const brewFlow: IBrewWeightFlow = {} as IBrewWeightFlow; - brewFlow.timestamp = timestamp; - brewFlow.brew_time = ''; - brewFlow.actual_weight = shotEntry.weight; - brewFlow.old_weight = 0; - brewFlow.actual_smoothed_weight = 0; - brewFlow.old_smoothed_weight = 0; - brewFlow.not_mutated_weight = 0; - newBrewFlow.weight.push(brewFlow); - if (shotEntry.weight > 0 && firstDripTimeSet === false) { - firstDripTimeSet = true; + private retriggerScroll() { + setTimeout(async () => { + const el = this.historyShotContent.nativeElement; + const scrollComponent: AgVirtualSrollComponent = this.shotDataScroll; - //this.brewComponent.brewFirstDripTime.setTime(seconds, milliseconds); - //this.brewComponent.brewFirstDripTime.changeEvent(); + if (scrollComponent) { + scrollComponent.el.style.height = + (el.offsetHeight - + 20) + + 'px'; } + }, 150); + } - const pressureFlow: IBrewPressureFlow = {} as IBrewPressureFlow; - pressureFlow.timestamp = timestamp; - pressureFlow.brew_time = ''; - pressureFlow.actual_pressure = shotEntry.pressure; - pressureFlow.old_pressure = 0; - newBrewFlow.pressureFlow.push(pressureFlow); - - const temperatureFlow: IBrewTemperatureFlow = {} as IBrewTemperatureFlow; - temperatureFlow.timestamp = timestamp; - temperatureFlow.brew_time = ''; - temperatureFlow.actual_temperature = shotEntry.temperature; - temperatureFlow.old_temperature = 0; - newBrewFlow.temperatureFlow.push(temperatureFlow); + public getElementOffsetWidth() { + if (this.ionItemEl?.nativeElement?.offsetWidth) { + return this.ionItemEl?.nativeElement?.offsetWidth - 50; } + return 0; + } - const lastEntry = newBrewFlow.weight[newBrewFlow.weight.length - 1]; - console.log(newBrewFlow); - } public dismiss(): void { this.modalController.dismiss( @@ -107,8 +85,17 @@ export class BrewModalImportShotMeticulousComponent implements OnInit { ); } public choose(): void { + let returningData; + + for (const entry of this.history) { + if (entry.id === this.radioSelection) { + returningData = entry; + break; + } + } this.modalController.dismiss( { + choosenHistory: returningData, dismissed: true, }, undefined, diff --git a/src/app/brew/brew.page.html b/src/app/brew/brew.page.html index 6bdb6ffd..776ca7fd 100644 --- a/src/app/brew/brew.page.html +++ b/src/app/brew/brew.page.html @@ -55,6 +55,12 @@ + + + + + +
@@ -65,8 +71,8 @@
- - + @@ -84,6 +90,10 @@ + + + +
@@ -94,8 +104,8 @@
- - + diff --git a/src/app/brew/brew.page.ts b/src/app/brew/brew.page.ts index dfb6e618..4b291ebb 100644 --- a/src/app/brew/brew.page.ts +++ b/src/app/brew/brew.page.ts @@ -6,8 +6,7 @@ import { OnInit, ViewChild, } from '@angular/core'; -import { ModalController, Platform } from '@ionic/angular'; -import { UIAlert } from '../../services/uiAlert'; +import { ModalController } from '@ionic/angular'; import { UIHelper } from '../../services/uiHelper'; import { UIBrewStorage } from '../../services/uiBrewStorage'; import { UISettingsStorage } from '../../services/uiSettingsStorage'; @@ -19,7 +18,6 @@ import { Bean } from '../../classes/bean/bean'; import { BrewFilterComponent } from './brew-filter/brew-filter.component'; import { Settings } from '../../classes/settings/settings'; import { AgVirtualSrollComponent } from 'ag-virtual-scroll'; -import { UIAnalytics } from '../../services/uiAnalytics'; @Component({ selector: 'brew', @@ -49,18 +47,18 @@ export class BrewPage implements OnInit { public archivedBrewsFilter: IBrewPageFilter; public openBrewsFilter: IBrewPageFilter; + public openBrewsCollapsed: boolean = false; + public archivedBrewsCollapsed: boolean = false; + public settings: Settings; constructor( private readonly modalCtrl: ModalController, - private readonly platform: Platform, private readonly uiBrewStorage: UIBrewStorage, private readonly changeDetectorRef: ChangeDetectorRef, - private readonly uiAlert: UIAlert, public uiHelper: UIHelper, public uiBrewHelper: UIBrewHelper, private readonly uiSettingsStorage: UISettingsStorage, - private readonly uiAnalytics: UIAnalytics ) { this.settings = this.uiSettingsStorage.getSettings(); this.archivedBrewsFilter = this.settings.GET_BREW_FILTER(); @@ -70,6 +68,8 @@ export class BrewPage implements OnInit { public ionViewWillEnter(): void { this.archivedBrewsFilter = this.settings.brew_filter.ARCHIVED; this.openBrewsFilter = this.settings.brew_filter.OPEN; + this.openBrewsCollapsed = this.settings.brew_collapsed.OPEN; + this.archivedBrewsCollapsed = this.settings.brew_collapsed.ARCHIVED; this.loadBrews(); this.retriggerScroll(); @@ -123,6 +123,26 @@ export class BrewPage implements OnInit { this.__initializeBrewView('archiv'); } + public isCollapseActive() { + let collapsed: boolean =false; + if (this.brew_segment === 'open') { + collapsed = this.openBrewsCollapsed; + } else { + collapsed = this.archivedBrewsCollapsed; + } + return collapsed; + } + + public toggleCollapseBrews() { + if (this.brew_segment === 'open') { + this.openBrewsCollapsed = !this.openBrewsCollapsed; + } else { + this.archivedBrewsCollapsed = !this.archivedBrewsCollapsed; + } + this.__saveCollapseFilter(); + this.research(); + } + public isFilterActive(): boolean { let checkingFilter: IBrewPageFilter; let checkingFilterText: string = ''; @@ -198,6 +218,8 @@ export class BrewPage implements OnInit { return instructor.config.uuid; } + + public async showFilter() { let brewFilter: IBrewPageFilter; if (this.brew_segment === 'open') { @@ -237,6 +259,11 @@ export class BrewPage implements OnInit { this.settings.brew_filter.ARCHIVED = this.archivedBrewsFilter; await this.uiSettingsStorage.saveSettings(this.settings); } + private async __saveCollapseFilter() { + this.settings.brew_collapsed.OPEN = this.openBrewsCollapsed; + this.settings.brew_collapsed.ARCHIVED = this.archivedBrewsCollapsed; + await this.uiSettingsStorage.saveSettings(this.settings); + } public research() { this.__initializeBrewView(this.brew_segment); diff --git a/src/app/preparation/preparation-add-type/preparation-add-type.component.ts b/src/app/preparation/preparation-add-type/preparation-add-type.component.ts index 4d2a4a22..db570736 100644 --- a/src/app/preparation/preparation-add-type/preparation-add-type.component.ts +++ b/src/app/preparation/preparation-add-type/preparation-add-type.component.ts @@ -11,6 +11,7 @@ import { PREPARATION_STYLE_TYPE } from '../../../enums/preparations/preparationS import { PreparationTool } from '../../../classes/preparation/preparationTool'; import PREPARATION_TRACKING from '../../../data/tracking/preparationTracking'; import { UIAnalytics } from '../../../services/uiAnalytics'; +import { UIPreparationHelper } from '../../../services/uiPreparationHelper'; @Component({ selector: 'preparation-add-type', @@ -34,9 +35,11 @@ export class PreparationAddTypeComponent implements OnInit { private readonly navParams: NavParams, private readonly uiToast: UIToast, private readonly translate: TranslateService, - private readonly uiAnalytics: UIAnalytics + private readonly uiAnalytics: UIAnalytics, + private readonly uiPreparationHelper: UIPreparationHelper ) { this.data.type = this.navParams.get('type'); + if (this.data.type !== PREPARATION_TYPES.CUSTOM_PREPARATION) { this.data.name = this.translate.instant( 'PREPARATION_TYPE_' + this.data.type @@ -69,11 +72,15 @@ export class PreparationAddTypeComponent implements OnInit { this.data.manage_parameters.coffee_first_drip_time = false; this.data.default_last_coffee_parameters.coffee_first_drip_time = false; } - await this.uiPreparationStorage.add(this.data); + const newPreparation = await this.uiPreparationStorage.add(this.data); this.dismiss(true); if (!this.hide_toast_message) { this.uiToast.showInfoToast('TOAST_PREPARATION_ADDED_SUCCESSFULLY'); } + + if (this.data.type === PREPARATION_TYPES.METICULOUS || this.data.type === PREPARATION_TYPES.XENIA || this.data.type === PREPARATION_TYPES.SANREMO_YOU) { + await this.uiPreparationHelper.connectDevice(newPreparation); + } } public async dismiss(_added: boolean) { diff --git a/src/app/preparation/preparation-add/preparation-add.component.html b/src/app/preparation/preparation-add/preparation-add.component.html index 590de67e..7a6d4394 100644 --- a/src/app/preparation/preparation-add/preparation-add.component.html +++ b/src/app/preparation/preparation-add/preparation-add.component.html @@ -12,28 +12,31 @@ - - - + -
- -
+ + + + +
+ +
-
- {{'PREPARATION_TYPE_' + key | translate}} -
-
-
- + {{'PREPARATION_TYPE_' + key | translate}} +
+
+ +
+ +
+
+ {{'PREPARATION_TYPE_ADD_CUSTOM' | translate}}
-
- {{'PREPARATION_TYPE_ADD_CUSTOM' | translate}} -
-
-
-
+
+
+
+
diff --git a/src/app/preparation/preparation-add/preparation-add.component.ts b/src/app/preparation/preparation-add/preparation-add.component.ts index 47f68849..ada8b095 100644 --- a/src/app/preparation/preparation-add/preparation-add.component.ts +++ b/src/app/preparation/preparation-add/preparation-add.component.ts @@ -7,6 +7,7 @@ import { NgForm } from '@angular/forms'; import { PreparationAddTypeComponent } from '../preparation-add-type/preparation-add-type.component'; import PREPARATION_TRACKING from '../../../data/tracking/preparationTracking'; import { UIAnalytics } from '../../../services/uiAnalytics'; +import { environment } from '../../../environments/environment'; @Component({ selector: 'preparation-add', templateUrl: './preparation-add.component.html', @@ -18,6 +19,8 @@ export class PreparationAddComponent implements OnInit { public preparation_types_enum = PREPARATION_TYPES; + public ENVIRONMENT = environment; + @ViewChild('addPreparationForm', { static: false }) public preparationForm: NgForm; @@ -35,6 +38,16 @@ export class PreparationAddComponent implements OnInit { ); } + public individualPreparationVisible(_key) { + if (_key ==='SANREMO_YOU') { + if (this.ENVIRONMENT.FEATURES_ACTIVE.SANREMO_YOU === true) { + return true; + } + return false; + } + return true; + } + public async choosePreparation(_prepType: PREPARATION_TYPES) { //Animated false, else backdrop would sometimes not disappear and stay until user touches again. const modal = await this.modalController.create({ diff --git a/src/app/preparation/preparation-connected-device/preparation-connected-device.component.html b/src/app/preparation/preparation-connected-device/preparation-connected-device.component.html index 42bf7062..071f6557 100644 --- a/src/app/preparation/preparation-connected-device/preparation-connected-device.component.html +++ b/src/app/preparation/preparation-connected-device/preparation-connected-device.component.html @@ -12,18 +12,19 @@ - {{"PREPARATION_DEVICE.TYPE.NONE" | translate}} {{"PREPARATION_DEVICE.TYPE.XENIA" | translate}} {{"PREPARATION_DEVICE.TYPE.METICULOUS" | translate}} + {{"PREPARATION_DEVICE.TYPE.SANREMO_YOU" | translate}} - + {{"PREPARATION_DEVICE.URL" | translate}} - @@ -31,7 +32,7 @@ - V1 V2 @@ -40,7 +41,7 @@

{{"PREPARATION_DEVICE.RESIDUAL_LAG_TIME" | translate }} {{uiHelper.toFixedIfNecessary(preparation.connectedPreparationDevice.customParams.residualLagTime,2)}}

+ style="vertical-align: top;">{{uiHelper.toFixedIfNecessary(data.connectedPreparationDevice.customParams.residualLagTime,2)}}
@@ -49,33 +50,59 @@

{{"PREPARATION_DEVICE.RESIDUAL_LAG_TIME" | translate }}  - - +
{{"SAVE_LOGFILES_TO_NOTES_FROM_MACHINE" | translate}}
- - + + + {{"PREPARATION_DEVICE.URL" | translate}} + + + + + + + + {{"PREPARATION_DEVICE.URL" | translate}} - + + + + + +

{{"PREPARATION_DEVICE.RESIDUAL_LAG_TIME" | translate }} {{uiHelper.toFixedIfNecessary(data.connectedPreparationDevice.customParams.residualLagTime,2)}}

+
+
+ + +

{{"PREPARATION_DEVICE.RESIDUAL_LAG_TIME_DESCRIPTION" | translate }}

+
+
+ + + + +
- - - - + diff --git a/src/app/preparation/preparation-connected-device/preparation-connected-device.component.ts b/src/app/preparation/preparation-connected-device/preparation-connected-device.component.ts index e4425e92..8e9d18f1 100644 --- a/src/app/preparation/preparation-connected-device/preparation-connected-device.component.ts +++ b/src/app/preparation/preparation-connected-device/preparation-connected-device.component.ts @@ -12,6 +12,8 @@ import { UIAlert } from '../../../services/uiAlert'; import { UIHelper } from '../../../services/uiHelper'; import { UISettingsStorage } from '../../../services/uiSettingsStorage'; import { Settings } from '../../../classes/settings/settings'; +import { environment } from '../../../environments/environment'; +import { PREPARATION_TYPES } from '../../../enums/preparations/preparationTypes'; @Component({ selector: 'app-preparation-connected-device', @@ -26,6 +28,8 @@ export class PreparationConnectedDeviceComponent { public PREPARATION_DEVICE_TYPE = PreparationDeviceType; @Input() public preparation: IPreparation; + public ENVIRONMENT_PARAMS = environment; + public pinFormatter(value: any) { const parsedFloat = parseFloat(value); if (isNaN(parsedFloat)) { @@ -48,6 +52,18 @@ export class PreparationConnectedDeviceComponent { if (this.preparation !== undefined) { this.data.initializeByObject(this.preparation); } + if (this.data.connectedPreparationDevice.type === PreparationDeviceType.NONE) { + if (this.data.type === PREPARATION_TYPES.METICULOUS) { + this.data.connectedPreparationDevice.type = PreparationDeviceType.METICULOUS; + } + if (this.data.type === PREPARATION_TYPES.XENIA) { + this.data.connectedPreparationDevice.type = PreparationDeviceType.XENIA; + } + if (this.data.type === PREPARATION_TYPES.SANREMO_YOU) { + this.data.connectedPreparationDevice.type = PreparationDeviceType.SANREMO_YOU; + } + } + } public dismiss(): void { @@ -98,7 +114,40 @@ export class PreparationConnectedDeviceComponent { this.data.connectedPreparationDevice.customParams.residualLagTime = 1.35; } } - + if ( + this.data.connectedPreparationDevice.type === + PreparationDeviceType.METICULOUS + ) + { + if (this.data.connectedPreparationDevice.url.endsWith('/') === true) { + this.data.connectedPreparationDevice.url = + this.data.connectedPreparationDevice.url.slice(0, -1); + } + if ( + this.data.connectedPreparationDevice.url.startsWith('http') === + false + ) { + this.data.connectedPreparationDevice.url = + 'http://' + this.data.connectedPreparationDevice.url; + } + } + if ( + this.data.connectedPreparationDevice.type === + PreparationDeviceType.SANREMO_YOU + ) + { + if (this.data.connectedPreparationDevice.url.endsWith('/') === true) { + this.data.connectedPreparationDevice.url = + this.data.connectedPreparationDevice.url.slice(0, -1); + } + if ( + this.data.connectedPreparationDevice.url.startsWith('http') === + false + ) { + this.data.connectedPreparationDevice.url = + 'http://' + this.data.connectedPreparationDevice.url; + } + } if ( this.data.connectedPreparationDevice.type !== PreparationDeviceType.NONE ) { @@ -136,4 +185,6 @@ export class PreparationConnectedDeviceComponent { } public ngOnInit() {} + + } diff --git a/src/app/preparation/preparation-modal-select/preparation-modal-select.component.html b/src/app/preparation/preparation-modal-select/preparation-modal-select.component.html index 5d775696..3151de2b 100644 --- a/src/app/preparation/preparation-modal-select/preparation-modal-select.component.html +++ b/src/app/preparation/preparation-modal-select/preparation-modal-select.component.html @@ -28,7 +28,8 @@ -
{{prep.name}}
+
{{prep.name}}

{{"LAST_USE" | translate}}: {{lastUsed(prep) | formatDate:[settings?.date_format]}} - @@ -47,7 +48,8 @@ -

{{prep.name}}
+
{{prep.name}}

{{"LAST_USE" | translate}}: {{lastUsed(prep) | formatDate:[settings?.date_format]}} - @@ -72,7 +74,8 @@

{{prep.name}}
-
{{prep.name}}
+
{{prep.name}}

{{"LAST_USE" | translate}}: {{lastUsed(prep) | formatDate:[settings?.date_format]}} - @@ -91,7 +94,8 @@

{{prep.name}}
-
{{prep.name}}
+
{{prep.name}}

{{"LAST_USE" | translate}}: {{lastUsed(prep) | formatDate:[settings?.date_format]}} - diff --git a/src/app/settings/settings.page.html b/src/app/settings/settings.page.html index 33306782..9ead758f 100644 --- a/src/app/settings/settings.page.html +++ b/src/app/settings/settings.page.html @@ -55,12 +55,16 @@

{{"EXPORT" | translate}}

Temp: Export Brew By Weight - Xenia
- + {{"EXCEL_EXPORT" | translate}} - - {{"IMPORT_BEANS_EXCEL" | translate}} + + {{"IMPORT_ROASTED_BEANS_EXCEL" | translate}} + + + + {{"IMPORT_GREEN_BEANS_EXCEL" | translate}} @@ -361,7 +365,7 @@

{{"PAGE_SETTINGS_BREW_TIMER_START_DELAY_ACTIVE" | translate}}

{{"PAGE_SETTINGS_MANAGE_FEATURES" | translate}} -
{{"ACTIVE_BEAN_FREEZING_FEATURE" | translate}}
diff --git a/src/app/settings/settings.page.ts b/src/app/settings/settings.page.ts index 46b8bd9b..c5047fb9 100644 --- a/src/app/settings/settings.page.ts +++ b/src/app/settings/settings.page.ts @@ -14,7 +14,6 @@ import { debounceTime, distinctUntilChanged } from 'rxjs/operators'; import { DirectoryEntry, FileEntry } from '@awesome-cordova-plugins/file'; import { FileChooser } from '@awesome-cordova-plugins/file-chooser/ngx'; import { File } from '@awesome-cordova-plugins/file/ngx'; -import { FilePath } from '@awesome-cordova-plugins/file-path/ngx'; import { IBean } from '../../interfaces/bean/iBean'; import { IBrew } from '../../interfaces/brew/iBrew'; import { ISettings } from '../../interfaces/settings/iSettings'; @@ -918,6 +917,9 @@ export class SettingsPage { public async resetFilter() { this.settings.resetFilter(); } + public async resetBeanSort() { + this.settings.resetBeanSort(); + } public async fixWeightChangeMinFlowNumber() { //We need to trigger this, because the slider sometimes procudes values like 0.60000001, and we need to fix this before saving @@ -1223,7 +1225,9 @@ export class SettingsPage { await this.uiAlert.showLoadingSpinner(); try { const allXeniaPreps = []; - const allPreparations = this.uiPreparationStorage.getAllEntries(); + let allPreparations = this.uiPreparationStorage.getAllEntries(); + // Just take 60, else the excel will be exploding. + allPreparations = allPreparations.reverse().slice(0,60); for (const prep of allPreparations) { if ( prep.connectedPreparationDevice.type === PreparationDeviceType.XENIA @@ -1274,7 +1278,7 @@ export class SettingsPage { } } - public importBeansExcel(): void { + public importBeansExcel(_type: string='roasted'): void { if (this.platform.is('cordova')) { this.uiAnalytics.trackEvent( SETTINGS_TRACKING.TITLE, @@ -1291,7 +1295,12 @@ export class SettingsPage { this.uiFileHelper.readFileEntryAsArrayBuffer(fileEntry).then( async (_arrayBuffer) => { - this.uiExcel.importBeansByExcel(_arrayBuffer); + if (_type ==='roasted') { + this.uiExcel.importBeansByExcel(_arrayBuffer); + }else { + this.uiExcel.importGreenBeansByExcel(_arrayBuffer); + } + }, () => { // Backup, maybe it was a .JSON? @@ -1317,7 +1326,11 @@ export class SettingsPage { } this.uiFileHelper.readFileAsArrayBuffer(path, file).then( async (_arrayBuffer) => { - this.uiExcel.importBeansByExcel(_arrayBuffer); + if (_type ==='roasted') { + this.uiExcel.importBeansByExcel(_arrayBuffer); + } else { + this.uiExcel.importGreenBeansByExcel(_arrayBuffer); + } }, () => { // Backup, maybe it was a .JSON? diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index a34ce5ec..731d7b0f 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -190,6 +190,8 @@ import { BeanFreezeInformationComponent } from '../../components/beans/bean-free import { BeanPopoverFrozenListComponent } from '../beans/bean-popover-frozen-list/bean-popover-frozen-list.component'; import { PipesModule } from 'src/pipes/pipes.module'; import { BrewModalImportShotMeticulousComponent } from '../brew/brew-modal-import-shot-meticulous/brew-modal-import-shot-meticulous.component'; +import { DataCorruptionFoundComponent } from '../../popover/data-corruption-found/data-corruption-found.component'; +import { BeanPopoverListComponent } from '../beans/bean-popover-list/bean-popover-list.component'; @NgModule({ declarations: [ @@ -232,6 +234,7 @@ import { BrewModalImportShotMeticulousComponent } from '../brew/brew-modal-impor WelcomePopoverComponent, AnalyticsPopoverComponent, MeticulousHelpPopoverComponent, + DataCorruptionFoundComponent, QrCodeScannerPopoverComponent, UpdatePopoverComponent, DatetimePopoverComponent, @@ -317,6 +320,7 @@ import { BrewModalImportShotMeticulousComponent } from '../brew/brew-modal-impor BeanPopoverAddComponent, BeanPopoverFreezeComponent, BeanPopoverFrozenListComponent, + BeanPopoverListComponent, BeanArchivePopoverComponent, MillPopoverActionsComponent, BeanModalSelectComponent, @@ -444,6 +448,7 @@ import { BrewModalImportShotMeticulousComponent } from '../brew/brew-modal-impor WelcomePopoverComponent, AnalyticsPopoverComponent, MeticulousHelpPopoverComponent, + DataCorruptionFoundComponent, QrCodeScannerPopoverComponent, UpdatePopoverComponent, DatetimePopoverComponent, @@ -526,6 +531,7 @@ import { BrewModalImportShotMeticulousComponent } from '../brew/brew-modal-impor BeanPopoverAddComponent, BeanPopoverFreezeComponent, BeanPopoverFrozenListComponent, + BeanPopoverListComponent, BeanArchivePopoverComponent, BeanModalSelectComponent, AssociatedBrewsComponent, diff --git a/src/assets/custom-ion-icons/beanconqueror-expand-active.svg b/src/assets/custom-ion-icons/beanconqueror-expand-active.svg new file mode 100644 index 00000000..c7db982c --- /dev/null +++ b/src/assets/custom-ion-icons/beanconqueror-expand-active.svg @@ -0,0 +1 @@ + diff --git a/src/assets/custom-ion-icons/beanconqueror-expand-inactive.svg b/src/assets/custom-ion-icons/beanconqueror-expand-inactive.svg new file mode 100644 index 00000000..a4b978e8 --- /dev/null +++ b/src/assets/custom-ion-icons/beanconqueror-expand-inactive.svg @@ -0,0 +1 @@ + diff --git a/src/assets/custom-ion-icons/beanconqueror-sanremo-you-logo.svg b/src/assets/custom-ion-icons/beanconqueror-sanremo-you-logo.svg new file mode 100644 index 00000000..076d07b5 --- /dev/null +++ b/src/assets/custom-ion-icons/beanconqueror-sanremo-you-logo.svg @@ -0,0 +1,35 @@ + + + + + + + + diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index b73e2994..5990d359 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -1030,7 +1030,8 @@ "TYPE": { "NONE": "None", "XENIA": "Xenia", - "METICULOUS": "Meticulous" + "METICULOUS": "Meticulous", + "SANREMO_YOU": "Sanremo YOU" }, "URL": "Url", "CHOOSE_DEVICE": "Choose device", @@ -1063,6 +1064,10 @@ "CHOOSE_PROFILE": "Choose profile", "SHOT_STARTED": "Shot started", "SHOT_ENDED": "Shot ended" + }, + "TYPE_SANREMO_YOU": { + "TITLE": "Sanremo YOU Machine", + "STOP_AT_WEIGHT": "Stop at weight" } }, "DEVICE_CONNECTION": "Device connection", @@ -1352,5 +1357,18 @@ "BEAN_DATA_FROZEN_NOTE": "Frozen notes", "IGNORE_NEGATIVE_VALUES_DESCRIPTION": "Activating this, results into a graph which will be one second behind", "IGNORE_ANOMALY_VALUES_DESCRIPTION": "Activating this, results into a graph which will be one second behind", - "SAVE_LOGFILES_TO_NOTES_FROM_MACHINE": "Save the machine logs when saving a brew" -} \ No newline at end of file + "SAVE_LOGFILES_TO_NOTES_FROM_MACHINE": "Save the machine logs when saving a brew", + "IMPORT_SHOT_FROM_METICULOUS": "Import a shot", + "BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "All found entries in the excel list, have been transformed into new beans, enjoy a tasty brew!", + "IMPORT_UNSUCCESSFULLY": "Import not successful", + "BEAN_LIST": "Bean list", + "IMPORT_ROASTED_BEANS_EXCEL": "Import roasted beans via excel", + "IMPORT_GREEN_BEANS_EXCEL": "Import green beans via excel", + "BEANS_IMPORTED_UNSUCCESSFULLY_WRONG_EXCELFILE": "It looks like that the chosen excel file is corrupted, has wrong data, is wrong structured, or was wrongly chosen. Because of this no beans could be imported.", + "OK": "Ok", + "BEAN_SORT_BEAN_AGE": "Bean age", + "GREEN_BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "All found entries in the excel list, have been transformed into new green beans, have great roasts!", + "PREPARATION_TYPE_SANREMO_YOU": "Sanremo YOU", + "PREPARATION_TYPE_XENIA": "Xenia", + "BEAN_POPUP_YOU_DONT_SEE_EVERYTHING_DESCRIPTION": "You're adding a bean which has variety information in it, but those parameters are not activated. Do you want to activate them now?" +} diff --git a/src/classes/bean/bean.ts b/src/classes/bean/bean.ts index 8c0b28a6..633b48a3 100755 --- a/src/classes/bean/bean.ts +++ b/src/classes/bean/bean.ts @@ -156,6 +156,13 @@ export class Bean implements IBean { return false; } } + public isUnfrozen() { + if (this.frozenDate && this.unfrozenDate) { + return true; + } else { + return false; + } + } public initializeByObject(beanObj: IBean): void { Object.assign(this, beanObj); diff --git a/src/classes/devices/argosThermometer.ts b/src/classes/devices/argosThermometer.ts new file mode 100644 index 00000000..5b04c434 --- /dev/null +++ b/src/classes/devices/argosThermometer.ts @@ -0,0 +1,72 @@ +import { PeripheralData } from './ble.types'; +import { Logger } from './common/logger'; + +import { TemperatureDevice } from './temperatureBluetoothDevice'; + +declare var ble: any; + +export class ArgosThermometer extends TemperatureDevice { + public static DEVICE_NAME = 'ARGOS'; + public static TEMPERATURE_SERVICE_UUID = + '6a521c59-55b5-4384-85c0-6534e63fb09e'; + public static TEMPERATURE_CHAR_UUID = '6a521c62-55b5-4384-85c0-6534e63fb09e'; + + private logger: Logger; + + constructor(data: PeripheralData) { + super(data); + this.connect(); + this.logger = new Logger('ArgosTemperatureSensor'); + } + + public static test(device: any): boolean { + return ( + device && + device.name && + device.name.toLowerCase().startsWith(ArgosThermometer.DEVICE_NAME.toLowerCase()) + ); + } + + public connect() { + this.attachNotification(); + } + + public disconnect() { + this.deattachNotification(); + } + + private attachNotification() { + ble.startNotification( + this.device_id, + ArgosThermometer.TEMPERATURE_SERVICE_UUID, + ArgosThermometer.TEMPERATURE_CHAR_UUID, + + async (_data: any) => { + let newData = new Uint8Array(_data.slice(0,-1)); + this.parseStatusUpdate(newData); + }, + + (_data: any) => {} + ); + } + + private parseStatusUpdate(temperatureRawStatus: Uint8Array) { + this.logger.log( + 'temperatureRawStatus received is: ' + temperatureRawStatus + ); + + const temperature_in_f = temperatureRawStatus[-1] << 8 + temperatureRawStatus[-2]; +console.log("New temperature inc" + temperature_in_f); + this.setTemperature(temperature_in_f, temperatureRawStatus); + } + + private deattachNotification() { + ble.stopNotification( + this.device_id, + ArgosThermometer.TEMPERATURE_SERVICE_UUID, + ArgosThermometer.TEMPERATURE_CHAR_UUID, + (e: any) => {}, + (e: any) => {} + ); + } +} diff --git a/src/classes/devices/index.ts b/src/classes/devices/index.ts index b18f3a7e..c6657b1c 100644 --- a/src/classes/devices/index.ts +++ b/src/classes/devices/index.ts @@ -25,6 +25,7 @@ import { BookooScale } from './bokooScale'; import { BasicGrillThermometer } from './basicGrillThermometer'; import { MeaterThermometer } from './meaterThermometer'; import { CombustionThermometer } from './combustionThermometer'; +import { ArgosThermometer } from './argosThermometer'; export { BluetoothScale, SCALE_TIMER_COMMAND } from './bluetoothDevice'; export * from './common'; @@ -56,6 +57,7 @@ export enum TemperatureType { BASICGRILL = 'BASICGRILL', MEATER = 'MEATER', COMBUSTION = 'COMBUSTION', + ARGOS = 'ARGOS' } export enum RefractometerType { @@ -129,6 +131,8 @@ export function makeTemperatureDevice( return new MeaterThermometer(data); case TemperatureType.COMBUSTION: return new CombustionThermometer(data); + case TemperatureType.ARGOS: + return new ArgosThermometer(data); default: return null; } diff --git a/src/classes/preparation/preparation.ts b/src/classes/preparation/preparation.ts index d18e4639..d163ddfb 100755 --- a/src/classes/preparation/preparation.ts +++ b/src/classes/preparation/preparation.ts @@ -12,6 +12,7 @@ import { UIHelper } from '../../services/uiHelper'; import { ListViewBrewParameter } from '../parameter/listViewBrewParameter'; import { RepeatBrewParameter } from '../parameter/repeatBrewParameter'; import { ConnectedPreparationDevice } from '../preparationDevice/connectedPreparationDevice'; +import { PreparationDeviceType } from '../preparationDevice'; export class Preparation implements IPreparation { public name: string; @@ -163,6 +164,10 @@ export class Preparation implements IPreparation { return PREPARATION_STYLE_TYPE.POUR_OVER; case PREPARATION_TYPES.METICULOUS: return PREPARATION_STYLE_TYPE.ESPRESSO; + case PREPARATION_TYPES.XENIA: + return PREPARATION_STYLE_TYPE.ESPRESSO; + case PREPARATION_TYPES.SANREMO_YOU: + return PREPARATION_STYLE_TYPE.ESPRESSO; default: return PREPARATION_STYLE_TYPE.POUR_OVER; } @@ -305,4 +310,11 @@ export class Preparation implements IPreparation { public hasPhotos(): boolean { return this.attachments && this.attachments.length > 0; } + public hasDeviceConnection(): boolean { + return ( + this.connectedPreparationDevice?.type !== + PreparationDeviceType.NONE + ); + } + } diff --git a/src/classes/preparationDevice/index.ts b/src/classes/preparationDevice/index.ts index ad1960ce..729697c2 100644 --- a/src/classes/preparationDevice/index.ts +++ b/src/classes/preparationDevice/index.ts @@ -3,11 +3,13 @@ import { PreparationDevice } from './preparationDevice'; import { HttpClient } from '@angular/common/http'; import { Preparation } from '../preparation/preparation'; import { MeticulousDevice } from './meticulous/meticulousDevice'; +import { SanremoYOUDevice } from './sanremo/sanremoYOUDevice'; export enum PreparationDeviceType { NONE = 'NONE', XENIA = 'XENIA', METICULOUS = 'METICULOUS', + SANREMO_YOU = 'SANREMO_YOU', } export function makePreparationDevice( @@ -20,6 +22,8 @@ export function makePreparationDevice( return new XeniaDevice(_http, _preparation); case PreparationDeviceType.METICULOUS: return new MeticulousDevice(_http, _preparation); + case PreparationDeviceType.SANREMO_YOU: + return new SanremoYOUDevice(_http, _preparation); default: return null; } diff --git a/src/classes/preparationDevice/meticulous/meticulousDevice.ts b/src/classes/preparationDevice/meticulous/meticulousDevice.ts index 93dbd744..5dae48b3 100644 --- a/src/classes/preparationDevice/meticulous/meticulousDevice.ts +++ b/src/classes/preparationDevice/meticulous/meticulousDevice.ts @@ -2,17 +2,15 @@ import { PreparationDevice } from '../preparationDevice'; import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Preparation } from '../../preparation/preparation'; import { MeticulousShotData } from './meticulousShotData'; -import Api, { - ApiResponseError, - ProfileIdent, - ActionType, -} from 'meticulous-api'; +import Api, { ActionType, ProfileIdent } from '@meticulous-home/espresso-api'; import { IMeticulousParams } from '../../../interfaces/preparationDevices/meticulous/iMeticulousParams'; import { Profile } from 'meticulous-typescript-profile'; -import { UILog } from '../../../services/uiLog'; import { catchError, timeout } from 'rxjs/operators'; import { of } from 'rxjs'; +import { HistoryListingEntry } from '@meticulous-home/espresso-api/dist/types'; +import moment from 'moment'; +import { BrewFlow, IBrewPressureFlow, IBrewRealtimeWaterFlow, IBrewTemperatureFlow, IBrewWeightFlow } from '../../brew/brewFlow'; declare var cordova; declare var io; @@ -25,8 +23,56 @@ export class MeticulousDevice extends PreparationDevice { private _profiles: Array = []; + private serverURL: string = ''; + public static returnBrewFlowForShotData(_shotData) { + const newMoment = moment(new Date()).startOf('day'); + const newBrewFlow = new BrewFlow(); + + for (const entry of _shotData as any) { + const shotEntry: any = entry.shot; + const shotEntryTime = newMoment.clone().add('milliseconds', entry.time); + const timestamp = shotEntryTime.format('HH:mm:ss.SSS'); + + const realtimeWaterFlow: IBrewRealtimeWaterFlow = + {} as IBrewRealtimeWaterFlow; + + realtimeWaterFlow.brew_time = ''; + realtimeWaterFlow.timestamp = timestamp; + realtimeWaterFlow.smoothed_weight = 0; + realtimeWaterFlow.flow_value = shotEntry.flow; + realtimeWaterFlow.timestampdelta = 0; + + newBrewFlow.realtimeFlow.push(realtimeWaterFlow); + + const brewFlow: IBrewWeightFlow = {} as IBrewWeightFlow; + brewFlow.timestamp = timestamp; + brewFlow.brew_time = ''; + brewFlow.actual_weight = shotEntry.weight; + brewFlow.old_weight = 0; + brewFlow.actual_smoothed_weight = 0; + brewFlow.old_smoothed_weight = 0; + brewFlow.not_mutated_weight = 0; + newBrewFlow.weight.push(brewFlow); + + const pressureFlow: IBrewPressureFlow = {} as IBrewPressureFlow; + pressureFlow.timestamp = timestamp; + pressureFlow.brew_time = ''; + pressureFlow.actual_pressure = shotEntry.pressure; + pressureFlow.old_pressure = 0; + newBrewFlow.pressureFlow.push(pressureFlow); + + const temperatureFlow: IBrewTemperatureFlow = {} as IBrewTemperatureFlow; + temperatureFlow.timestamp = timestamp; + temperatureFlow.brew_time = ''; + temperatureFlow.actual_temperature = shotEntry.temperature; + temperatureFlow.old_temperature = 0; + newBrewFlow.temperatureFlow.push(temperatureFlow); + } + return newBrewFlow; + } + constructor(protected httpClient: HttpClient, _preparation: Preparation) { super(httpClient, _preparation); this.meticulousShotData = undefined; @@ -48,7 +94,7 @@ export class MeticulousDevice extends PreparationDevice { }; this.httpClient .post( - this.serverURL + 'api/v1/history', + this.serverURL + '/api/v1/history', { sort: 'desc', max_results: 20, @@ -93,24 +139,50 @@ export class MeticulousDevice extends PreparationDevice { try { const profileResponse = await this.metApi.getProfile(_profileId); - if (profileResponse instanceof ApiResponseError) { + const profile = profileResponse.data as unknown as Profile; + return profile; + /* if (profileResponse instanceof Profile) { } else { - const profile = profileResponse.data as unknown as Profile; - return profile; - } + + }*/ } catch (ex) {} return undefined; } + public async getHistoryShortListing() { + + const response = await this.metApi.getHistoryShortListing(); + if (response && response.data && response.data.history) + { + return response.data.history as Array; + } + } + + + + public async searchHistory() { + const response = await this.metApi.searchHistory({ + query: '', + ids: [], + start_date: '', + end_date: '', + order_by: ['date'], + sort: 'desc', + max_results: 10, + dump_data: true + } ); + if (response && response.data && response.data.history) + { + return response.data.history as Array; + } + } + public async loadProfileByID(_profileId: string) { try { const loadProfile = await this.metApi.loadProfileByID(_profileId); + const profile = loadProfile.data as unknown as Profile; + return profile; - if (loadProfile instanceof ApiResponseError) { - } else { - const profile = loadProfile.data as unknown as Profile; - return profile; - } } catch (ex) { console.log(ex.message); } @@ -135,11 +207,8 @@ export class MeticulousDevice extends PreparationDevice { try { if (this._profiles.length <= 0) { const profiles = await this.metApi.listProfiles(); + this._profiles = profiles.data as Array; - if (profiles instanceof ApiResponseError) { - } else { - this._profiles = profiles.data as Array; - } } } catch (ex) {} } @@ -149,8 +218,9 @@ export class MeticulousDevice extends PreparationDevice { } public override async deviceConnected(): Promise { - const promise = new Promise((resolve, reject) => { - if (this.socket !== undefined) { + const promise = new Promise(async(resolve, reject) => { + const settings: any = await this.metApi.getSettings(); + if (settings?.data?.config) { resolve(true); } else { resolve(false); diff --git a/src/classes/preparationDevice/sanremo/sanremoYOUDevice.ts b/src/classes/preparationDevice/sanremo/sanremoYOUDevice.ts new file mode 100644 index 00000000..d110aa9d --- /dev/null +++ b/src/classes/preparationDevice/sanremo/sanremoYOUDevice.ts @@ -0,0 +1,187 @@ +import { PreparationDevice } from '../preparationDevice'; +import { HttpClient } from '@angular/common/http'; +import { Preparation } from '../../preparation/preparation'; + +import { ISanremoYOUParams } from '../../../interfaces/preparationDevices/sanremoYOU/iSanremoYOUParams'; +declare var cordova; +export class SanremoYOUDevice extends PreparationDevice { + public scriptList: Array<{ INDEX: number; TITLE: string }> = []; + + + private connectionURL: string =''; + constructor(protected httpClient: HttpClient, _preparation: Preparation) { + super(httpClient, _preparation); + + this.connectionURL =this.getPreparation().connectedPreparationDevice.url; + } + + public async deviceConnected(): Promise { + const promise = new Promise((resolve, reject) => { + const options = { + method: 'get', + }; + let urlAdding = '/api/runtime'; + + cordova.plugin.http.sendRequest( + this.connectionURL + urlAdding, + options, + (response) => { + try { + const parsedJSON = JSON.parse(response.data); + resolve(true); + return; + + if (parsedJSON && 'id' in parsedJSON) { + resolve(true); + } else { + reject(''); + } + } catch (e) { + // alert("Error in Resolve " + JSON.stringify(e)); + reject(JSON.stringify(e)); + } + }, + (response) => { + // prints 403 + // alert("Error " + JSON.stringify(response)); + reject(JSON.stringify(response)); + } + ); + }); + return promise; + } + + public getPressure() { + return this.pressure; + } + + public getResidualLagTime() { + const connectedPreparationDevice = + this.getPreparation().connectedPreparationDevice; + if ( + connectedPreparationDevice.customParams && + connectedPreparationDevice.customParams.residualLagTime + ) { + return connectedPreparationDevice.customParams.residualLagTime; + } else { + // Fixed value. + return 1.35; + } + } + + public getSaveLogfilesFromMachine(): boolean { + const connectedPreparationDevice = + this.getPreparation().connectedPreparationDevice; + if ( + connectedPreparationDevice.customParams && + connectedPreparationDevice.customParams.saveLogfilesFromMachine + ) { + return connectedPreparationDevice.customParams.saveLogfilesFromMachine; + } else { + // Fixed value. + return false; + } + } + + public getTemperature() { + return this.temperature; + } + public getDevicetemperature() { + return this.deviceTemperature; + } + + public fetchRuntimeData(_callback: any = null) { + const options = { + method: 'get', + }; + const urlAdding = '/api/runtime'; + + cordova.plugin.http.sendRequest( + this.connectionURL + urlAdding, + options, + (response) => { + try { + /**{"status":1,"description":"ON","statusPhase":0,"alarms":0,"warnings":2,"tempBoilerCoffe":82.1,"tempBolierServices":100.2,"pumpServicesPress":0.2,"pumpPress":0.0,"counterVol":0,"realtimeFlow":0,"setPressPaddle":0.0}**/ + const parsedJSON = JSON.parse(response.data); + const temp = parsedJSON.tempBoilerCoffe; + const press = parsedJSON.pumpPress*10; + + this.temperature = temp; + this.pressure = press; + if (_callback) { + _callback(); + } + } catch (e) {} + }, + (response) => { + // prints 403 + } + ); + } + + + + + public startShot() { + const promise = new Promise((resolve, reject) => { + const options = { + method: 'get', + }; + + let urlAdding = '/api/action/man'; + + cordova.plugin.http.sendRequest( + this.getPreparation().connectedPreparationDevice.url + urlAdding, + options, + (response) => { + try { + const parsedJSON = JSON.parse(response.data); + resolve(parsedJSON); + } catch (e) { + reject(JSON.stringify(e)); + } + }, + (response) => { + // prints 403 + reject(JSON.stringify(response)); + } + ); + }); + return promise; + } + public stopShot() { + const promise = new Promise((resolve, reject) => { + const options = { + method: 'get', + }; + + let urlAdding = '/api/action/man'; + + cordova.plugin.http.sendRequest( + this.getPreparation().connectedPreparationDevice.url + urlAdding, + options, + (response) => { + try { + const parsedJSON = JSON.parse(response.data); + resolve(parsedJSON); + } catch (e) { + reject(JSON.stringify(e)); + } + }, + (response) => { + // prints 403 + reject(JSON.stringify(response)); + } + ); + }); + return promise; + } +} + +export class SanremoYOUParams implements ISanremoYOUParams { + public stopAtWeight: number = 0; + public residualLagTime: number = 1.35; + constructor() { + this.residualLagTime = 1.35; + } +} diff --git a/src/classes/preparationDevice/xenia/xeniaDevice.ts b/src/classes/preparationDevice/xenia/xeniaDevice.ts index ef5d9578..2b699b24 100644 --- a/src/classes/preparationDevice/xenia/xeniaDevice.ts +++ b/src/classes/preparationDevice/xenia/xeniaDevice.ts @@ -337,6 +337,7 @@ export class XeniaParams implements IXeniaParams { public scriptAtFirstDripId: number = 0; public scriptAtWeightReachedId: number = 0; public scriptAtWeightReachedNumber: number = 0; + public residualLagTime: number = 0; constructor() {} } diff --git a/src/classes/settings/settings.ts b/src/classes/settings/settings.ts index fdb5c42c..6ff1711d 100755 --- a/src/classes/settings/settings.ts +++ b/src/classes/settings/settings.ts @@ -88,6 +88,16 @@ export class Settings implements ISettings { ARCHIVED: IBeanPageSort; FROZEN: IBeanPageSort; }; + public bean_collapsed: { + OPEN: boolean; + ARCHIVED: boolean; + FROZEN: boolean; + }; + public brew_collapsed: { + OPEN: boolean; + ARCHIVED: boolean; + }; + public green_bean_sort: { OPEN: IBeanPageSort; @@ -317,6 +327,15 @@ export class Settings implements ISettings { FROZEN: {} as IBeanPageSort, }; + this.bean_collapsed = { + OPEN: false, + ARCHIVED: false, + FROZEN: false, + }; + this.brew_collapsed = { + OPEN: false, + ARCHIVED: false, + }; this.green_bean_sort = { OPEN: {} as IBeanPageSort, ARCHIVED: {} as IBeanPageSort, @@ -505,14 +524,7 @@ export class Settings implements ISettings { ); } - public resetFilter() { - this.brew_filter = { - OPEN: {} as IBrewPageFilter, - ARCHIVED: {} as IBrewPageFilter, - }; - this.brew_filter.OPEN = this.GET_BREW_FILTER(); - this.brew_filter.ARCHIVED = this.GET_BREW_FILTER(); - + public resetBeanFilter() { this.bean_filter = { OPEN: {} as IBeanPageFilter, ARCHIVED: {} as IBeanPageFilter, @@ -521,7 +533,23 @@ export class Settings implements ISettings { this.bean_filter.OPEN = this.GET_BEAN_FILTER(); this.bean_filter.ARCHIVED = this.GET_BEAN_FILTER(); this.bean_filter.FROZEN = this.GET_BEAN_FILTER(); + } + + public resetBrewFilter() { + this.brew_filter = { + OPEN: {} as IBrewPageFilter, + ARCHIVED: {} as IBrewPageFilter, + }; + this.brew_filter.OPEN = this.GET_BREW_FILTER(); + this.brew_filter.ARCHIVED = this.GET_BREW_FILTER(); + } + + public resetFilter() { + this.resetBrewFilter(); + this.resetBeanFilter(); + } + public resetBeanSort() { this.bean_sort = { OPEN: {} as IBeanPageSort, ARCHIVED: {} as IBeanPageSort, diff --git a/src/components/bean-information/bean-information.component.html b/src/components/bean-information/bean-information.component.html index a4831a2f..8b4a5cd3 100644 --- a/src/components/bean-information/bean-information.component.html +++ b/src/components/bean-information/bean-information.component.html @@ -43,14 +43,15 @@ - +
+
- + @@ -144,7 +145,7 @@ {{"BEANS_AMOUNT_USED" | translate}}
- {{daysOld()}}
diff --git a/src/components/bean-information/bean-information.component.ts b/src/components/bean-information/bean-information.component.ts index ecc2130f..3f3617cd 100644 --- a/src/components/bean-information/bean-information.component.ts +++ b/src/components/bean-information/bean-information.component.ts @@ -51,7 +51,7 @@ export class BeanInformationComponent implements OnInit { @Input() public bean: Bean; @Input() public showActions: boolean = true; @Input() public disabled: boolean = false; - + @Input() public collapsed: boolean = false; @ViewChild('card', { read: ElementRef }) public cardEl: ElementRef; @ViewChild('beanStars', { read: NgxStarsComponent, static: false }) @@ -498,7 +498,7 @@ export class BeanInformationComponent implements OnInit { private async resetSettings() { const settings: Settings = this.uiSettingsStorage.getSettings(); - settings.resetFilter(); + settings.resetBeanFilter(); await this.uiSettingsStorage.saveSettings(settings); } diff --git a/src/components/beans/bean-freeze-information/bean-freeze-information.component.html b/src/components/beans/bean-freeze-information/bean-freeze-information.component.html index 4238b14a..175b0079 100644 --- a/src/components/beans/bean-freeze-information/bean-freeze-information.component.html +++ b/src/components/beans/bean-freeze-information/bean-freeze-information.component.html @@ -1,10 +1,10 @@ - + - + - - - - - - - - -
- -
-
- -
- -
-
- - - - - {{brew.config.unix_timestamp | formatDate:[settings?.date_format]}}{{brew.config.unix_timestamp | formatDate:["HH:mm"]}} - - - - - - - - -
{{bean?.name}} ({{bean?.roaster}})
- + + + + + + + + +
+ +
+
+ +
+ +
+
+ + + + + + + + + {{ brew.config.unix_timestamp | formatDate:[settings?.date_format] }}{{ brew.config.unix_timestamp | formatDate:["HH:mm"] }} + + + + + + + + + +
+ + () + + {{ bean?.name }} ({{ bean?.roaster }}) +
+
+ - - - + + + + {{ this.uiHelper.toFixedIfNecessary(brew.rating, 2) }} + +
+
+
+
+
+ + +
+
+
+
+ + + + {{ "BREW_DATA_PREPARATION_METHOD" | translate }} +
+ {{ preparation?.name }} +
+ - - {{brew?.grind_weight | number : '.0-2'}}gr / {{brew.brew_beverage_quantity | number : '.0-2'}}{{brewQuantityEnum[brew?.brew_beverage_quantity_type]}} ({{brew?.getBrewRatio()}}) - - - +
+ {{ mill?.name }} +
+ + {{ "BREW_DATA_IN_OUT_BR" | translate }} +
+ + {{ brew?.grind_weight | number : '.0-2' }}gr + / {{ brew.brew_quantity | number : '.0-2' }}{{ brewQuantityEnum[brew?.brew_quantity_type] }} ({{ brew?.getBrewRatio() }} + ) + + + {{ brew?.grind_weight | number : '.0-2' }}gr + / {{ brew.brew_beverage_quantity | number : '.0-2' }}{{ brewQuantityEnum[brew?.brew_beverage_quantity_type] }} ({{ brew?.getBrewRatio() }} + ) + +
+
- {{brew?.getFormattedTotalCoffeeBrewTime()}} -
- 0 && uiBrewHelper.fieldVisible(settings.visible_list_view_parameters.mill_speed, preparation?.visible_list_view_parameters.mill_speed, - preparation?.use_custom_parameters))" size="6"> - {{'BREW_DATA_GRIND_SIZE' | translate}}
- + {{ 'BREW_DATA_GRIND_SIZE' | translate }} +
+ {{brew?.grind_size}}{{ brew?.grind_size }}
- {{brew?.tds}}, %{{brew?.getExtractionYield()}} -
- - {{'BREW_DATA_BREW_TEMPERATURE' | translate}}
- {{brew?.brew_temperature}} -
- - {{'BREW_DATA_PRESSURE_PROFILE' | translate}}
- {{brew?.pressure_profile}} -
- - {{"BREW_DATA_BEAN_WEIGHT_IN" | translate}}
- {{brew?.bean_weight_in}} -
- +
+ {{ brew?.bean_weight_in }} +
+ + {{ "BREW_DATA_MILL_TIMER" | translate }} +
+ {{ brew?.mill_timer }} +
+ - {{"BREW_DATA_VESSEL_NAME_WEIGHT" | translate}}
- {{brew?.vessel_name}} / {{brew?.vessel_weight}} -
- +
+ {{ brew?.vessel_name }} / {{ brew?.vessel_weight }} +
+ + {{ "BREW_DATA_TEMPERATURE_TIME" | translate }} +
+ {{ brew?.getFormattedTotalCoffeeTemperatureTime() }} +
+ + {{ "BREW_DATA_COFFEE_BLOOMING_TIME" | translate }} +
+ {{ brew?.getFormattedTotalCoffeeBloomingTime() }} +
+ 0)'> + {{ "BREW_DATA_COFFEE_FIRST_DRIP_TIME" | translate }} +
+ {{ brew?.getFormattedTotalCoffeeFirstDripTime() }} +
+ - {{"BREW_DATA_WATER" | translate}}
- {{brew?.getWater().name}} -
- +
+ {{ brew?.getWater().name }} +
+ - {{"BREW_DATA_COFFEE_TYPE" | translate}}
- {{brew?.coffee_type}} -
- +
+ {{ brew?.coffee_type }} +
+ - {{"BREW_DATA_COFFEE_CONCENTRATION" | translate}}
- {{brew?.coffee_concentration}} -
- +
+ {{ brew?.coffee_concentration }} +
+ + {{ "BREW_INFORMATION_BEAN_AGE" | translate }} +
+ {{ brew?.getCalculatedBeanAge() }} +
+
- {{getCuppedBrewFlavors()}} -
- +
+ {{ brew?.getPreparationToolName(uuid) }} + +
+
+ + {{ 'BREW_DATA_FLAVOR' | translate }} +
+ {{ getCuppedBrewFlavors() }} +
+ - {{"BREW_DATA_NOTES" | translate}}
-
{{brew?.note}}
-
+ {{ "BREW_DATA_NOTES" | translate }} +
+ +
{{ brew?.note }}
+
+
-
-
-
-
-
+
+
+
+
+
- - - - - + + + + + -
-
- {{brew.config.unix_timestamp | formatDate:["DD"]}}.
- - {{brew.config.unix_timestamp | formatDate:["MMMM"]}}
{{brew.config.unix_timestamp | formatDate:["YYYY"]}} +
+
+ {{ brew.config.unix_timestamp | formatDate:["DD"] }}.
+ + {{ brew.config.unix_timestamp | formatDate:["MMMM"] }} +
{{ brew.config.unix_timestamp | formatDate:["YYYY"] }}
-
+
-
-
- - -
- - - - - - - {{bean?.name}} - - - - {{preparation?.name}} - - - - - {{mill?.name}} - - - - - - - - - - - - - -
-
- - +
+
+
+ +
+ + + + + + + + + + + () + + {{ bean?.name }} + + + + + {{ preparation?.name }} + + + + + {{ mill?.name }} + + + + + + + + + + + + + + +
+
+
+
diff --git a/src/components/brew-information/brew-information.component.ts b/src/components/brew-information/brew-information.component.ts index e333a98f..28d1a2b5 100644 --- a/src/components/brew-information/brew-information.component.ts +++ b/src/components/brew-information/brew-information.component.ts @@ -33,7 +33,7 @@ import { TranslateService } from '@ngx-translate/core'; import { BrewTrackingService } from '../../services/brewTracking/brew-tracking.service'; import { UIHealthKit } from '../../services/uiHealthKit'; import * as htmlToImage from 'html-to-image'; -import { Visualizer } from '../../classes/visualizer/visualizer'; + import { UIFileHelper } from '../../services/uiFileHelper'; import { BrewFlow } from '../../classes/brew/brewFlow'; @@ -48,6 +48,7 @@ declare var window; }) export class BrewInformationComponent implements OnInit { @Input() public brew: Brew; + @Input() public collapsed: boolean = false; @Input() public layout: string = 'brew'; @ViewChild('card', { read: ElementRef }) public cardEl: ElementRef; @@ -80,7 +81,6 @@ export class BrewInformationComponent implements OnInit { private readonly uiHealthKit: UIHealthKit, private readonly platform: Platform, private readonly uiFileHelper: UIFileHelper, - private readonly uiBeanStorage: UIBeanStorage, private readonly uiBeanHelper: UIBeanHelper, private readonly visualizerService: VisualizerService ) {} diff --git a/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts b/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts index 7aa9f234..d1800295 100644 --- a/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts +++ b/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts @@ -8,19 +8,15 @@ import { NgZone, OnInit, Output, - ViewChild, + ViewChild } from '@angular/core'; import { PREPARATION_STYLE_TYPE } from '../../../enums/preparations/preparationStyleTypes'; import { PreparationDeviceType } from '../../../classes/preparationDevice'; -import { - BluetoothScale, - SCALE_TIMER_COMMAND, - ScaleType, -} from '../../../classes/devices'; +import { BluetoothScale, SCALE_TIMER_COMMAND, ScaleType } from '../../../classes/devices'; import { ModalController, Platform } from '@ionic/angular'; import { CoffeeBluetoothDevicesService, - CoffeeBluetoothServiceEvent, + CoffeeBluetoothServiceEvent } from '../../../services/coffeeBluetoothDevices/coffee-bluetooth-devices.service'; import { TemperatureDevice } from '../../../classes/devices/temperatureBluetoothDevice'; import { PressureDevice } from '../../../classes/devices/pressureBluetoothDevice'; @@ -33,7 +29,7 @@ import { IBrewRealtimeWaterFlow, IBrewTemperatureFlow, IBrewWaterFlow, - IBrewWeightFlow, + IBrewWeightFlow } from '../../../classes/brew/brewFlow'; import { Preparation } from '../../../classes/preparation/preparation'; import { UIPreparationStorage } from '../../../services/uiPreparationStorage'; @@ -62,13 +58,14 @@ import { Graph } from '../../../classes/graph/graph'; import { UIGraphStorage } from '../../../services/uiGraphStorage.service'; import regression from 'regression'; import { TextToSpeechService } from '../../../services/textToSpeech/text-to-speech.service'; +import { SanremoYOUDevice } from '../../../classes/preparationDevice/sanremo/sanremoYOUDevice'; declare var Plotly; @Component({ selector: 'brew-brewing-graph', templateUrl: './brew-brewing-graph.component.html', - styleUrls: ['./brew-brewing-graph.component.scss'], + styleUrls: ['./brew-brewing-graph.component.scss'] }) export class BrewBrewingGraphComponent implements OnInit { @ViewChild('smartScaleWeight', { read: ElementRef }) @@ -133,6 +130,7 @@ export class BrewBrewingGraphComponent implements OnInit { private temperatureThresholdWasHit: boolean = false; private xeniaOverviewInterval: any = undefined; private meticulousInterval: any = undefined; + private sanremoYOUFetchingInterval: any = undefined; public lastChartLayout: any = undefined; public lastChartRenderingInstance: number = 0; @@ -153,6 +151,7 @@ export class BrewBrewingGraphComponent implements OnInit { public textToSpeechWeightInterval: any = undefined; public textToSpeechTimerInterval: any = undefined; + constructor( private readonly platform: Platform, private readonly bleManager: CoffeeBluetoothDevicesService, @@ -172,7 +171,8 @@ export class BrewBrewingGraphComponent implements OnInit { public readonly uiBrewHelper: UIBrewHelper, private readonly uiGraphStorage: UIGraphStorage, private readonly textToSpeech: TextToSpeechService - ) {} + ) { + } public ngOnInit() { this.settings = this.uiSettingsStorage.getSettings(); @@ -205,7 +205,7 @@ export class BrewBrewingGraphComponent implements OnInit { if ( this.pressureDeviceConnected() && this.data.getPreparation().style_type === - PREPARATION_STYLE_TYPE.ESPRESSO + PREPARATION_STYLE_TYPE.ESPRESSO ) { await this.__connectPressureDevice(true); isSomethingConnected = true; @@ -217,7 +217,7 @@ export class BrewBrewingGraphComponent implements OnInit { if ( this.brewComponent?.brewBrewingPreparationDeviceEl?.preparationDeviceConnected() && this.brewComponent?.brewBrewingPreparationDeviceEl?.getPreparationDeviceType() === - PreparationDeviceType.METICULOUS + PreparationDeviceType.METICULOUS ) { isSomethingConnected = true; } @@ -360,7 +360,7 @@ export class BrewBrewingGraphComponent implements OnInit { this.temperatureDeviceConnected() === true || (this.pressureDeviceConnected() === true && this.data.getPreparation()?.style_type === - PREPARATION_STYLE_TYPE.ESPRESSO) || + PREPARATION_STYLE_TYPE.ESPRESSO) || this.brewComponent?.brewBrewingPreparationDeviceEl ?.preparationDevice !== undefined ) { @@ -393,7 +393,8 @@ export class BrewBrewingGraphComponent implements OnInit { this.bleManager.getPressureDevice(); try { pressureDevice.updateZero(); - } catch (ex) {} + } catch (ex) { + } } } @@ -401,7 +402,7 @@ export class BrewBrewingGraphComponent implements OnInit { if ( this.brewComponent?.brewBrewingPreparationDeviceEl?.preparationDeviceConnected() && this.brewComponent?.brewBrewingPreparationDeviceEl?.getPreparationDeviceType() === - PreparationDeviceType.METICULOUS + PreparationDeviceType.METICULOUS ) { return 3; } @@ -493,7 +494,8 @@ export class BrewBrewingGraphComponent implements OnInit { setTimeout(() => { try { Plotly.purge(this.profileDiv.nativeElement); - } catch (ex) {} + } catch (ex) { + } this.graphSettings = this.uiHelper.cloneData(this.settings.graph.FILTER); if ( this.data.getPreparation().style_type === @@ -526,11 +528,11 @@ export class BrewBrewingGraphComponent implements OnInit { line: { shape: 'linear', color: '#ebe6dd', - width: 2, + width: 2 }, visible: this.graphSettings.weight, hoverinfo: this.isDetail ? 'all' : 'skip', - showlegend: false, + showlegend: false }; this.flowPerSecondTraceReference = { x: [], @@ -542,11 +544,11 @@ export class BrewBrewingGraphComponent implements OnInit { line: { shape: 'linear', color: '#cbd5d9', - width: 2, + width: 2 }, visible: this.graphSettings.calc_flow, hoverinfo: this.isDetail ? 'all' : 'skip', - showlegend: false, + showlegend: false }; this.realtimeFlowTraceReference = { @@ -559,11 +561,11 @@ export class BrewBrewingGraphComponent implements OnInit { line: { shape: 'linear', color: '#9cb5be', - width: 2, + width: 2 }, visible: this.graphSettings.realtime_flow, hoverinfo: this.isDetail ? 'all' : 'skip', - showlegend: false, + showlegend: false }; this.pressureTraceReference = { @@ -576,11 +578,11 @@ export class BrewBrewingGraphComponent implements OnInit { line: { shape: 'linear', color: '#9be8d3', - width: 2, + width: 2 }, visible: this.graphSettings.pressure, hoverinfo: this.isDetail ? 'all' : 'skip', - showlegend: false, + showlegend: false }; this.temperatureTraceReference = { @@ -593,11 +595,11 @@ export class BrewBrewingGraphComponent implements OnInit { line: { shape: 'linear', color: '#eaad9f', - width: 2, + width: 2 }, visible: this.graphSettings.temperature, hoverinfo: this.isDetail ? 'all' : 'skip', - showlegend: false, + showlegend: false }; const presetFlowProfile = this.reference_profile_raw; @@ -629,7 +631,7 @@ export class BrewBrewingGraphComponent implements OnInit { this.weightTraceReference.x.push( new Date( moment(data.timestamp, 'HH:mm:ss.SSS').toDate().getTime() - - delay + delay ) ); this.weightTraceReference.y.push(data.actual_weight); @@ -638,7 +640,7 @@ export class BrewBrewingGraphComponent implements OnInit { this.flowPerSecondTraceReference.x.push( new Date( moment(data.timestamp, 'HH:mm:ss.SSS').toDate().getTime() - - delay + delay ) ); this.flowPerSecondTraceReference.y.push(data.value); @@ -648,7 +650,7 @@ export class BrewBrewingGraphComponent implements OnInit { this.realtimeFlowTraceReference.x.push( new Date( moment(data.timestamp, 'HH:mm:ss.SSS').toDate().getTime() - - delay + delay ) ); this.realtimeFlowTraceReference.y.push(data.flow_value); @@ -664,7 +666,7 @@ export class BrewBrewingGraphComponent implements OnInit { this.pressureTraceReference.x.push( new Date( moment(data.timestamp, 'HH:mm:ss.SSS').toDate().getTime() - - delay + delay ) ); this.pressureTraceReference.y.push(data.actual_pressure); @@ -679,7 +681,7 @@ export class BrewBrewingGraphComponent implements OnInit { this.temperatureTraceReference.x.push( new Date( moment(data.timestamp, 'HH:mm:ss.SSS').toDate().getTime() - - delay + delay ) ); this.temperatureTraceReference.y.push(data.actual_temperature); @@ -698,11 +700,11 @@ export class BrewBrewingGraphComponent implements OnInit { line: { shape: 'linear', color: '#cdc2ac', - width: 2, + width: 2 }, visible: this.graphSettings.weight, hoverinfo: this.isDetail ? 'all' : 'skip', - showlegend: false, + showlegend: false }; this.flowPerSecondTrace = { x: [], @@ -714,11 +716,11 @@ export class BrewBrewingGraphComponent implements OnInit { line: { shape: 'linear', color: '#7F97A2', - width: 2, + width: 2 }, visible: this.graphSettings.calc_flow, hoverinfo: this.isDetail ? 'all' : 'skip', - showlegend: false, + showlegend: false }; this.realtimeFlowTrace = { @@ -731,11 +733,11 @@ export class BrewBrewingGraphComponent implements OnInit { line: { shape: 'linear', color: '#09485D', - width: 2, + width: 2 }, visible: this.graphSettings.realtime_flow, hoverinfo: this.isDetail ? 'all' : 'skip', - showlegend: false, + showlegend: false }; this.pressureTrace = { @@ -748,11 +750,11 @@ export class BrewBrewingGraphComponent implements OnInit { line: { shape: 'linear', color: '#05C793', - width: 2, + width: 2 }, visible: this.graphSettings.pressure, hoverinfo: this.isDetail ? 'all' : 'skip', - showlegend: false, + showlegend: false }; this.temperatureTrace = { @@ -765,11 +767,11 @@ export class BrewBrewingGraphComponent implements OnInit { line: { shape: 'linear', color: '#CC3311', - width: 2, + width: 2 }, visible: this.graphSettings.temperature, hoverinfo: this.isDetail ? 'all' : 'skip', - showlegend: false, + showlegend: false }; if ( @@ -796,7 +798,7 @@ export class BrewBrewingGraphComponent implements OnInit { this.weightTrace.x.push( new Date( moment(data.timestamp, 'HH:mm:ss.SSS').toDate().getTime() - - delay + delay ) ); this.weightTrace.y.push(data.actual_weight); @@ -805,7 +807,7 @@ export class BrewBrewingGraphComponent implements OnInit { this.flowPerSecondTrace.x.push( new Date( moment(data.timestamp, 'HH:mm:ss.SSS').toDate().getTime() - - delay + delay ) ); this.flowPerSecondTrace.y.push(data.value); @@ -815,7 +817,7 @@ export class BrewBrewingGraphComponent implements OnInit { this.realtimeFlowTrace.x.push( new Date( moment(data.timestamp, 'HH:mm:ss.SSS').toDate().getTime() - - delay + delay ) ); this.realtimeFlowTrace.y.push(data.flow_value); @@ -830,7 +832,7 @@ export class BrewBrewingGraphComponent implements OnInit { this.pressureTrace.x.push( new Date( moment(data.timestamp, 'HH:mm:ss.SSS').toDate().getTime() - - delay + delay ) ); this.pressureTrace.y.push(data.actual_pressure); @@ -844,7 +846,7 @@ export class BrewBrewingGraphComponent implements OnInit { this.temperatureTrace.x.push( new Date( moment(data.timestamp, 'HH:mm:ss.SSS').toDate().getTime() - - delay + delay ) ); this.temperatureTrace.y.push(data.actual_temperature); @@ -887,7 +889,8 @@ export class BrewBrewingGraphComponent implements OnInit { this.updateChart(); } }, 250); - } catch (ex) {} + } catch (ex) { + } }, timeout); } @@ -908,7 +911,8 @@ export class BrewBrewingGraphComponent implements OnInit { weightEl.textContent = this.data.brew_quantity + ' g'; } } - } catch (ex) {} + } catch (ex) { + } }, 350); } @@ -916,7 +920,8 @@ export class BrewBrewingGraphComponent implements OnInit { let chartWidth: number = 300; try { chartWidth = this.canvaContainer.nativeElement.offsetWidth; - } catch (ex) {} + } catch (ex) { + } const chartHeight: number = 150; const tickFormat = '%M:%S'; @@ -985,7 +990,7 @@ export class BrewBrewingGraphComponent implements OnInit { r: 20, b: 20, t: 20, - pad: 2, + pad: 2 }, showlegend: false, dragmode: false, @@ -999,7 +1004,7 @@ export class BrewBrewingGraphComponent implements OnInit { domain: [0, 1], fixedrange: true, type: 'date', - range: [startRange, endRange], + range: [startRange, endRange] }, yaxis: { title: '', @@ -1009,7 +1014,7 @@ export class BrewBrewingGraphComponent implements OnInit { side: 'left', position: 0.03, rangemode: 'nonnegative', - range: [suggestedMinWeight, suggestedMaxWeight], + range: [suggestedMinWeight, suggestedMaxWeight] }, yaxis2: { title: '', @@ -1022,14 +1027,14 @@ export class BrewBrewingGraphComponent implements OnInit { position: 0.97, fixedrange: true, rangemode: 'nonnegative', - range: [suggestedMinFlow, suggestedMaxFlow], - }, + range: [suggestedMinFlow, suggestedMaxFlow] + } }; const pressureDevice = this.bleManager.getPressureDevice(); if ( (pressureDevice != null && this.getPreparation().style_type === - PREPARATION_STYLE_TYPE.ESPRESSO) || + PREPARATION_STYLE_TYPE.ESPRESSO) || this.brewComponent?.brewBrewingPreparationDeviceEl?.preparationDeviceConnected() || !this.platform.is('cordova') ) { @@ -1043,7 +1048,7 @@ export class BrewBrewingGraphComponent implements OnInit { showgrid: false, position: 0.91, fixedrange: true, - range: [0, 10], + range: [0, 10] }; } const temperatureDevice = this.bleManager.getTemperatureDevice(); @@ -1063,7 +1068,7 @@ export class BrewBrewingGraphComponent implements OnInit { position: 0.8, fixedrange: true, visible: false, - range: [0, 100], + range: [0, 100] }; } } else { @@ -1075,14 +1080,14 @@ export class BrewBrewingGraphComponent implements OnInit { r: 20, b: 20, t: 20, - pad: 2, + pad: 2 }, showlegend: false, xaxis: { tickformat: tickFormat, visible: true, domain: [0, 1], - type: 'date', + type: 'date' }, yaxis: { title: '', @@ -1090,7 +1095,7 @@ export class BrewBrewingGraphComponent implements OnInit { tickfont: { color: '#cdc2ac' }, side: 'left', position: 0.05, - visible: true, + visible: true }, yaxis2: { title: '', @@ -1101,8 +1106,8 @@ export class BrewBrewingGraphComponent implements OnInit { side: 'right', position: 0.95, showgrid: false, - visible: true, - }, + visible: true + } }; layout['yaxis4'] = { @@ -1115,7 +1120,7 @@ export class BrewBrewingGraphComponent implements OnInit { showgrid: false, position: 0.93, range: [0, 12], - visible: true, + visible: true }; layout['yaxis5'] = { @@ -1129,7 +1134,7 @@ export class BrewBrewingGraphComponent implements OnInit { position: 0.8, fixedrange: true, range: [0, 100], - visible: true, + visible: true }; if (this.weightTrace.x && this.weightTrace.x.length > 0) { @@ -1159,14 +1164,14 @@ export class BrewBrewingGraphComponent implements OnInit { if (this.isDetail === true) { const config = { displayModeBar: false, // this is the line that hides the bar. - responsive: true, + responsive: true }; return config; } else { const config = { responsive: false, scrollZoom: false, - displayModeBar: false, // this is the line that hides the bar. + displayModeBar: false // this is the line that hides the bar. }; return config; } @@ -1250,7 +1255,8 @@ export class BrewBrewingGraphComponent implements OnInit { this.brewComponent.timer.reset(); this.checkChanges(); }, - () => {} + () => { + } ); break; @@ -1332,8 +1338,8 @@ export class BrewBrewingGraphComponent implements OnInit { try { return this.uiHelper.toFixedIfNecessary( this.flow_profile_raw.realtimeFlow[ - this.flow_profile_raw.realtimeFlow.length - 1 - ].flow_value, + this.flow_profile_raw.realtimeFlow.length - 1 + ].flow_value, 2 ); } catch (ex) { @@ -1345,7 +1351,7 @@ export class BrewBrewingGraphComponent implements OnInit { this.ngZone.runOutsideAngular(() => { if (this.brewComponent.maximizeFlowGraphIsShown === true) { this.brewComponent.brewTemperatureGraphSubject.next({ - temperature: _temperature, + temperature: _temperature }); } @@ -1353,7 +1359,8 @@ export class BrewBrewingGraphComponent implements OnInit { const temperatureEl = this.temperatureEl.nativeElement; temperatureEl.textContent = _temperature; - } catch (ex) {} + } catch (ex) { + } }); } @@ -1361,7 +1368,7 @@ export class BrewBrewingGraphComponent implements OnInit { this.ngZone.runOutsideAngular(() => { if (this.brewComponent.maximizeFlowGraphIsShown === true) { this.brewComponent.brewPressureGraphSubject.next({ - pressure: _pressure, + pressure: _pressure }); } @@ -1369,7 +1376,8 @@ export class BrewBrewingGraphComponent implements OnInit { const pressureEl = this.pressureEl.nativeElement; pressureEl.textContent = _pressure; - } catch (ex) {} + } catch (ex) { + } }); } @@ -1398,7 +1406,7 @@ export class BrewBrewingGraphComponent implements OnInit { this.brewComponent.brewFlowGraphSubject.next({ scaleWeight: actualScaleWeight, smoothedWeight: actualSmoothedWeightPerSecond, - avgFlow: avgFlow, + avgFlow: avgFlow }); } @@ -1410,7 +1418,8 @@ export class BrewBrewingGraphComponent implements OnInit { weightEl.textContent = actualScaleWeight + ' g'; flowEl.textContent = actualSmoothedWeightPerSecond + ' g/s'; avgFlowEl.textContent = 'Ø ' + avgFlow + ' g/s'; - } catch (ex) {} + } catch (ex) { + } }); } @@ -1446,7 +1455,7 @@ export class BrewBrewingGraphComponent implements OnInit { this.profileDiv.nativeElement, { x: xData, - y: yData, + y: yData }, tracesData ); @@ -1598,7 +1607,8 @@ export class BrewBrewingGraphComponent implements OnInit { this.lastChartLayout.xaxis.range = [delay, delayedTime]; Plotly.relayout(this.profileDiv.nativeElement, this.lastChartLayout); } - } catch (ex) {} + } catch (ex) { + } }); } @@ -1686,7 +1696,7 @@ export class BrewBrewingGraphComponent implements OnInit { temperatureDevice || (pressureDevice && this.data.getPreparation().style_type === - PREPARATION_STYLE_TYPE.ESPRESSO) + PREPARATION_STYLE_TYPE.ESPRESSO) ) { this.initializeFlowChart(false); } @@ -1750,9 +1760,10 @@ export class BrewBrewingGraphComponent implements OnInit { if ( this.brewComponent?.brewBrewingPreparationDeviceEl?.preparationDeviceConnected() && this.brewComponent?.brewBrewingPreparationDeviceEl?.getPreparationDeviceType() === - PreparationDeviceType.XENIA && + PreparationDeviceType.XENIA && this.machineStopScriptWasTriggered === false ) { + this.machineStopScriptWasTriggered = true; // If the event is not xenia, we pressed buttons, if the event was triggerd by xenia, timer already stopped. // If we press pause, stop scripts. this.uiLog.log(`Xenia Script - Pause button pressed, stop script`); @@ -1777,7 +1788,27 @@ export class BrewBrewingGraphComponent implements OnInit { if ( this.brewComponent?.brewBrewingPreparationDeviceEl?.preparationDeviceConnected() && this.brewComponent?.brewBrewingPreparationDeviceEl?.getPreparationDeviceType() === - PreparationDeviceType.METICULOUS && + PreparationDeviceType.SANREMO_YOU && + _event !== 'sanremo_you' && this.machineStopScriptWasTriggered === false + ) { + this.machineStopScriptWasTriggered = true; + this.uiLog.log(`Sanremo YOU - Pause button pressed, stop shot`); + const prepDeviceCall: SanremoYOUDevice = this.brewComponent + ?.brewBrewingPreparationDeviceEl?.preparationDevice as SanremoYOUDevice; + prepDeviceCall.stopShot().catch((_msg) => { + this.uiToast.showInfoToast( + 'We could not stop - manual triggered: ' + _msg, + false + ); + this.uiLog.log('We could not stop - manual triggered: ' + _msg); + }); + this.stopFetchingDataFromSanremoYOU(); + } + + if ( + this.brewComponent?.brewBrewingPreparationDeviceEl?.preparationDeviceConnected() && + this.brewComponent?.brewBrewingPreparationDeviceEl?.getPreparationDeviceType() === + PreparationDeviceType.METICULOUS && _event !== 'meticulous' ) { this.stopFetchingDataFromMeticulous(); @@ -1826,6 +1857,35 @@ export class BrewBrewingGraphComponent implements OnInit { } } + public startFetchingDataFromSanremoYOU() { + const prepDeviceCall: SanremoYOUDevice = this.brewComponent + .brewBrewingPreparationDeviceEl.preparationDevice as SanremoYOUDevice; + + this.stopFetchingDataFromSanremoYOU(); + const setSanremoData = () => { + const temp = prepDeviceCall.getTemperature(); + const press = prepDeviceCall.getPressure(); + this.__setPressureFlow({ actual: press, old: press }); + this.__setTemperatureFlow({ actual: temp, old: temp }); + }; + prepDeviceCall.fetchRuntimeData(() => { + // before we start the interval, we fetch the data once to overwrite, and set them. + setSanremoData(); + }); + + this.sanremoYOUFetchingInterval = setInterval(async () => { + try { + // We don't use the callback function to make sure we don't have to many performance issues + prepDeviceCall.fetchRuntimeData(() => { + //before we start the interval, we fetch the data once to overwrite, and set them. + setSanremoData(); + }); + } catch (ex) { + } + }, 100); + + } + public startFetchingDataFromMeticulous() { const prepDeviceCall: MeticulousDevice = this.brewComponent .brewBrewingPreparationDeviceEl.preparationDevice as MeticulousDevice; @@ -1881,18 +1941,18 @@ export class BrewBrewingGraphComponent implements OnInit { if (hasShotStarted) { this.__setPressureFlow({ actual: shotData.pressure, - old: shotData.pressure, + old: shotData.pressure }); this.__setTemperatureFlow({ actual: shotData.temperature, - old: shotData.temperature, + old: shotData.temperature }); this.__setFlowProfile({ actual: shotData.weight, old: shotData.oldWeight, smoothed: shotData.smoothedWeight, - oldSmoothed: shotData.oldSmoothedWeight, + oldSmoothed: shotData.oldSmoothedWeight }); //this.__setMachineWeightFlow({ actual: shotData.weight, old: shotData.weight,smoothed:100,oldSmoothed:100 }); @@ -1939,7 +1999,8 @@ export class BrewBrewingGraphComponent implements OnInit { 2 ); } - } catch (ex) {} + } catch (ex) { + } }); }, 100); @@ -1950,7 +2011,8 @@ export class BrewBrewingGraphComponent implements OnInit { //before we start the interval, we fetch the data once to overwrite, and set them. setTempAndPressure(); }); - } catch (ex) {} + } catch (ex) { + } }, 500); } @@ -1961,6 +2023,13 @@ export class BrewBrewingGraphComponent implements OnInit { } } + public stopFetchingDataFromSanremoYOU() { + if (this.sanremoYOUFetchingInterval !== undefined) { + clearInterval(this.sanremoYOUFetchingInterval); + this.sanremoYOUFetchingInterval = undefined; + } + } + public stopFetchingDataFromMeticulous() { if (this.meticulousInterval !== undefined) { clearInterval(this.meticulousInterval); @@ -2081,7 +2150,7 @@ export class BrewBrewingGraphComponent implements OnInit { actual: weight, old: 1, smoothed: 1, - oldSmoothed: 1, + oldSmoothed: 1 }); this.setActualSmartInformation(); }, 100); @@ -2101,7 +2170,7 @@ export class BrewBrewingGraphComponent implements OnInit { } else { if ( this.data.getPreparation().style_type === - PREPARATION_STYLE_TYPE.ESPRESSO && + PREPARATION_STYLE_TYPE.ESPRESSO && pressureDevice ) { this.brewComponent.maximizeFlowGraph(); @@ -2163,7 +2232,7 @@ export class BrewBrewingGraphComponent implements OnInit { if ( this.brewComponent?.brewBrewingPreparationDeviceEl?.preparationDeviceConnected() && this.brewComponent?.brewBrewingPreparationDeviceEl?.getPreparationDeviceType() === - PreparationDeviceType.XENIA + PreparationDeviceType.XENIA ) { if (this.data.preparationDeviceBrew.params.scriptStartId > 0) { this.uiLog.log(`Xenia Script - Script start - Trigger script`); @@ -2184,7 +2253,7 @@ export class BrewBrewingGraphComponent implements OnInit { (_jsonResp: any) => { this.uiLog.log( 'Check of xenia answer if script started MA_STATUS: ' + - _jsonResp.MA_Status + _jsonResp.MA_Status ); if (_jsonResp.MA_STATUS === 3) { //Great we started. @@ -2223,38 +2292,58 @@ export class BrewBrewingGraphComponent implements OnInit { } else if ( this.brewComponent?.brewBrewingPreparationDeviceEl?.preparationDeviceConnected() && this.brewComponent?.brewBrewingPreparationDeviceEl?.getPreparationDeviceType() === - PreparationDeviceType.METICULOUS + PreparationDeviceType.METICULOUS ) { + const prepDeviceCall: MeticulousDevice = this.brewComponent + .brewBrewingPreparationDeviceEl.preparationDevice as MeticulousDevice; + + if (this.data.preparationDeviceBrew.params.chosenProfileId !== '') { + this.uiLog.log( + `A Meticulous profile was choosen, execute it - ${this.data.preparationDeviceBrew.params.chosenProfileId}` + ); + await prepDeviceCall.loadProfileByID( + this.data.preparationDeviceBrew.params.chosenProfileId + ); + await prepDeviceCall.startExecute(); + } else { + this.uiLog.log( + 'No Meticulous profile was selected, just listen for the start' + ); + } + if ( - this.brewComponent?.brewBrewingPreparationDeviceEl?.getPreparationDeviceType() === - PreparationDeviceType.METICULOUS + this.settings.bluetooth_scale_maximize_on_start_timer === true && + this.brewComponent.maximizeFlowGraphIsShown === false ) { - const prepDeviceCall: MeticulousDevice = this.brewComponent - .brewBrewingPreparationDeviceEl.preparationDevice as MeticulousDevice; + this.brewComponent.maximizeFlowGraph(); + } - if (this.data.preparationDeviceBrew.params.chosenProfileId !== '') { - this.uiLog.log( - `A Meticulous profile was choosen, execute it - ${this.data.preparationDeviceBrew.params.chosenProfileId}` - ); - await prepDeviceCall.loadProfileByID( - this.data.preparationDeviceBrew.params.chosenProfileId - ); - await prepDeviceCall.startExecute(); - } else { - this.uiLog.log( - 'No Meticulous profile was selected, just listen for the start' - ); - } + this.startFetchingDataFromMeticulous(); - if ( - this.settings.bluetooth_scale_maximize_on_start_timer === true && - this.brewComponent.maximizeFlowGraphIsShown === false - ) { - this.brewComponent.maximizeFlowGraph(); - } + } else if ( + this.brewComponent?.brewBrewingPreparationDeviceEl?.preparationDeviceConnected() && + this.brewComponent?.brewBrewingPreparationDeviceEl?.getPreparationDeviceType() === + PreparationDeviceType.SANREMO_YOU + ) { + const prepDeviceCall: SanremoYOUDevice = this.brewComponent + .brewBrewingPreparationDeviceEl.preparationDevice as SanremoYOUDevice; + prepDeviceCall.startShot() .catch((_msg) => { + this.uiLog.log('We could not start shot on sanremo: ' + _msg); + this.uiToast.showInfoToast( + 'We could not start shot on sanremo: ' + _msg, + false + ); + }); - this.startFetchingDataFromMeticulous(); + if ( + this.settings.bluetooth_scale_maximize_on_start_timer === true && + this.brewComponent.maximizeFlowGraphIsShown === false + ) { + this.brewComponent.maximizeFlowGraph(); } + + this.startFetchingDataFromSanremoYOU(); + } } @@ -2450,8 +2539,8 @@ export class BrewBrewingGraphComponent implements OnInit { this.ngZone.runOutsideAngular(() => { this.textToSpeech.speak( this.translate.instant('TEXT_TO_SPEECH.TIME') + - ' ' + - this.data.brew_time + ' ' + + this.data.brew_time ); }); }, 5000); @@ -2496,7 +2585,8 @@ export class BrewBrewingGraphComponent implements OnInit { if (scale) { scale.tare(); } - } catch (ex) {} + } catch (ex) { + } } this.brewComponent.timerStartPressed('AUTO_START_PRESSURE'); @@ -2548,7 +2638,8 @@ export class BrewBrewingGraphComponent implements OnInit { }, this.settings.bluetooth_command_delay); }); } - } catch (ex) {} + } catch (ex) { + } } this.brewComponent.timerStartPressed('AUTO_START_TEMPERATURE'); @@ -2656,8 +2747,9 @@ export class BrewBrewingGraphComponent implements OnInit { * We try to match also turbo-shots which are like 7-8 grams. * Scales with just 3 values per second would be like 7 / 3 values per second = 2.33g increase each tick. * So we won't get jump from like 1 to 10 gram, then to like 40 grams + * Update 26.08.24 - We change from 5 to 6, because we had one shot where the value jumped from 0 to 5,5 and we didn't track anymore */ - const plausibleEspressoWeightIncreaseBound: number = 5; + const plausibleEspressoWeightIncreaseBound: number = 6; risingFactorOK = entryBeforeVal + plausibleEspressoWeightIncreaseBound >= weight; @@ -2687,10 +2779,200 @@ export class BrewBrewingGraphComponent implements OnInit { old: oldWeight, smoothed: smoothedWeight, oldSmoothed: oldSmoothedWeight, - notMutatedWeight: notMutatedWeight, + notMutatedWeight: notMutatedWeight }; } + + private calculateBrewByWeight(_currentWeightValue: number, _residualLagTime: number, _targetWeight: number, _brewByWeightActive: boolean, _scale: BluetoothScale) { + let weight: number = this.uiHelper.toFixedIfNecessary( + _currentWeightValue, + 1 + ); + if (this.ignoreScaleWeight === true) { + if (this.flowProfileTempAll.length > 0) { + const oldFlowProfileTemp = + this.flowProfileTempAll[this.flowProfileTempAll.length - 1]; + weight = this.uiHelper.toFixedIfNecessary( + oldFlowProfileTemp.weight, + 1 + ); + } + } + + if ( + this.flow_profile_raw.realtimeFlow && + this.flow_profile_raw.realtimeFlow.length > 0 + ) { + + const targetWeight = _targetWeight; + + const brewByWeightActive: boolean = _brewByWeightActive; + + + let n = 3; + if (this.flowNCalculation > 0) { + n = this.flowNCalculation; + } else { + n = this.flowProfileTempAll.length; + } + const lag_time = this.uiHelper.toFixedIfNecessary(1 / n, 2); + + + let average_flow_rate = 0; + let lastFlowValue = 0; + + const linearArray = []; + + const weightFlowCalc: Array = + this.flow_profile_raw.weight.slice(-(n - 1)); + + for (let i = 0; i < weightFlowCalc.length; i++) { + if (weightFlowCalc[i] && weightFlowCalc[i].actual_weight) { + const linearArrayEntry = [i, weightFlowCalc[i].actual_weight]; + linearArray.push(linearArrayEntry); + } + } + linearArray.push([n - 1, weight]); + + const linearRegressionCalc = regression.linear(linearArray); + average_flow_rate = linearRegressionCalc.equation[0] * n; + + const scaleType = _scale.getScaleType(); + + this.pushBrewByWeight( + targetWeight, + lag_time, + this.flowTime + '.' + this.flowSecondTick, + lastFlowValue, + weight, + lag_time + _residualLagTime, + weight + average_flow_rate * (lag_time + _residualLagTime) >= + targetWeight, + average_flow_rate * (lag_time + _residualLagTime), + _residualLagTime, + average_flow_rate, + scaleType + ); + let thresholdHit: boolean = false; + if (brewByWeightActive) { + thresholdHit = + weight + + average_flow_rate * (lag_time + _residualLagTime) >= + targetWeight; + } else { + thresholdHit = weight >= targetWeight; + } + return thresholdHit; + } + return false; + } + + private triggerStopShotOnXenia(_actualScaleWeight) { + const prepDeviceCall: XeniaDevice = this.brewComponent + .brewBrewingPreparationDeviceEl + .preparationDevice as XeniaDevice; + if ( + this.data.preparationDeviceBrew.params + .scriptAtWeightReachedId > 0 + ) { + this.uiLog.log( + `Xenia Script - Weight Reached: ${_actualScaleWeight} - Trigger custom script` + ); + prepDeviceCall + .startScript( + this.data.preparationDeviceBrew.params + .scriptAtWeightReachedId + ) + .catch((_msg) => { + this.uiToast.showInfoToast( + 'We could not start script at weight: ' + _msg, + false + ); + this.uiLog.log( + 'We could not start script at weight: ' + _msg + ); + }); + this.writeExecutionTimeToNotes( + 'Weight reached script', + this.data.preparationDeviceBrew.params + .scriptAtWeightReachedId, + this.getScriptName( + this.data.preparationDeviceBrew.params + .scriptAtWeightReachedId + ) + ); + } else { + this.uiLog.log( + `Xenia Script - Weight Reached - Trigger stop script` + ); + prepDeviceCall.stopScript().catch((_msg) => { + this.uiToast.showInfoToast( + 'We could not stop script at weight: ' + _msg, + false + ); + this.uiLog.log( + 'We could not stop script at weight: ' + _msg + ); + }); + this.writeExecutionTimeToNotes( + 'Stop script (Weight reached)', + 0, + this.translate.instant( + 'PREPARATION_DEVICE.TYPE_XENIA.SCRIPT_LIST_GENERAL_STOP' + ) + ); + } + + // This will be just called once, we stopped the shot and now we check if we directly shall stop or not + if ( + this.settings + .bluetooth_scale_espresso_stop_on_no_weight_change === + false + ) { + this.stopFetchingAndSettingDataFromXenia(); + this.brewComponent.timer.pauseTimer('xenia'); + } else { + // We weight for the normal "setFlow" to stop the detection of the graph, there then aswell is the stop fetch of the xenia triggered. + } + } + + private triggerStopShotOnSanremoYOU(_actualScaleWeight) { + const prepDeviceCall: SanremoYOUDevice = this.brewComponent + .brewBrewingPreparationDeviceEl + .preparationDevice as SanremoYOUDevice; + + this.uiLog.log( + `Sanremo YOU Stop: ${_actualScaleWeight}` + ); + prepDeviceCall + .stopShot( + + ) + .catch((_msg) => { + this.uiToast.showInfoToast( + 'We could not stop at weight: ' + _msg, + false + ); + this.uiLog.log( + 'We could not start script at weight: ' + _msg + ); + }); + + // This will be just called once, we stopped the shot and now we check if we directly shall stop or not + if ( + this.settings + .bluetooth_scale_espresso_stop_on_no_weight_change === + false + ) { + this.stopFetchingDataFromSanremoYOU(); + this.brewComponent.timer.pauseTimer('sanremo_you'); + } else { + // We weight for the normal "setFlow" to stop the detection of the graph, there then aswell is the stop fetch of the xenia triggered. + } + } + + public attachToScaleWeightChange() { const scale: BluetoothScale = this.bleManager.getScale(); const preparationStyleType = this.data.getPreparation().style_type; @@ -2703,6 +2985,39 @@ export class BrewBrewingGraphComponent implements OnInit { } this.machineStopScriptWasTriggered = false; + + const prepDeviceConnected = this.brewComponent.brewBrewingPreparationDeviceEl.preparationDeviceConnected(); + let residual_lag_time = 1.35; + let targetWeight = 0; + let brewByWeightActive: boolean = false; + let preparationDeviceType: PreparationDeviceType; + + if (prepDeviceConnected) { + preparationDeviceType = this.brewComponent.brewBrewingPreparationDeviceEl.getPreparationDeviceType(); + switch (preparationDeviceType) { + case PreparationDeviceType.XENIA: + + const prepXeniaDeviceCall: XeniaDevice = this.brewComponent + .brewBrewingPreparationDeviceEl + .preparationDevice as XeniaDevice; + residual_lag_time = prepXeniaDeviceCall.getResidualLagTime(); + targetWeight = this.data.preparationDeviceBrew.params + .scriptAtWeightReachedNumber; + brewByWeightActive = this.data.preparationDeviceBrew?.params?.brew_by_weight_active; + break; + case PreparationDeviceType.SANREMO_YOU: + const prepSanremoDeviceCall: SanremoYOUDevice = this.brewComponent + .brewBrewingPreparationDeviceEl + .preparationDevice as SanremoYOUDevice; + residual_lag_time = prepSanremoDeviceCall.getResidualLagTime(); + targetWeight = this.data.preparationDeviceBrew.params.stopAtWeight; + brewByWeightActive = true; + break; + + } + } + + this.scaleFlowSubscription = scale.flowChange.subscribe((_valChange) => { let _val; if (this.ignoreScaleWeight === false) { @@ -2715,193 +3030,31 @@ export class BrewBrewingGraphComponent implements OnInit { _val = _valChange; } - if ( - this.brewComponent.timer.isTimerRunning() && - this.brewComponent.brewBrewingPreparationDeviceEl.preparationDeviceConnected() && - this.brewComponent.brewBrewingPreparationDeviceEl.getPreparationDeviceType() === - PreparationDeviceType.XENIA && - this.data.preparationDeviceBrew.params.scriptAtWeightReachedNumber > 0 - ) { - if (this.isFirstXeniaScriptSet()) { - let weight: number = this.uiHelper.toFixedIfNecessary( - _val.actual, - 1 - ); - if (this.ignoreScaleWeight === true) { - if (this.flowProfileTempAll.length > 0) { - const oldFlowProfileTemp = - this.flowProfileTempAll[this.flowProfileTempAll.length - 1]; - weight = this.uiHelper.toFixedIfNecessary( - oldFlowProfileTemp.weight, - 1 - ); - } - } - - if ( - this.flow_profile_raw.realtimeFlow && - this.flow_profile_raw.realtimeFlow.length > 0 - ) { - const prepDeviceCall: XeniaDevice = this.brewComponent - .brewBrewingPreparationDeviceEl - .preparationDevice as XeniaDevice; - - const targetWeight = - this.data.preparationDeviceBrew.params - .scriptAtWeightReachedNumber; + if (this.brewComponent.timer.isTimerRunning() && prepDeviceConnected) { - const brewByWeightActive: boolean = - this.data.preparationDeviceBrew?.params?.brew_by_weight_active; + if (preparationDeviceType === PreparationDeviceType.XENIA && this.data.preparationDeviceBrew.params.scriptAtWeightReachedNumber > 0 && this.isFirstXeniaScriptSet()) { + /**We call this function before the if, because we still log the data**/ + const thresholdHit = this.calculateBrewByWeight(_val.actual, residual_lag_time, targetWeight, brewByWeightActive, scale); - let n = 3; - if (this.flowNCalculation > 0) { - n = this.flowNCalculation; - } else { - n = this.flowProfileTempAll.length; + if (this.machineStopScriptWasTriggered === false) { + if (thresholdHit) { + this.machineStopScriptWasTriggered = true; + this.triggerStopShotOnXenia(_val.actual); } - const lag_time = this.uiHelper.toFixedIfNecessary(1 / n, 2); - const residual_lag_time = prepDeviceCall.getResidualLagTime(); - - let average_flow_rate = 0; - let lastFlowValue = 0; - - const linearArray = []; - - const weightFlowCalc: Array = - this.flow_profile_raw.weight.slice(-(n - 1)); - - for (let i = 0; i < weightFlowCalc.length; i++) { - if (weightFlowCalc[i] && weightFlowCalc[i].actual_weight) { - const linearArrayEntry = [i, weightFlowCalc[i].actual_weight]; - linearArray.push(linearArrayEntry); - } - } - linearArray.push([n - 1, weight]); - - const linearRegressionCalc = regression.linear(linearArray); - average_flow_rate = linearRegressionCalc.equation[0] * n; - - /** Old calculcation - try { - lastFlowValue = - this.flow_profile_raw.realtimeFlow[ - this.flow_profile_raw.realtimeFlow.length - 1 - ].flow_value; - - const avgFlowValCalc: Array = - this.flow_profile_raw.realtimeFlow.slice(-n); - - for (let i = 0; i < avgFlowValCalc.length; i++) { - if (avgFlowValCalc[i] && avgFlowValCalc[i].flow_value) { - average_flow_rate = - average_flow_rate + avgFlowValCalc[i].flow_value; - } - } - if (average_flow_rate > 0) { - average_flow_rate = this.uiHelper.toFixedIfNecessary( - average_flow_rate / n, - 2 - ); - } - } catch (ex) {}**/ - - const scaleType = scale.getScaleType(); - - this.pushBrewByWeight( - this.data.preparationDeviceBrew.params - .scriptAtWeightReachedNumber, - lag_time, - this.flowTime + '.' + this.flowSecondTick, - lastFlowValue, - weight, - lag_time + residual_lag_time, - weight + average_flow_rate * (lag_time + residual_lag_time) >= - targetWeight, - average_flow_rate * (lag_time + residual_lag_time), - residual_lag_time, - average_flow_rate, - scaleType - ); - - if (this.machineStopScriptWasTriggered === false) { - let thresholdHit: boolean = false; - if (brewByWeightActive) { - thresholdHit = - weight + - average_flow_rate * (lag_time + residual_lag_time) >= - targetWeight; - } else { - thresholdHit = weight >= targetWeight; - } - - if (thresholdHit) { - if ( - this.data.preparationDeviceBrew.params - .scriptAtWeightReachedId > 0 - ) { - this.uiLog.log( - `Xenia Script - Weight Reached: ${weight} - Trigger custom script` - ); - prepDeviceCall - .startScript( - this.data.preparationDeviceBrew.params - .scriptAtWeightReachedId - ) - .catch((_msg) => { - this.uiToast.showInfoToast( - 'We could not start script at weight: ' + _msg, - false - ); - this.uiLog.log( - 'We could not start script at weight: ' + _msg - ); - }); - this.writeExecutionTimeToNotes( - 'Weight reached script', - this.data.preparationDeviceBrew.params - .scriptAtWeightReachedId, - this.getScriptName( - this.data.preparationDeviceBrew.params - .scriptAtWeightReachedId - ) - ); - } else { - this.uiLog.log( - `Xenia Script - Weight Reached - Trigger stop script` - ); - prepDeviceCall.stopScript().catch((_msg) => { - this.uiToast.showInfoToast( - 'We could not stop script at weight: ' + _msg, - false - ); - this.uiLog.log( - 'We could not stop script at weight: ' + _msg - ); - }); - this.writeExecutionTimeToNotes( - 'Stop script (Weight reached)', - 0, - this.translate.instant( - 'PREPARATION_DEVICE.TYPE_XENIA.SCRIPT_LIST_GENERAL_STOP' - ) - ); - } - this.machineStopScriptWasTriggered = true; - // This will be just called once, we stopped the shot and now we check if we directly shall stop or not - if ( - this.settings - .bluetooth_scale_espresso_stop_on_no_weight_change === - false - ) { - this.stopFetchingAndSettingDataFromXenia(); - this.brewComponent.timer.pauseTimer('xenia'); - } else { - // We weight for the normal "setFlow" to stop the detection of the graph, there then aswell is the stop fetch of the xenia triggered. - } - } + } + } else if (this.brewComponent.brewBrewingPreparationDeviceEl.getPreparationDeviceType() === + PreparationDeviceType.SANREMO_YOU) { + + /**We call this function before the if, because we still log the data**/ + const thresholdHit = this.calculateBrewByWeight(_val.actual, residual_lag_time, targetWeight, brewByWeightActive, scale); + if (this.machineStopScriptWasTriggered === false) { + if (thresholdHit) { + this.machineStopScriptWasTriggered = true; + this.triggerStopShotOnSanremoYOU(_val.actual); } } } + } if (this.ignoreScaleWeight === false) { this.__setFlowProfile(_val); @@ -2914,7 +3067,7 @@ export class BrewBrewingGraphComponent implements OnInit { old: oldFlowProfileTemp.oldWeight, smoothed: oldFlowProfileTemp.smoothedWeight, oldSmoothed: oldFlowProfileTemp.oldSmoothedWeight, - notMutatedWeight: _val.notMutatedWeight, + notMutatedWeight: _val.notMutatedWeight }; this.__setFlowProfile(passVal); } @@ -2940,6 +3093,7 @@ export class BrewBrewingGraphComponent implements OnInit { this.deattachToScaleStartTareListening(); this.stopFetchingAndSettingDataFromXenia(); this.stopFetchingDataFromMeticulous(); + this.stopFetchingDataFromSanremoYOU(); if (this.settings?.text_to_speech_active) { this.textToSpeech.end(); @@ -2960,7 +3114,8 @@ export class BrewBrewingGraphComponent implements OnInit { _flowProfile ); resolve(jsonParsed); - } catch (ex) {} + } catch (ex) { + } } } else { resolve(BeanconquerorFlowTestDataDummySecondDummy); @@ -2975,7 +3130,8 @@ export class BrewBrewingGraphComponent implements OnInit { try { const jsonParsed = await this.uiFileHelper.getJSONFile(flowProfilePath); this.flow_profile_raw = jsonParsed; - } catch (ex) {} + } catch (ex) { + } } private async deleteFlowProfile() { @@ -2985,7 +3141,8 @@ export class BrewBrewingGraphComponent implements OnInit { 'brews/' + this.data.config.uuid + '_flow_profile.json'; await this.uiFileHelper.deleteFile(flowProfilePath); } - } catch (ex) {} + } catch (ex) { + } } private __setMachineWaterFlow(_flow: any) { @@ -3006,7 +3163,7 @@ export class BrewBrewingGraphComponent implements OnInit { old: old, flowTime: this.flowTime, flowTimeSecond: this.flowTime + '.' + this.flowSecondTick, - flowTimestamp: this.uiHelper.getActualTimeWithMilliseconds(), + flowTimestamp: this.uiHelper.getActualTimeWithMilliseconds() }; const realtimeWaterFlow: IBrewRealtimeWaterFlow = @@ -3050,7 +3207,7 @@ export class BrewBrewingGraphComponent implements OnInit { actual: actual, old: old, flowTime: this.flowTime, - flowTimeSecond: this.flowTime + '.' + this.flowSecondTick, + flowTimeSecond: this.flowTime + '.' + this.flowSecondTick }; if (!isSmartScaleConnected) { @@ -3103,7 +3260,7 @@ export class BrewBrewingGraphComponent implements OnInit { actual: actual, old: old, flowTime: this.flowTime, - flowTimeSecond: this.flowTime + '.' + this.flowSecondTick, + flowTimeSecond: this.flowTime + '.' + this.flowSecondTick }; if (!isSmartScaleConnected) { @@ -3171,7 +3328,7 @@ export class BrewBrewingGraphComponent implements OnInit { flowTimeSecond: this.flowTime + '.' + this.flowSecondTick, flowTimestamp: this.uiHelper.getActualTimeWithMilliseconds(), dateUnixTime: undefined, - notMutatedWeight: notMutatedWeight, + notMutatedWeight: notMutatedWeight }; flowObj.dateUnixTime = new Date(flowObj.unixTime); @@ -3272,7 +3429,7 @@ export class BrewBrewingGraphComponent implements OnInit { lastVal - firstVal < 0.5 || (this.flowProfileArr.length > 2 && this.flowProfileArr[this.flowProfileArr.length - 2] - firstVal < - 0.5) + 0.5) ) { // Threshold for filter is bigger, 0.5g // Threshshold, weight changes because of strange thing happening. @@ -3436,7 +3593,8 @@ export class BrewBrewingGraphComponent implements OnInit { timeStampDelta = flowObj.unixTime - this.flowProfileTempAll[this.flowProfileTempAll.length - n].unixTime; - } catch (ex) {} + } catch (ex) { + } } realtimeWaterFlow.timestampdelta = timeStampDelta; @@ -3448,7 +3606,8 @@ export class BrewBrewingGraphComponent implements OnInit { this.flowProfileTempAll[this.flowProfileTempAll.length - n] .smoothedWeight) * (1000 / timeStampDelta); - } catch (ex) {} + } catch (ex) { + } if (Number.isNaN(calcFlowValue)) { calcFlowValue = 0; } @@ -3462,7 +3621,7 @@ export class BrewBrewingGraphComponent implements OnInit { if ( this.data.getPreparation().style_type === - PREPARATION_STYLE_TYPE.ESPRESSO && + PREPARATION_STYLE_TYPE.ESPRESSO && flowObj.weight >= this.settings.bluetooth_scale_first_drip_threshold ) { // If the drip timer is showing, we can set the first drip and not doing a reference to the normal weight. @@ -3510,17 +3669,24 @@ export class BrewBrewingGraphComponent implements OnInit { if ( this.brewComponent.brewBrewingPreparationDeviceEl.preparationDeviceConnected() && this.brewComponent.brewBrewingPreparationDeviceEl.getPreparationDeviceType() === - PreparationDeviceType.XENIA + PreparationDeviceType.XENIA ) { this.stopFetchingAndSettingDataFromXenia(); } + if ( + this.brewComponent.brewBrewingPreparationDeviceEl.preparationDeviceConnected() && + this.brewComponent.brewBrewingPreparationDeviceEl.getPreparationDeviceType() === + PreparationDeviceType.SANREMO_YOU + ) { + this.stopFetchingDataFromSanremoYOU(); + } let isMeticulous: boolean = false; if ( this.brewComponent.brewBrewingPreparationDeviceEl.preparationDeviceConnected() && this.brewComponent.brewBrewingPreparationDeviceEl.getPreparationDeviceType() === - PreparationDeviceType.METICULOUS + PreparationDeviceType.METICULOUS ) { isMeticulous = true; } @@ -3580,7 +3746,7 @@ export class BrewBrewingGraphComponent implements OnInit { if ( this.settings.bluetooth_scale_espresso_stop_on_no_weight_change === - false || + false || this.weightTrace.y.length < 50 || this.data.brew_time <= 5 ) { @@ -3701,7 +3867,8 @@ export class BrewBrewingGraphComponent implements OnInit { this.lastChartLayout.width = this.canvaContainer.nativeElement.offsetWidth; Plotly.relayout(this.profileDiv.nativeElement, this.lastChartLayout); - } catch (ex) {} + } catch (ex) { + } }, 50); } } @@ -3767,8 +3934,8 @@ export class BrewBrewingGraphComponent implements OnInit { component: BrewChooseGraphReferenceComponent, id: BrewChooseGraphReferenceComponent.COMPONENT_ID, componentProps: { - brew: this.data, - }, + brew: this.data + } }); // will force rerender :D @@ -3806,7 +3973,7 @@ export class BrewBrewingGraphComponent implements OnInit { if ( this.brewComponent?.brewBrewingPreparationDeviceEl?.preparationDeviceConnected() && this.brewComponent?.brewBrewingPreparationDeviceEl?.getPreparationDeviceType() === - PreparationDeviceType.XENIA && + PreparationDeviceType.XENIA && this.data.preparationDeviceBrew.params.scriptAtFirstDripId > 0 ) { if (this.isFirstXeniaScriptSet()) { @@ -3842,7 +4009,7 @@ export class BrewBrewingGraphComponent implements OnInit { !this.smartScaleConnected() && this.brewComponent?.brewBrewingPreparationDeviceEl?.preparationDeviceConnected() && this.brewComponent?.brewBrewingPreparationDeviceEl?.getPreparationDeviceType() === - PreparationDeviceType.XENIA + PreparationDeviceType.XENIA ) { // If scale is not connected but the device, we can now choose that still the script is executed if existing. if (this.isFirstXeniaScriptSet()) { @@ -3859,12 +4026,12 @@ export class BrewBrewingGraphComponent implements OnInit { .catch((_msg) => { this.uiToast.showInfoToast( 'We could not start script at first drip - manual triggered: ' + - _msg, + _msg, false ); this.uiLog.log( 'We could not start script at first drip - manual triggered: ' + - _msg + _msg ); }); this.writeExecutionTimeToNotes( @@ -3883,7 +4050,7 @@ export class BrewBrewingGraphComponent implements OnInit { if ( this.brewComponent?.brewBrewingPreparationDeviceEl?.preparationDeviceConnected() && this.brewComponent?.brewBrewingPreparationDeviceEl?.getPreparationDeviceType() === - PreparationDeviceType.XENIA + PreparationDeviceType.XENIA ) { if (this.data.preparationDeviceBrew.params.scriptStartId > 0) { return true; diff --git a/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.html b/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.html index e052d021..1c314521 100644 --- a/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.html +++ b/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.html @@ -79,6 +79,15 @@ {{"IMPORT_SHOT_FROM_METICULOUS" | translate}}
+ + + {{"PREPARATION_DEVICE.TYPE_SANREMO_YOU.TITLE" | translate }} + + + + + diff --git a/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.ts b/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.ts index c1346992..1b87af23 100644 --- a/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.ts +++ b/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.ts @@ -37,9 +37,10 @@ import { IBrewTemperatureFlow, IBrewWeightFlow, } from '../../../classes/brew/brewFlow'; -import { BrewChooseGraphReferenceComponent } from '../../../app/brew/brew-choose-graph-reference/brew-choose-graph-reference.component'; import { BrewModalImportShotMeticulousComponent } from '../../../app/brew/brew-modal-import-shot-meticulous/brew-modal-import-shot-meticulous.component'; import { ModalController } from '@ionic/angular'; +import { HistoryListingEntry } from '@meticulous-home/espresso-api/dist/types'; +import { SanremoYOUDevice, SanremoYOUParams } from '../../../classes/preparationDevice/sanremo/sanremoYOUDevice'; @Component({ selector: 'brew-brewing-preparation-device', templateUrl: './brew-brewing-preparation-device.component.html', @@ -50,7 +51,7 @@ export class BrewBrewingPreparationDeviceComponent implements OnInit { @Input() public isEdit: boolean = false; @Output() public dataChange = new EventEmitter(); @Input() public brewComponent: BrewBrewingComponent; - public preparationDevice: XeniaDevice | MeticulousDevice = undefined; + public preparationDevice: XeniaDevice | MeticulousDevice | SanremoYOUDevice = undefined; public preparation: Preparation = undefined; public settings: Settings = undefined; @@ -69,8 +70,10 @@ export class BrewBrewingPreparationDeviceComponent implements OnInit { private readonly uiSettingsStorage: UISettingsStorage, private readonly uiPreparationStorage: UIPreparationStorage, private readonly changeDetectorRef: ChangeDetectorRef, - private readonly modalController: ModalController - ) {} + private readonly modalController: ModalController, + public readonly uiBrewHelper: UIBrewHelper + ) { + } public ngOnInit() { this.settings = this.uiSettingsStorage.getSettings(); @@ -107,6 +110,9 @@ export class BrewBrewingPreparationDeviceComponent implements OnInit { } else if (connectedDevice instanceof MeticulousDevice) { await this.instanceMeticulousPreparationDevice(connectedDevice, _brew); } + else if (connectedDevice instanceof SanremoYOUDevice) { + await this.instanceSanremoYOUPreparationDevice(connectedDevice, _brew); + } this.checkChanges(); } else { this.preparationDevice = undefined; @@ -293,6 +299,20 @@ export class BrewBrewingPreparationDeviceComponent implements OnInit { ); } + private async instanceSanremoYOUPreparationDevice( + connectedDevice: SanremoYOUDevice, + _brew: Brew = null + ) { + this.data.preparationDeviceBrew.type = PreparationDeviceType.SANREMO_YOU; + this.data.preparationDeviceBrew.params = new SanremoYOUParams(); + await connectedDevice.deviceConnected().then(() => { + this.preparationDevice = connectedDevice as SanremoYOUDevice; + }, () => { + //Not connected + }); + + } + public async importShotFromMeticulous() { const modal = await this.modalController.create({ component: BrewModalImportShotMeticulousComponent, @@ -304,6 +324,82 @@ export class BrewBrewingPreparationDeviceComponent implements OnInit { await modal.present(); const rData = await modal.onWillDismiss(); + + if (rData && rData.data && rData.data.choosenHistory) { + const chosenEntry = rData.data.choosenHistory as HistoryListingEntry; + this.generateShotFlowProfileFromMeticulousData(chosenEntry); + } + } + + private generateShotFlowProfileFromMeticulousData(_historyData) { + + const newMoment = moment(new Date()).startOf('day'); + + let firstDripTimeSet: boolean = false; + const newBrewFlow = new BrewFlow(); + + let seconds: number = 0; + let milliseconds: number = 0; + for (const entry of _historyData.data as any) { + const shotEntry: any = entry.shot; + const shotEntryTime = newMoment.clone().add('milliseconds', entry.time); + const timestamp = shotEntryTime.format('HH:mm:ss.SSS'); + + seconds = shotEntryTime.diff(newMoment, 'seconds'); + milliseconds = shotEntryTime.get('milliseconds'); + + const realtimeWaterFlow: IBrewRealtimeWaterFlow = + {} as IBrewRealtimeWaterFlow; + + realtimeWaterFlow.brew_time = ''; + realtimeWaterFlow.timestamp = timestamp; + realtimeWaterFlow.smoothed_weight = 0; + realtimeWaterFlow.flow_value = shotEntry.flow; + realtimeWaterFlow.timestampdelta = 0; + + newBrewFlow.realtimeFlow.push(realtimeWaterFlow); + + const brewFlow: IBrewWeightFlow = {} as IBrewWeightFlow; + brewFlow.timestamp = timestamp; + brewFlow.brew_time = ''; + brewFlow.actual_weight = shotEntry.weight; + brewFlow.old_weight = 0; + brewFlow.actual_smoothed_weight = 0; + brewFlow.old_smoothed_weight = 0; + brewFlow.not_mutated_weight = 0; + newBrewFlow.weight.push(brewFlow); + + if (shotEntry.weight > 0 && firstDripTimeSet === false) { + firstDripTimeSet = true; + + this.brewComponent.brewFirstDripTime?.setTime(seconds, milliseconds); + this.brewComponent.brewFirstDripTime?.changeEvent(); + } + + const pressureFlow: IBrewPressureFlow = {} as IBrewPressureFlow; + pressureFlow.timestamp = timestamp; + pressureFlow.brew_time = ''; + pressureFlow.actual_pressure = shotEntry.pressure; + pressureFlow.old_pressure = 0; + newBrewFlow.pressureFlow.push(pressureFlow); + + const temperatureFlow: IBrewTemperatureFlow = {} as IBrewTemperatureFlow; + temperatureFlow.timestamp = timestamp; + temperatureFlow.brew_time = ''; + temperatureFlow.actual_temperature = shotEntry.temperature; + temperatureFlow.old_temperature = 0; + newBrewFlow.temperatureFlow.push(temperatureFlow); + } + + const lastEntry = newBrewFlow.weight[newBrewFlow.weight.length - 1]; + this.brewComponent.data.brew_beverage_quantity = lastEntry.actual_weight; + + this.brewComponent.timer?.setTime(seconds, milliseconds); + this.brewComponent.timer?.changeEvent(); + + this.brewComponent.brewBrewingGraphEl.flow_profile_raw = newBrewFlow; + this.brewComponent.brewBrewingGraphEl.initializeFlowChart(true); + } public getPreparationDeviceType() { @@ -311,6 +407,8 @@ export class BrewBrewingPreparationDeviceComponent implements OnInit { return PreparationDeviceType.XENIA; } else if (this.preparationDevice instanceof MeticulousDevice) { return PreparationDeviceType.METICULOUS; + } else if (this.preparationDevice instanceof SanremoYOUDevice) { + return PreparationDeviceType.SANREMO_YOU; } return PreparationDeviceType.NONE; } diff --git a/src/components/graph-display-card/graph-display-card.component.ts b/src/components/graph-display-card/graph-display-card.component.ts index 64c38920..6651cc52 100644 --- a/src/components/graph-display-card/graph-display-card.component.ts +++ b/src/components/graph-display-card/graph-display-card.component.ts @@ -15,6 +15,8 @@ import { UIHelper } from '../../services/uiHelper'; import { UIFileHelper } from '../../services/uiFileHelper'; import { Platform } from '@ionic/angular'; import { UISettingsStorage } from '../../services/uiSettingsStorage'; +import { HistoryListingEntry } from '@meticulous-home/espresso-api/dist/types'; +import { MeticulousDevice } from '../../classes/preparationDevice/meticulous/meticulousDevice'; declare var Plotly; @Component({ @@ -26,6 +28,8 @@ export class GraphDisplayCardComponent implements OnInit { @Input() public flowProfileData: any; @Input() public flowProfilePath: any; + @Input() public meticulousHistoryData: HistoryListingEntry; + @Input() public chartWidth: number; public flow_profile_raw: BrewFlow = new BrewFlow(); @@ -56,8 +60,10 @@ export class GraphDisplayCardComponent implements OnInit { this.settings = this.uiSettingsStorage.getSettings(); if (this.flowProfilePath) { await this.readFlowProfile(); - } else { + } else if (this.flowProfileData) { this.flow_profile_raw = this.uiHelper.cloneData(this.flowProfileData); + } else if (this.meticulousHistoryData) { + this.flow_profile_raw = MeticulousDevice.returnBrewFlowForShotData(this.meticulousHistoryData.data); } setTimeout(() => { this.initializeFlowChart(); diff --git a/src/components/photo-add/photo-add.component.ts b/src/components/photo-add/photo-add.component.ts index 273ebc95..dcaebba0 100644 --- a/src/components/photo-add/photo-add.component.ts +++ b/src/components/photo-add/photo-add.component.ts @@ -2,10 +2,10 @@ import { Component, ElementRef, EventEmitter, - Input, + Input, OnDestroy, OnInit, Output, - ViewChild, + ViewChild } from '@angular/core'; import { UIImage } from '../../services/uiImage'; import { Brew } from '../../classes/brew/brew'; @@ -24,7 +24,7 @@ import { TranslateService } from '@ngx-translate/core'; templateUrl: './photo-add.component.html', styleUrls: ['./photo-add.component.scss'], }) -export class PhotoAddComponent implements OnInit { +export class PhotoAddComponent implements OnInit,OnDestroy { @Input() public data: Brew | Bean | GreenBean | Mill | Preparation; @Output() public dataChange = new EventEmitter< Brew | Bean | GreenBean | Mill | Preparation @@ -47,7 +47,9 @@ export class PhotoAddComponent implements OnInit { this.updateSlider(); }, 250); } - + public ngOnDestroy() { + + } public addImage(): void { this.uiImage.showOptionChooser().then((_option) => { if (_option === 'CHOOSE') { diff --git a/src/components/preparation-information-card/preparation-information-card.component.ts b/src/components/preparation-information-card/preparation-information-card.component.ts index 578bbdcd..b07e55ba 100644 --- a/src/components/preparation-information-card/preparation-information-card.component.ts +++ b/src/components/preparation-information-card/preparation-information-card.component.ts @@ -217,17 +217,7 @@ export class PreparationInformationCardComponent implements OnInit { } public async connectDevice() { - this.uiAnalytics.trackEvent( - PREPARATION_TRACKING.TITLE, - PREPARATION_TRACKING.ACTIONS.CONNECT_DEVICE - ); - const modal = await this.modalController.create({ - component: PreparationConnectedDeviceComponent, - componentProps: { preparation: this.preparation }, - id: PreparationConnectedDeviceComponent.COMPONENT_ID, - }); - await modal.present(); - await modal.onWillDismiss(); + await this.uiPreparationHelper.connectDevice(this.preparation); } public async longPressEditPreparation(event) { diff --git a/src/enums/beans/beanSortAfter.ts b/src/enums/beans/beanSortAfter.ts index 9c324f15..abc8786c 100755 --- a/src/enums/beans/beanSortAfter.ts +++ b/src/enums/beans/beanSortAfter.ts @@ -5,6 +5,6 @@ export enum BEAN_SORT_AFTER { ROASTING_DATE = 'ROASTING DATE', CREATION_DATE = 'CREATION DATE', PURCHASE_DATE = 'PURCHASE DATE', - RATING ='RATING' - + RATING ='RATING', + BEAN_AGE ='BEAN AGE' } diff --git a/src/enums/preparations/preparationTypes.ts b/src/enums/preparations/preparationTypes.ts index 57f5e041..140e7d28 100755 --- a/src/enums/preparations/preparationTypes.ts +++ b/src/enums/preparations/preparationTypes.ts @@ -1,5 +1,8 @@ export enum PREPARATION_TYPES { CUSTOM_PREPARATION = 'CUSTOM_PREPARATION', + METICULOUS = 'METICULOUS', + SANREMO_YOU = 'SANREMO_YOU', + XENIA = 'XENIA', AEROPRESS = 'AEROPRESS', V60 = 'V60', CHEMEX = 'CHEMEX', @@ -40,5 +43,4 @@ export enum PREPARATION_TYPES { ROK = 'ROK', TORNADO_DUO = 'TORNADO_DUO', TRICOLATE = 'TRICOLATE', - METICULOUS = 'METICULOUS', } diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts index 2a39fae0..63744692 100644 --- a/src/environments/environment.prod.ts +++ b/src/environments/environment.prod.ts @@ -1,4 +1,7 @@ export const environment = { production: true, - API_URL: 'https://backend.beanconqueror.com/' + API_URL: 'https://backend.beanconqueror.com/', + FEATURES_ACTIVE: { + SANREMO_YOU: false + } }; diff --git a/src/environments/environment.ts b/src/environments/environment.ts index faa76bbe..af970cf9 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -5,6 +5,9 @@ export const environment = { production: false, API_URL: 'https://backend.beanconqueror.com/', + FEATURES_ACTIVE: { + SANREMO_YOU: false + } }; /* diff --git a/src/interfaces/preparationDevices/sanremoYOU/iSanremoYOUParams.ts b/src/interfaces/preparationDevices/sanremoYOU/iSanremoYOUParams.ts new file mode 100644 index 00000000..b5ce33ea --- /dev/null +++ b/src/interfaces/preparationDevices/sanremoYOU/iSanremoYOUParams.ts @@ -0,0 +1,5 @@ +export interface ISanremoYOUParams { + stopAtWeight: number; + residualLagTime: number; + +} diff --git a/src/interfaces/settings/iSettings.ts b/src/interfaces/settings/iSettings.ts index 60396ee0..2aa0afac 100755 --- a/src/interfaces/settings/iSettings.ts +++ b/src/interfaces/settings/iSettings.ts @@ -84,6 +84,16 @@ export interface ISettings { ARCHIVED: IBeanPageSort; }; + bean_collapsed: { + OPEN: boolean; + ARCHIVED: boolean; + FROZEN: boolean; + }; + brew_collapsed: { + OPEN: boolean; + ARCHIVED: boolean; + }; + green_bean_sort: { OPEN: IBeanPageSort; ARCHIVED: IBeanPageSort; diff --git a/src/popover/data-corruption-found/data-corruption-found.component.html b/src/popover/data-corruption-found/data-corruption-found.component.html new file mode 100644 index 00000000..8f17dd60 --- /dev/null +++ b/src/popover/data-corruption-found/data-corruption-found.component.html @@ -0,0 +1,69 @@ + + Data corruption found + + + + + + It somehow looks that your current data are corrupted, we've checked the backup file, in the following list you'll see how much data you've stored actually and how many are stored in your backup.
+ You can then choose to restore this backup or not.
+ If you don't feel save right now please make a screenshot of this screen, contact info@beanconqueror.com and close this app completely - any issues will be prevent with this +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Key + Actual Storage + + Backup +
Beans{{actualUIStorageDataObj?.BEANS}}{{backupDataObj?.BEANS?.length}}
Brews{{actualUIStorageDataObj?.BREWS}}{{backupDataObj?.BREWS?.length}}
Preparation{{actualUIStorageDataObj?.PREPARATION}}{{backupDataObj?.PREPARATION?.length}}
Grinder{{actualUIStorageDataObj?.MILL}}{{backupDataObj?.MILL?.length}}
+
+
+
+ + + + + Dont import backup + + + Import backup + + + + + diff --git a/src/popover/data-corruption-found/data-corruption-found.component.scss b/src/popover/data-corruption-found/data-corruption-found.component.scss new file mode 100644 index 00000000..25eeb2a5 --- /dev/null +++ b/src/popover/data-corruption-found/data-corruption-found.component.scss @@ -0,0 +1,6 @@ +:host { + table { + width:100%; + text-align:left; + } +} diff --git a/src/popover/data-corruption-found/data-corruption-found.component.spec.ts b/src/popover/data-corruption-found/data-corruption-found.component.spec.ts new file mode 100644 index 00000000..8ffd97dd --- /dev/null +++ b/src/popover/data-corruption-found/data-corruption-found.component.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { IonicModule } from '@ionic/angular'; + +import { DataCorruptionFoundComponent } from './data-corruption-found.component'; + +describe('DataCorruptionFoundComponent', () => { + let component: DataCorruptionFoundComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [ DataCorruptionFoundComponent ], + imports: [IonicModule.forRoot()] + }).compileComponents(); + + fixture = TestBed.createComponent(DataCorruptionFoundComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + })); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/popover/data-corruption-found/data-corruption-found.component.ts b/src/popover/data-corruption-found/data-corruption-found.component.ts new file mode 100644 index 00000000..4c859d78 --- /dev/null +++ b/src/popover/data-corruption-found/data-corruption-found.component.ts @@ -0,0 +1,68 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { ModalController, Platform } from '@ionic/angular'; + + +@Component({ + selector: 'app-data-corruption-found', + templateUrl: './data-corruption-found.component.html', + styleUrls: ['./data-corruption-found.component.scss'], +}) +export class DataCorruptionFoundComponent implements OnInit { + public static POPOVER_ID: string = 'data-corruption-found-popover'; + + @Input() public actualUIStorageDataObj: any = undefined; + @Input() public backupDataObj: any = undefined; + + private disableHardwareBack; + + constructor( + private readonly modalController: ModalController, + private readonly platform: Platform + ) { + + } + + public ngOnInit() { + try { + + this.disableHardwareBack = this.platform.backButton.subscribeWithPriority( + 9999, + (processNextHandler) => { + // Don't do anything. + } + ); + + } catch (ex) {} + } + + public async dismiss() { + try { + this.disableHardwareBack.unsubscribe(); + } catch (ex) {} + + this.modalController.dismiss( + { + dismissed: true, + }, + undefined, + DataCorruptionFoundComponent.POPOVER_ID + ); + } + + + public async import() { + try { + this.disableHardwareBack.unsubscribe(); + } catch (ex) {} + + this.modalController.dismiss( + { + dismissed: true, + import: true, + }, + undefined, + DataCorruptionFoundComponent.POPOVER_ID + ); + } + +} diff --git a/src/services/coffeeBluetoothDevices/coffee-bluetooth-devices.service.ts b/src/services/coffeeBluetoothDevices/coffee-bluetooth-devices.service.ts index 39f10438..be3849ef 100644 --- a/src/services/coffeeBluetoothDevices/coffee-bluetooth-devices.service.ts +++ b/src/services/coffeeBluetoothDevices/coffee-bluetooth-devices.service.ts @@ -41,6 +41,7 @@ import { BookooPressure } from 'src/classes/devices/bookooPressure'; import { BasicGrillThermometer } from 'src/classes/devices/basicGrillThermometer'; import { MeaterThermometer } from 'src/classes/devices/meaterThermometer'; import { CombustionThermometer } from '../../classes/devices/combustionThermometer'; +import { ArgosThermometer } from '../../classes/devices/argosThermometer'; declare var device: any; declare var ble: any; @@ -546,7 +547,8 @@ export class CoffeeBluetoothDevicesService { ETITemperature.test(scanDevice) || BasicGrillThermometer.test(scanDevice) || MeaterThermometer.test(scanDevice) || - CombustionThermometer.test(scanDevice) + CombustionThermometer.test(scanDevice) || + ArgosThermometer.test(scanDevice) ) { // We found all needed devices. promiseResolved = true; @@ -1047,6 +1049,17 @@ export class CoffeeBluetoothDevicesService { type: TemperatureType.COMBUSTION, }); } + else if (ArgosThermometer.test(deviceTemperature)) { + this.logger.log( + 'BleManager - We found a Argos Thermometer device ' + + JSON.stringify(deviceTemperature) + ); + supportedDevices.push({ + id: deviceTemperature.id, + type: TemperatureType.ARGOS, + }); + } + } resolve(supportedDevices); }); @@ -1092,6 +1105,16 @@ export class CoffeeBluetoothDevicesService { }); return; } + else if (ArgosThermometer.test(deviceTemperature)) { + this.logger.log( + 'BleManager - We found a Argos Thermometer device ' + ); + resolve({ + id: deviceTemperature.id, + type: TemperatureType.ARGOS, + }); + return; + } } resolve(undefined); } diff --git a/src/services/uiBeanHelper.ts b/src/services/uiBeanHelper.ts index 6c3980d5..be5600be 100644 --- a/src/services/uiBeanHelper.ts +++ b/src/services/uiBeanHelper.ts @@ -29,6 +29,8 @@ import { BEAN_ROASTING_TYPE_ENUM } from '../enums/beans/beanRoastingType'; import { AssociatedBrewsComponent } from '../app/brew/associated-brews/associated-brews.component'; import { BrewCuppingComponent } from '../app/brew/brew-cupping/brew-cupping.component'; import { BeanPopoverFreezeComponent } from '../app/beans/bean-popover-freeze/bean-popover-freeze.component'; +import { BeanPopoverFrozenListComponent } from '../app/beans/bean-popover-frozen-list/bean-popover-frozen-list.component'; +import { BeanPopoverListComponent } from '../app/beans/bean-popover-list/bean-popover-list.component'; /** * Handles every helping functionalities @@ -319,6 +321,16 @@ export class UIBeanHelper { await modal.onWillDismiss(); } + public async showBeans(_beanList: Array) { + const modal = await this.modalController.create({ + component: BeanPopoverListComponent, + id: BeanPopoverListComponent.COMPONENT_ID, + componentProps: { beansList: _beanList }, + }); + await modal.present(); + await modal.onWillDismiss(); + } + public async editBean(_bean: Bean) { const modal = await this.modalController.create({ component: BeansEditComponent, diff --git a/src/services/uiExcel.ts b/src/services/uiExcel.ts index d52ebf16..c9cd4e8a 100755 --- a/src/services/uiExcel.ts +++ b/src/services/uiExcel.ts @@ -15,7 +15,6 @@ import { BEAN_ROASTING_TYPE_ENUM } from '../enums/beans/beanRoastingType'; import { BEAN_MIX_ENUM } from '../enums/beans/mix'; import { UIPreparationStorage } from './uiPreparationStorage'; import { UIAlert } from './uiAlert'; -import { SocialSharing } from '@awesome-cordova-plugins/social-sharing/ngx'; import { UIFileHelper } from './uiFileHelper'; import { UIMillStorage } from './uiMillStorage'; import { BrewFlow, IBrewWaterFlow } from '../classes/brew/brewFlow'; @@ -23,6 +22,11 @@ import moment from 'moment'; import { UISettingsStorage } from './uiSettingsStorage'; import { Settings } from '../classes/settings/settings'; import { Brew } from '../classes/brew/brew'; +import { Bean } from '../classes/bean/bean'; +import { IBeanInformation } from '../interfaces/bean/iBeanInformation'; +import { UIBeanHelper } from './uiBeanHelper'; +import { GreenBean } from '../classes/green-bean/green-bean'; +import { UIGreenBeanStorage } from './uiGreenBeanStorage'; @Injectable({ providedIn: 'root', @@ -37,14 +41,14 @@ export class UIExcel { private readonly platform: Platform, private readonly uiBrewStorage: UIBrewStorage, private readonly uiBeanStorage: UIBeanStorage, + private readonly uiGreenBeanStorage: UIGreenBeanStorage, private readonly uiPreparationStoraage: UIPreparationStorage, private readonly translate: TranslateService, private readonly uiAlert: UIAlert, - private readonly socialsharing: SocialSharing, private readonly uiFileHelper: UIFileHelper, private readonly uiMillStorage: UIMillStorage, - private readonly alertCtrl: AlertController, - private readonly uiSettingsStorage: UISettingsStorage + private readonly uiSettingsStorage: UISettingsStorage, + private readonly uiBeanHelper: UIBeanHelper ) { this.settings = this.uiSettingsStorage.getSettings(); } @@ -80,8 +84,6 @@ export class UIExcel { const wsData: any[][] = [header]; for (const entry of _flow.weight) { - const notMutatedWeight: number = 0; - const wbEntry: Array = [ entry.timestamp, entry.brew_time, @@ -719,18 +721,498 @@ export class UIExcel { } } + + public async importGreenBeansByExcel(_arrayBuffer) { + try { + /* data is an ArrayBuffer */ + const wb = XLSX.read(_arrayBuffer); + const data = XLSX.utils.sheet_to_json(wb.Sheets['Green Beans']); + + + let varietyInformationWhereAdded: boolean = false; + const addedBeans: Array = []; + const toAddBeans: Array = []; + for (const entry of data) { + /** + * 1. Bean certification: 14 + * 1. Country: 5 + * 1. Elevation: 9 + * 1. Farm: 7 + * 1. Farmer: 8 + * 1. Harvested: 12 + * 1. Percentage: 13 + * 1. Processing: 11 + * 1. Region: 6 + * 1. Variety: 10 + * 1. Fob Price + * 1. Purchasing Price + * Archived: false - + * Cost: 13 - + * Cupping points: 92 - + * Decaffeinated: false - + * EAN / Articlenumber: 2 - + * Flavour profile: "Red, whine, apple" - + * Name: 123 - + * Notes: 3 + * Rating: 4 + * Website: 1 - + * Weight: 250 - + * Buy Date + */ + + const bean: GreenBean = new GreenBean(); + + const nameEntry = entry['Name']; + if (nameEntry) { + bean.name = nameEntry.toString(); + } else { + continue; + } + + const weightEntry = entry['Weight']; + if (weightEntry && Number(weightEntry) > 0) { + bean.weight = Number(weightEntry); + } + + const buyDateEntry = entry['Buy Date']; + if (buyDateEntry && Number(buyDateEntry) > 0) { + bean.date = this.getJsDateFromExcel(Number(buyDateEntry)); + } + + const websiteEntry = entry['Website']; + if (websiteEntry) { + bean.url = websiteEntry.toString(); + } + + + const flavourProfileEntry = entry['Flavour profile']; + if (flavourProfileEntry) { + bean.aromatics = flavourProfileEntry.toString(); + } + + const eanArticleNumberEntry = entry['EAN / Articlenumber']; + if (eanArticleNumberEntry) { + bean.ean_article_number = eanArticleNumberEntry.toString(); + } + + const decaffeinatedEntry = entry['Decaffeinated']; + if (decaffeinatedEntry) { + bean.decaffeinated = decaffeinatedEntry; + } + + const cuppingPointsEntry = entry['Cupping points']; + if (cuppingPointsEntry) { + bean.cupping_points = cuppingPointsEntry.toString(); + } + + const costEntry = entry['Cost']; + if (costEntry) { + bean.cost = Number(costEntry); + } + + const archivedEntry = entry['Archived']; + if (archivedEntry) { + bean.finished = archivedEntry; + } + + const beanInformation: IBeanInformation = {} as IBeanInformation; + let hasOneBeanInformation: boolean = false; + + const informationCertificationEntry = entry['1. Bean certification']; + if (informationCertificationEntry) { + beanInformation.certification = informationCertificationEntry.toString(); + hasOneBeanInformation=true; + } + + const informationCountryEntry = entry['1. Country']; + if (informationCountryEntry) { + beanInformation.country = informationCountryEntry.toString(); + hasOneBeanInformation=true; + } + + const informationElevationEntry = entry['1. Elevation']; + if (informationElevationEntry) { + beanInformation.elevation = informationElevationEntry.toString(); + hasOneBeanInformation=true; + } + + const informationFarmEntry = entry['1. Farm']; + if (informationFarmEntry) { + beanInformation.farm = informationFarmEntry.toString(); + hasOneBeanInformation=true; + } + + const informationFarmerEntry = entry['1. Farmer']; + if (informationFarmerEntry) { + beanInformation.farmer = informationFarmerEntry.toString(); + hasOneBeanInformation=true; + } + + const informationHarvestedEntry = entry['1. Harvested']; + if (informationHarvestedEntry) { + beanInformation.harvest_time = informationHarvestedEntry.toString(); + hasOneBeanInformation=true; + } + + const informationPercentageEntry = entry['1. Percentage']; + if (informationPercentageEntry && Number(informationPercentageEntry) > 0) { + beanInformation.percentage = Number(informationPercentageEntry); + hasOneBeanInformation=true; + } + + const informationProcessingEntry = entry['1. Processing']; + if (informationProcessingEntry) { + beanInformation.processing = informationProcessingEntry.toString(); + hasOneBeanInformation=true; + } + + const informationRegionEntry = entry['1. Region']; + if (informationRegionEntry) { + beanInformation.region = informationRegionEntry.toString(); + hasOneBeanInformation=true; + } + + const informationVarietyEntry = entry['1. Variety']; + if (informationVarietyEntry) { + beanInformation.variety = informationVarietyEntry.toString(); + hasOneBeanInformation=true; + } + + const informationFobPriceEntry = entry['1. Fob Price']; + if (informationFobPriceEntry && Number(informationFobPriceEntry) > 0) { + beanInformation.fob_price = Number(informationFobPriceEntry); + hasOneBeanInformation=true; + } + + const informationPurchasingPriceEntry = entry['1. Purchasing Price']; + if (informationPurchasingPriceEntry && Number(informationPurchasingPriceEntry) > 0) { + beanInformation.purchasing_price = Number(informationPurchasingPriceEntry); + hasOneBeanInformation=true; + } + + if (hasOneBeanInformation) { + varietyInformationWhereAdded = true; + bean.bean_information.push(beanInformation); + } else { + /** Add atleast one empty bean information**/ + const emptyBeanInformation: IBeanInformation = {} as IBeanInformation; + bean.bean_information.push(emptyBeanInformation); + } + + toAddBeans.push(bean); + } + + + if (varietyInformationWhereAdded) { + if (this.settings.bean_manage_parameters.bean_information === false) { + this.settings.bean_manage_parameters.bean_information = true; + await this.uiSettingsStorage.saveSettings(this.settings); + } + } + + + /** + * Add all beans afterwards to not have some added and then going into an exception + */ + for await (const addBean of toAddBeans) { + try { + const newBean: GreenBean = await this.uiGreenBeanStorage.add(addBean); + addedBeans.push(newBean); + } catch(ex) { + + } + } + + if (addedBeans.length>0) { + try { + await this.uiAlert.showMessage('GREEN_BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION','IMPORT_SUCCESSFULLY',undefined,true); + }catch(ex) { + + } + + } else { + this.uiAlert.showMessage('BEANS_IMPORTED_UNSUCCESSFULLY_WRONG_EXCELFILE','IMPORT_UNSUCCESSFULLY','OK',false); + } + + } catch (ex) { + this.uiAlert.showMessage(ex.message,this.translate.instant('IMPORT_UNSUCCESSFULLY'),this.translate.instant('OK'),false); + } + } + public async importBeansByExcel(_arrayBuffer) { try { /* data is an ArrayBuffer */ const wb = XLSX.read(_arrayBuffer); const data = XLSX.utils.sheet_to_json(wb.Sheets['Beans']); + + let isOneEntryFrozen: boolean = false; + let varietyInformationWhereAdded: boolean = false; + const addedBeans: Array = []; + const toAddBeans: Array = []; for (const entry of data) { - //const bean: Bean = new Bean(); - //bean.bean_information + /** + * 1. Bean certification: 14 + * 1. Country: 5 + * 1. Elevation: 9 + * 1. Farm: 7 + * 1. Farmer: 8 + * 1. Harvested: 12 + * 1. Percentage: 13 + * 1. Processing: 11 + * 1. Region: 6 + * 1. Variety: 10 + * 1. Fob Price + * 1. Purchasing Price + * Archived: false - + * Blend: "UNKNOWN" - + * Cost: 13 - + * Cupping points: 92 - + * Decaffeinated: false - + * Degree of Roast: "UNKNOWN" - + * EAN / Articlenumber: 2 - + * Flavour profile: "Red, whine, apple" - + * Name: 123 - + * Notes: 3 + * Rating: 4 + * Roast date: 44593 + * Roast type: "FILTER" - + * Roaster: 123 - + * Website: 1 - + * Weight: 250 - + * Frozen Date + * Unfrozen Date + * Freezing Storage Type + * Frozen Note + */ + + const bean: Bean = new Bean(); + + const nameEntry = entry['Name']; + if (nameEntry) { + bean.name = nameEntry.toString(); + } else { + continue; + } + + const weightEntry = entry['Weight']; + if (weightEntry && Number(weightEntry) > 0) { + bean.weight = Number(weightEntry); + } + + const roastDateEntry = entry['Roast date']; + if (roastDateEntry && Number(roastDateEntry) > 0) { + bean.roastingDate = this.getJsDateFromExcel(Number(roastDateEntry)); + } + + const websiteEntry = entry['Website']; + if (websiteEntry) { + bean.url = websiteEntry.toString(); + } + + const roasterEntry = entry['Roaster']; + if (roasterEntry) { + bean.roaster = roasterEntry.toString(); + } + + const roastTypeEntry = entry['Roast type']; + if (roastTypeEntry) { + bean.bean_roasting_type = roastTypeEntry; + } + + const flavourProfileEntry = entry['Flavour profile']; + if (flavourProfileEntry) { + bean.aromatics = flavourProfileEntry.toString(); + } + + const eanArticleNumberEntry = entry['EAN / Articlenumber']; + if (eanArticleNumberEntry) { + bean.ean_article_number = eanArticleNumberEntry.toString(); + } + + const degreeOfRoastEntry = entry['Degree of Roast']; + if (degreeOfRoastEntry) { + bean.roast = degreeOfRoastEntry; + } + const decaffeinatedEntry = entry['Decaffeinated']; + if (decaffeinatedEntry) { + bean.decaffeinated = decaffeinatedEntry; + } + + const cuppingPointsEntry = entry['Cupping points']; + if (cuppingPointsEntry) { + bean.cupping_points = cuppingPointsEntry.toString(); + } + + const costEntry = entry['Cost']; + if (costEntry) { + bean.cost = Number(costEntry); + } + + const blendEntry = entry['Blend']; + if (blendEntry) { + bean.beanMix = blendEntry; + } + + const archivedEntry = entry['Archived']; + if (archivedEntry) { + bean.finished = archivedEntry; + } + + const beanInformation: IBeanInformation = {} as IBeanInformation; + let hasOneBeanInformation: boolean = false; + + const informationCertificationEntry = entry['1. Bean certification']; + if (informationCertificationEntry) { + beanInformation.certification = informationCertificationEntry.toString(); + hasOneBeanInformation=true; + } + + const informationCountryEntry = entry['1. Country']; + if (informationCountryEntry) { + beanInformation.country = informationCountryEntry.toString(); + hasOneBeanInformation=true; + } + + const informationElevationEntry = entry['1. Elevation']; + if (informationElevationEntry) { + beanInformation.elevation = informationElevationEntry.toString(); + hasOneBeanInformation=true; + } + + const informationFarmEntry = entry['1. Farm']; + if (informationFarmEntry) { + beanInformation.farm = informationFarmEntry.toString(); + hasOneBeanInformation=true; + } + + const informationFarmerEntry = entry['1. Farmer']; + if (informationFarmerEntry) { + beanInformation.farmer = informationFarmerEntry.toString(); + hasOneBeanInformation=true; + } + + const informationHarvestedEntry = entry['1. Harvested']; + if (informationHarvestedEntry) { + beanInformation.harvest_time = informationHarvestedEntry.toString(); + hasOneBeanInformation=true; + } + + const informationPercentageEntry = entry['1. Percentage']; + if (informationPercentageEntry && Number(informationPercentageEntry) > 0) { + beanInformation.percentage = Number(informationPercentageEntry); + hasOneBeanInformation=true; + } + + const informationProcessingEntry = entry['1. Processing']; + if (informationProcessingEntry) { + beanInformation.processing = informationProcessingEntry.toString(); + hasOneBeanInformation=true; + } + + const informationRegionEntry = entry['1. Region']; + if (informationRegionEntry) { + beanInformation.region = informationRegionEntry.toString(); + hasOneBeanInformation=true; + } + + const informationVarietyEntry = entry['1. Variety']; + if (informationVarietyEntry) { + beanInformation.variety = informationVarietyEntry.toString(); + hasOneBeanInformation=true; + } + + const informationFobPriceEntry = entry['1. Fob Price']; + if (informationFobPriceEntry && Number(informationFobPriceEntry) > 0) { + beanInformation.fob_price = Number(informationFobPriceEntry); + hasOneBeanInformation=true; + } + + const informationPurchasingPriceEntry = entry['1. Purchasing Price']; + if (informationPurchasingPriceEntry && Number(informationPurchasingPriceEntry) > 0) { + beanInformation.purchasing_price = Number(informationPurchasingPriceEntry); + hasOneBeanInformation=true; + } + + if (hasOneBeanInformation) { + varietyInformationWhereAdded = true; + bean.bean_information.push(beanInformation); + } else { + /** Add atleast one empty bean information**/ + const emptyBeanInformation: IBeanInformation = {} as IBeanInformation; + bean.bean_information.push(emptyBeanInformation); + } + + const freezingStorageTypeEntry = entry['Freezing Storage Type']; + if (freezingStorageTypeEntry) { + bean.frozenStorageType = freezingStorageTypeEntry; + } + + const frozenNoteEntry = entry['Frozen Note']; + if (frozenNoteEntry) { + bean.frozenNote = frozenNoteEntry; + } + + const frozenDateEntry = entry['Frozen Date']; + if (frozenDateEntry && Number(frozenDateEntry) > 0) { + isOneEntryFrozen = true; + bean.frozenDate = this.getJsDateFromExcel(Number(frozenDateEntry)); + bean.frozenId = this.uiBeanHelper.generateFrozenId(); + } + + const unfrozenDateEntry = entry['Unfrozen Date']; + if (unfrozenDateEntry && Number(unfrozenDateEntry) > 0) { + isOneEntryFrozen = true; + bean.unfrozenDate = this.getJsDateFromExcel(Number(unfrozenDateEntry)); + } + toAddBeans.push(bean); } - console.log(data); - console.log(wb); - } catch (ex) {} + + if (isOneEntryFrozen) { + // Activate the frozen feature if not active + if (this.settings.freeze_coffee_beans === false) { + this.settings.freeze_coffee_beans = true; + await this.uiSettingsStorage.saveSettings(this.settings); + } + } + if (varietyInformationWhereAdded) { + if (this.settings.bean_manage_parameters.bean_information === false) { + this.settings.bean_manage_parameters.bean_information = true; + await this.uiSettingsStorage.saveSettings(this.settings); + } + } + + + /** + * Add all beans afterwards to not have some added and then going into an exception + */ + for await (const addBean of toAddBeans) { + try { + const newBean: Bean = await this.uiBeanStorage.add(addBean); + addedBeans.push(newBean); + } catch(ex) { + + } + } + + if (addedBeans.length>0) { + try { + await this.uiAlert.showMessage('BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION','IMPORT_SUCCESSFULLY',undefined,true); + }catch(ex) { + + } + this.uiBeanHelper.showBeans(addedBeans); + } else { + this.uiAlert.showMessage('BEANS_IMPORTED_UNSUCCESSFULLY_WRONG_EXCELFILE','IMPORT_UNSUCCESSFULLY','OK',false); + } + + } catch (ex) { + this.uiAlert.showMessage(ex.message,this.translate.instant('IMPORT_UNSUCCESSFULLY'),this.translate.instant('OK'),false); + } + } + + private getJsDateFromExcel(_dateNumber) { + return new Date((_dateNumber - (25567+2))*86400*1000).toISOString(); + } /* Export button */ public async export() { diff --git a/src/services/uiExportImportHelper.ts b/src/services/uiExportImportHelper.ts index 964f5ac9..2b6cfa9f 100644 --- a/src/services/uiExportImportHelper.ts +++ b/src/services/uiExportImportHelper.ts @@ -20,11 +20,13 @@ import { import * as zip from '@zip.js/zip.js'; import { FileEntry } from '@awesome-cordova-plugins/file'; import { UILog } from './uiLog'; -import { Platform } from '@ionic/angular'; +import { ModalController, Platform } from '@ionic/angular'; import { UIFileHelper } from './uiFileHelper'; import { UIAlert } from './uiAlert'; import moment from 'moment'; import { UIBrewStorage } from './uiBrewStorage'; + +import { DataCorruptionFoundComponent } from '../popover/data-corruption-found/data-corruption-found.component'; @Injectable({ providedIn: 'root', }) @@ -43,7 +45,8 @@ export class UIExportImportHelper { private readonly uiFileHelper: UIFileHelper, private readonly uiAlert: UIAlert, private readonly uiSettingsStorage: UISettingsStorage, - private readonly uiBrewStorage: UIBrewStorage + private readonly uiBrewStorage: UIBrewStorage, + private readonly modalController: ModalController ) {} public async buildExportZIP(): Promise { @@ -239,6 +242,64 @@ export class UIExportImportHelper { }); }); } + private async checkBackupAndSeeIfDataAreCorrupted(_actualUIStorageDataObj) { + + try { + this.uiLog.log("checkBackupAndSeeIfDataAreCorrupted - Check if we got a deep corruption"); + const dataObj = _actualUIStorageDataObj.DATA; + const parsedJSON: any = await this.readBackupZIPFile(); + if (parsedJSON) { + let somethingCorrupted = false; + if (parsedJSON.BEANS?.length > dataObj.BEANS) { + somethingCorrupted = true; + } else if (parsedJSON.BREWS?.length > dataObj.BREWS) + { + somethingCorrupted = true; + } else if (parsedJSON.PREPARATION?.length > dataObj.PREPARATION) + { + somethingCorrupted = true; + } + else if (parsedJSON.MILL?.length > dataObj.MILL) + { + somethingCorrupted = true; + } + + this.uiLog.log("checkBackupAndSeeIfDataAreCorrupted- Check over - if we got a deep corruption - Result: " + somethingCorrupted); + if (somethingCorrupted) { + const importBackup = await this.showDataCorruptionPopover(dataObj,parsedJSON); + if (importBackup) { + await this.importBackupJSON(parsedJSON); + } + } else { + this.uiLog.log("checkBackupAndSeeIfDataAreCorrupted - Check over - we didn't find any corrupted data"); + } + } else { + this.uiLog.log("checkBackupAndSeeIfDataAreCorrupted - We didn't found any json backup data so we can't do any checks"); + } + } catch(ex) { + this.uiLog.log("Check over - if we got a deep corruption - Result exception: " + JSON.stringify(ex)); + } + + } + + public async showDataCorruptionPopover(_actualUIStorageDataObj,_backupDataObj) { + const modal = await this.modalController.create({ + component: DataCorruptionFoundComponent, + id: DataCorruptionFoundComponent.POPOVER_ID, + componentProps: { + actualUIStorageDataObj: _actualUIStorageDataObj, + backupDataObj: _backupDataObj, + }, + }); + await modal.present(); + const returnData = await modal.onWillDismiss(); + this.uiLog.log('Data corruption, choose to import: ' + returnData?.data?.import); + if (returnData?.data?.import) { + //User choose to import backup, go + return true; + } + return false; + } public async checkBackup() { try { @@ -248,12 +309,13 @@ export class UIExportImportHelper { this.uiLog.log('Check Backup'); const hasData = await this.uiStorage.hasData(); - let corruptedDataObjCheck: any; + let actualUIStorageDataObj: any; if (hasData) { - corruptedDataObjCheck = await this.uiStorage.hasCorruptedData(); + actualUIStorageDataObj = await this.uiStorage.hasCorruptedData(); } + this.uiLog.log('Check Backup - Has data ' + hasData); - if (!hasData || corruptedDataObjCheck.CORRUPTED) { + if (!hasData || actualUIStorageDataObj.CORRUPTED) { if (!hasData) { this.uiLog.log( 'Check Backup - We didnt found any data inside the app, so try to find a backup and import it' @@ -268,16 +330,9 @@ export class UIExportImportHelper { if (parsedJSON) { await this.importBackupJSON(parsedJSON); } + resolve(null); } else { - /** - * BREWS: number, - * MILL: number, - * PREPARATION: number, - * BEANS: number, - */ - const parsedJSON = await this.readBackupZIPFile(); - console.log('BLAAA'); - console.log(parsedJSON); + await this.checkBackupAndSeeIfDataAreCorrupted(actualUIStorageDataObj); resolve(null); } } else { diff --git a/src/services/uiFileHelper.ts b/src/services/uiFileHelper.ts index fbc07582..1cfe8c19 100644 --- a/src/services/uiFileHelper.ts +++ b/src/services/uiFileHelper.ts @@ -847,10 +847,10 @@ export class UIFileHelper extends InstanceClass { ): Promise { return new Promise(async (resolve, reject) => { if (this.platform.is('cordova')) { - if (this.cachedInternalUrls[_filePath]) { + /** if (this.cachedInternalUrls[_filePath]) { //resolve(this.cachedInternalUrls[_filePath]); // return; - } + }**/ // let filePath: string; // filePath = _filePath; // filePath.slice(0, filePath.lastIndexOf('/')); @@ -884,8 +884,9 @@ export class UIFileHelper extends InstanceClass { this.domSanitizer.bypassSecurityTrustResourceUrl( convertedURL ); - this.cachedInternalUrls[_filePath] = convertedURL; - resolve(convertedURL); + const returningURL = convertedURL; + // this.cachedInternalUrls[_filePath] = convertedURL; + resolve(returningURL); }, () => { resolve(''); diff --git a/src/services/uiHelper.ts b/src/services/uiHelper.ts index 51ec29d8..4805deb9 100755 --- a/src/services/uiHelper.ts +++ b/src/services/uiHelper.ts @@ -147,6 +147,16 @@ export class UIHelper { return moment(_unix).format(format); } + public formatTimeNumber(_time: number|string, _format?: string): string { + let format: string = + this.getSettingsStorageInstance().getSettings().date_format + + ', HH:mm:ss'; + if (_format) { + format = _format; + } + return moment(_time).format(format); + } + public toFixedIfNecessary(value, dp) { const parsedFloat = parseFloat(value); if (isNaN(parsedFloat)) { diff --git a/src/services/uiPreparationHelper.ts b/src/services/uiPreparationHelper.ts index d00acf5e..bc28ca0b 100644 --- a/src/services/uiPreparationHelper.ts +++ b/src/services/uiPreparationHelper.ts @@ -20,9 +20,13 @@ import { PreparationDeviceType, } from '../classes/preparationDevice'; import { HttpClient } from '@angular/common/http'; -import { XeniaDevice } from '../classes/preparationDevice/xenia/xeniaDevice'; import { PreparationDevice } from '../classes/preparationDevice/preparationDevice'; import { PreparationSortToolsComponent } from '../app/preparation/preparation-sort-tools/preparation-sort-tools.component'; +import PREPARATION_TRACKING from '../data/tracking/preparationTracking'; +import { + PreparationConnectedDeviceComponent +} from '../app/preparation/preparation-connected-device/preparation-connected-device.component'; +import { UIAnalytics } from './uiAnalytics'; /** * Handles every helping functionalities @@ -39,7 +43,8 @@ export class UIPreparationHelper { private readonly uiHelper: UIHelper, private readonly translate: TranslateService, private readonly uiPreparationStorage: UIPreparationStorage, - private readonly httpClient: HttpClient + private readonly httpClient: HttpClient, + private readonly uiAnalytics: UIAnalytics ) { this.uiBrewStorage.attachOnEvent().subscribe((_val) => { // If an brew is deleted, we need to reset our array for the next call. @@ -84,6 +89,22 @@ export class UIPreparationHelper { await modal.present(); await modal.onWillDismiss(); } + public async connectDevice(_preparation: Preparation) { + this.uiAnalytics.trackEvent( + PREPARATION_TRACKING.TITLE, + PREPARATION_TRACKING.ACTIONS.CONNECT_DEVICE + ); + const modal = await this.modalController.create({ + component: PreparationConnectedDeviceComponent, + componentProps: { preparation: _preparation }, + id: PreparationConnectedDeviceComponent.COMPONENT_ID, + }); + await modal.present(); + await modal.onWillDismiss(); + } + + + public async editPreparationTool( _preparation: Preparation, diff --git a/src/services/uiUpdate.ts b/src/services/uiUpdate.ts index dd672e8d..60af4c65 100755 --- a/src/services/uiUpdate.ts +++ b/src/services/uiUpdate.ts @@ -66,6 +66,7 @@ export class UIUpdate { 'UPDATE_8', 'UPDATE_9', 'UPDATE_10', + 'UPDATE_11', ]; const version: Version = this.uiVersionStorage.getVersion(); const _silentUpdate = hasData; @@ -89,6 +90,7 @@ export class UIUpdate { await this.__checkUpdateForDataVersion('UPDATE_8', !hasData); await this.__checkUpdateForDataVersion('UPDATE_9', !hasData); await this.__checkUpdateForDataVersion('UPDATE_10', !hasData); + await this.__checkUpdateForDataVersion('UPDATE_11', !hasData); } catch (ex) { if (this.uiAlert.isLoadingSpinnerShown()) { await this.uiAlert.hideLoadingSpinner(); @@ -549,8 +551,15 @@ export class UIUpdate { case 'UPDATE_10': const settings_v10: Settings = this.uiSettingsStorage.getSettings(); settings_v10.resetFilter(); + settings_v10.resetBeanSort(); await this.uiSettingsStorage.saveSettings(settings_v10); break; + case 'UPDATE_11': + //#776 - We added a new beansort, thats why we reset the bean sort here. + const settings_v11: Settings = this.uiSettingsStorage.getSettings(); + settings_v11.resetBeanSort(); + await this.uiSettingsStorage.saveSettings(settings_v11); + break; default: break; } From 6d52f656890d49f187834761dd55b250cf24e1ae Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Tue, 27 Aug 2024 21:02:17 +0200 Subject: [PATCH 20/55] #767 - Frozen beans sort order is now saved #765 - Show popup if data base is corrupted #762 - Read a history shot of the meticulous to import #768 - Add ean search article number #769 - Make brew/bean toggleable #438 - Import beans via excel #772 - Display best brew / favorite / frozen etc. #773 - Show frozen icon for beans on brew-list #775 - Fixing wrong labels #776 - Sorting bean for bean age #629 - Import green beans via excel #777 - grow plausible var from 5 to 6 #778 - Showing bean images on selection #623 - Showing a popup that more informations are there but not visible. --- src/app/app.component.ts | 4 - .../bean-popover-freeze.component.ts | 70 ++- .../bean-popover-list.component.spec.ts | 4 +- .../bean-popover-list.component.ts | 15 +- .../beans/beans-add/beans-add.component.ts | 27 +- src/app/beans/beans.page.ts | 9 +- ...l-import-shot-meticulous.component.spec.ts | 2 +- ...-modal-import-shot-meticulous.component.ts | 24 +- src/app/brew/brew.page.ts | 8 +- .../preparation-add-type.component.ts | 6 +- .../preparation-add.component.ts | 2 +- .../preparation-connected-device.component.ts | 25 +- src/app/settings/settings.page.ts | 11 +- src/classes/devices/argosThermometer.ts | 11 +- src/classes/devices/index.ts | 2 +- src/classes/preparation/preparation.ts | 6 +- .../meticulous/meticulousDevice.ts | 32 +- .../sanremo/sanremoYOUDevice.ts | 10 +- src/classes/settings/settings.ts | 1 - .../brew-information.component.ts | 1 - .../brew-brewing-graph.component.ts | 406 ++++++++---------- ...ew-brewing-preparation-device.component.ts | 30 +- .../graph-display-card.component.ts | 4 +- .../photo-add/photo-add.component.ts | 11 +- src/enums/beans/beanSortAfter.ts | 4 +- src/environments/environment.prod.ts | 4 +- src/environments/environment.ts | 4 +- .../sanremoYOU/iSanremoYOUParams.ts | 1 - .../data-corruption-found.component.spec.ts | 4 +- .../data-corruption-found.component.ts | 11 +- .../coffee-bluetooth-devices.service.ts | 9 +- src/services/uiExcel.ts | 160 ++++--- src/services/uiExportImportHelper.ts | 56 ++- src/services/uiHelper.ts | 2 +- src/services/uiPreparationHelper.ts | 7 +- 35 files changed, 498 insertions(+), 485 deletions(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 891080a4..5c9e5762 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -324,7 +324,6 @@ export class AppComponent implements AfterViewInit { }); } catch (ex) {} - // Okay, so the platform is ready and our plugins are available. // Here you can do any higher level native things you might need. // #7 @@ -367,7 +366,6 @@ export class AppComponent implements AfterViewInit { }); } - if (this.platform.is('cordova')) { // Just support deeplinks on devices. this.intentHandlerService.attachOnHandleOpenUrl(); @@ -376,8 +374,6 @@ export class AppComponent implements AfterViewInit { this._translate.setDefaultLang('en'); await this._translate.use('en').toPromise(); - - if (this.platform.is('cordova')) { try { await this.uiExportImportHelper.checkBackup(); diff --git a/src/app/beans/bean-popover-freeze/bean-popover-freeze.component.ts b/src/app/beans/bean-popover-freeze/bean-popover-freeze.component.ts index 9b429b3a..8bb2d0fd 100644 --- a/src/app/beans/bean-popover-freeze/bean-popover-freeze.component.ts +++ b/src/app/beans/bean-popover-freeze/bean-popover-freeze.component.ts @@ -59,7 +59,10 @@ export class BeanPopoverFreezeComponent implements OnInit { public ngOnInit() { // cant be done in constructor, else the bean object is not known - this.leftOverBeanBagWeight = this.uiHelper.toFixedIfNecessary(this.bean.weight - this.getUsedWeightCount(),1); + this.leftOverBeanBagWeight = this.uiHelper.toFixedIfNecessary( + this.bean.weight - this.getUsedWeightCount(), + 1 + ); } public getUsedWeightCount(): number { @@ -86,8 +89,10 @@ export class BeanPopoverFreezeComponent implements OnInit { } public async save() { - const spillOver = - this.uiHelper.toFixedIfNecessary(this.leftOverBeanBagWeight - this.getActualFreezingQuantity(),1); + const spillOver = this.uiHelper.toFixedIfNecessary( + this.leftOverBeanBagWeight - this.getActualFreezingQuantity(), + 1 + ); let index = 1; @@ -109,10 +114,16 @@ export class BeanPopoverFreezeComponent implements OnInit { this.bean.config.uuid ); if (brews.length > 0) { - const oldWeight = this.uiHelper.toFixedIfNecessary(this.bean.weight,1) - this.bean.weight = this.uiHelper.toFixedIfNecessary(this.bean.weight - this.getActualFreezingQuantity(),1); + const oldWeight = this.uiHelper.toFixedIfNecessary(this.bean.weight, 1); + this.bean.weight = this.uiHelper.toFixedIfNecessary( + this.bean.weight - this.getActualFreezingQuantity(), + 1 + ); try { - const newCost = this.uiHelper.toFixedIfNecessary((this.bean.cost * this.bean.weight) / oldWeight,2) + const newCost = this.uiHelper.toFixedIfNecessary( + (this.bean.cost * this.bean.weight) / oldWeight, + 2 + ); this.bean.cost = newCost; } catch (ex) { this.bean.cost = 0; @@ -148,10 +159,16 @@ export class BeanPopoverFreezeComponent implements OnInit { /** * Because we had already maybe some brews, we take the bean weight, and subtract it with the spill over, because just using the spill over would not take previus brews into account */ - const oldWeight = this.uiHelper.toFixedIfNecessary(this.bean.weight,1); - this.bean.weight = this.uiHelper.toFixedIfNecessary(this.bean.weight - this.getActualFreezingQuantity(),1); + const oldWeight = this.uiHelper.toFixedIfNecessary(this.bean.weight, 1); + this.bean.weight = this.uiHelper.toFixedIfNecessary( + this.bean.weight - this.getActualFreezingQuantity(), + 1 + ); try { - const newCost = this.uiHelper.toFixedIfNecessary((this.bean.cost * this.bean.weight) / oldWeight,2); + const newCost = this.uiHelper.toFixedIfNecessary( + (this.bean.cost * this.bean.weight) / oldWeight, + 2 + ); this.bean.cost = newCost; } catch (ex) { this.bean.cost = 0; @@ -189,13 +206,16 @@ export class BeanPopoverFreezeComponent implements OnInit { if (this.bean.cost !== 0) { try { - const newCost = this.uiHelper.toFixedIfNecessary((this.bean.cost * _freezingWeight) / this.bean.weight,2); + const newCost = this.uiHelper.toFixedIfNecessary( + (this.bean.cost * _freezingWeight) / this.bean.weight, + 2 + ); clonedBean.cost = newCost; } catch (ex) { clonedBean.cost = 0; } } - clonedBean.weight = this.uiHelper.toFixedIfNecessary(_freezingWeight,1); + clonedBean.weight = this.uiHelper.toFixedIfNecessary(_freezingWeight, 1); clonedBean.config = new Config(); const newClonedBean = await this.uiBeanStorage.add(clonedBean); const newBean: Bean = new Bean(); @@ -245,11 +265,15 @@ export class BeanPopoverFreezeComponent implements OnInit { } public isAddingBagDisabled() { - - if (this.freezePartialBagGrams <=0) { + if (this.freezePartialBagGrams <= 0) { return true; } - if (this.uiHelper.toFixedIfNecessary(Number(this.freezePartialBagGrams) + this.getActualFreezingQuantity(),1) > this.leftOverBeanBagWeight) { + if ( + this.uiHelper.toFixedIfNecessary( + Number(this.freezePartialBagGrams) + this.getActualFreezingQuantity(), + 1 + ) > this.leftOverBeanBagWeight + ) { return true; } return false; @@ -261,8 +285,10 @@ export class BeanPopoverFreezeComponent implements OnInit { type: this.frozenStorage, }); - const leftFreezingCount = - this.uiHelper.toFixedIfNecessary(this.leftOverBeanBagWeight - this.getActualFreezingQuantity(),1); + const leftFreezingCount = this.uiHelper.toFixedIfNecessary( + this.leftOverBeanBagWeight - this.getActualFreezingQuantity(), + 1 + ); if (leftFreezingCount < this.freezePartialBagGrams) { this.freezePartialBagGrams = this.uiHelper.toFixedIfNecessary( leftFreezingCount, @@ -278,8 +304,10 @@ export class BeanPopoverFreezeComponent implements OnInit { type: this.frozenStorage, }); - const leftFreezingCount = - this.uiHelper.toFixedIfNecessary(this.leftOverBeanBagWeight - this.getActualFreezingQuantity(),1); + const leftFreezingCount = this.uiHelper.toFixedIfNecessary( + this.leftOverBeanBagWeight - this.getActualFreezingQuantity(), + 1 + ); if (leftFreezingCount < this.freezePartialBagGrams) { this.freezePartialBagGrams = this.uiHelper.toFixedIfNecessary( leftFreezingCount, @@ -292,8 +320,10 @@ export class BeanPopoverFreezeComponent implements OnInit { public deleteBag(_index) { this.addedBags.splice(_index, 1); - const leftFreezingCount = - this.uiHelper.toFixedIfNecessary(this.leftOverBeanBagWeight - this.getActualFreezingQuantity(),1); + const leftFreezingCount = this.uiHelper.toFixedIfNecessary( + this.leftOverBeanBagWeight - this.getActualFreezingQuantity(), + 1 + ); if (leftFreezingCount < this.freezePartialBagGrams) { this.freezePartialBagGrams = leftFreezingCount; } diff --git a/src/app/beans/bean-popover-list/bean-popover-list.component.spec.ts b/src/app/beans/bean-popover-list/bean-popover-list.component.spec.ts index 34d578e6..5af36a70 100644 --- a/src/app/beans/bean-popover-list/bean-popover-list.component.spec.ts +++ b/src/app/beans/bean-popover-list/bean-popover-list.component.spec.ts @@ -11,14 +11,14 @@ describe('BeanPopoverListComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - declarations: [ BeanPopoverListComponent ], + declarations: [BeanPopoverListComponent], imports: [IonicModule.forRoot(), TranslateModule.forRoot()], providers: [ { provide: TranslateService, useValue: TranslateServiceMock, }, - ] + ], }).compileComponents(); fixture = TestBed.createComponent(BeanPopoverListComponent); diff --git a/src/app/beans/bean-popover-list/bean-popover-list.component.ts b/src/app/beans/bean-popover-list/bean-popover-list.component.ts index 6ae0948d..7d565d15 100644 --- a/src/app/beans/bean-popover-list/bean-popover-list.component.ts +++ b/src/app/beans/bean-popover-list/bean-popover-list.component.ts @@ -1,4 +1,11 @@ -import { Component, ElementRef, HostListener, Input, OnInit, ViewChild } from '@angular/core'; +import { + Component, + ElementRef, + HostListener, + Input, + OnInit, + ViewChild, +} from '@angular/core'; import { Bean } from '../../../classes/bean/bean'; import { AgVirtualSrollComponent } from 'ag-virtual-scroll'; import { ModalController } from '@ionic/angular'; @@ -9,7 +16,6 @@ import { ModalController } from '@ionic/angular'; styleUrls: ['./bean-popover-list.component.scss'], }) export class BeanPopoverListComponent { - public static readonly COMPONENT_ID = 'bean-popover-list'; @Input() public beansList: Array = undefined; @@ -20,9 +26,7 @@ export class BeanPopoverListComponent { @ViewChild('beanContent', { read: ElementRef }) public beanContent: ElementRef; - constructor( - private readonly modalController: ModalController, - ) {} + constructor(private readonly modalController: ModalController) {} public async ionViewWillEnter() { this.loadBrews(); @@ -61,5 +65,4 @@ export class BeanPopoverListComponent { BeanPopoverListComponent.COMPONENT_ID ); } - } diff --git a/src/app/beans/beans-add/beans-add.component.ts b/src/app/beans/beans-add/beans-add.component.ts index fbad06f2..81f5afaf 100644 --- a/src/app/beans/beans-add/beans-add.component.ts +++ b/src/app/beans/beans-add/beans-add.component.ts @@ -20,7 +20,7 @@ import { Settings } from '../../../classes/settings/settings'; @Component({ selector: 'beans-add', templateUrl: './beans-add.component.html', - styleUrls: ['./beans-add.component.scss'] + styleUrls: ['./beans-add.component.scss'], }) export class BeansAddComponent implements OnInit { public static readonly COMPONENT_ID = 'bean-add'; @@ -49,8 +49,7 @@ export class BeansAddComponent implements OnInit { private readonly platform: Platform, public readonly uiBeanHelper: UIBeanHelper, private readonly uiSettingsStorage: UISettingsStorage - ) { - } + ) {} public ngOnInit() { this.settings = this.uiSettingsStorage.getSettings(); @@ -67,8 +66,6 @@ export class BeansAddComponent implements OnInit { private async checkIfInformationAreSetButNotDisplayed() { try { - - const params = this.settings.bean_manage_parameters; if (this.data.bean_information.length > 0) { const info: IBeanInformation = this.data.bean_information[0]; @@ -114,20 +111,20 @@ export class BeansAddComponent implements OnInit { //Woopsi doopsi, user hasn't enabled the bean_information, lets display him a popover //#623 try { - const yes = await this.uiAlert.showConfirm('BEAN_POPUP_YOU_DONT_SEE_EVERYTHING_DESCRIPTION', 'INFORMATION', true); + const yes = await this.uiAlert.showConfirm( + 'BEAN_POPUP_YOU_DONT_SEE_EVERYTHING_DESCRIPTION', + 'INFORMATION', + true + ); this.settings.bean_manage_parameters.bean_information = true; await this.uiSettingsStorage.update(this.settings); //Activate } catch (ex) { // Don't activate } - } - } - } catch (ex) { - - } + } catch (ex) {} } public async ionViewWillEnter() { @@ -228,11 +225,10 @@ export class BeansAddComponent implements OnInit { if (this.settings.security_check_when_going_back === true) { this.disableHardwareBack.unsubscribe(); } - } catch (ex) { - } + } catch (ex) {} this.modalController.dismiss( { - dismissed: true + dismissed: true, }, undefined, BeansAddComponent.COMPONENT_ID @@ -270,8 +266,7 @@ export class BeansAddComponent implements OnInit { try { const newPath: string = await this.uiFileHelper.copyFile(attachment); copyAttachments.push(newPath); - } catch (ex) { - } + } catch (ex) {} } this.data.attachments = copyAttachments; if (_bean.cupped_flavor) { diff --git a/src/app/beans/beans.page.ts b/src/app/beans/beans.page.ts index a9331db4..3e70c849 100644 --- a/src/app/beans/beans.page.ts +++ b/src/app/beans/beans.page.ts @@ -110,7 +110,7 @@ export class BeansPage implements OnDestroy { this.frozenBeansFilter = this.settings.bean_filter.FROZEN; this.openBeansFilter = this.settings.bean_filter.OPEN; - this.openBeansCollapsed = this.settings.bean_collapsed.OPEN; + this.openBeansCollapsed = this.settings.bean_collapsed.OPEN; this.archivedBeansCollapsed = this.settings.bean_collapsed.ARCHIVED; this.frozenBeansCollapsed = this.settings.bean_collapsed.FROZEN; this.loadBeans(); @@ -129,13 +129,13 @@ export class BeansPage implements OnDestroy { } } public isCollapseActive() { - let collapsed: boolean =false; + let collapsed: boolean = false; if (this.bean_segment === 'open') { collapsed = this.openBeansCollapsed; } else if (this.bean_segment === 'archive') { collapsed = this.archivedBeansCollapsed; } else if (this.bean_segment === 'frozen') { - collapsed = this.frozenBeansCollapsed; + collapsed = this.frozenBeansCollapsed; } return collapsed; } @@ -146,7 +146,7 @@ export class BeansPage implements OnDestroy { } else if (this.bean_segment === 'archive') { this.archivedBeansCollapsed = !this.archivedBeansCollapsed; } else if (this.bean_segment === 'frozen') { - this.frozenBeansCollapsed = !this.frozenBeansCollapsed; + this.frozenBeansCollapsed = !this.frozenBeansCollapsed; } this.__saveCollapseFilter(); this.research(); @@ -158,7 +158,6 @@ export class BeansPage implements OnDestroy { await this.uiSettingsStorage.saveSettings(this.settings); } - public loadBeans(): void { this.__initializeBeans(); this.changeDetectorRef.detectChanges(); diff --git a/src/app/brew/brew-modal-import-shot-meticulous/brew-modal-import-shot-meticulous.component.spec.ts b/src/app/brew/brew-modal-import-shot-meticulous/brew-modal-import-shot-meticulous.component.spec.ts index f05bd393..629913fa 100644 --- a/src/app/brew/brew-modal-import-shot-meticulous/brew-modal-import-shot-meticulous.component.spec.ts +++ b/src/app/brew/brew-modal-import-shot-meticulous/brew-modal-import-shot-meticulous.component.spec.ts @@ -25,7 +25,7 @@ describe('BrewModalImportShotMeticulousComponent', () => { provide: TranslateService, useValue: TranslateServiceMock, }, - ] + ], }).compileComponents(); fixture = TestBed.createComponent(BrewModalImportShotMeticulousComponent); diff --git a/src/app/brew/brew-modal-import-shot-meticulous/brew-modal-import-shot-meticulous.component.ts b/src/app/brew/brew-modal-import-shot-meticulous/brew-modal-import-shot-meticulous.component.ts index c98b7487..3ffbddff 100644 --- a/src/app/brew/brew-modal-import-shot-meticulous/brew-modal-import-shot-meticulous.component.ts +++ b/src/app/brew/brew-modal-import-shot-meticulous/brew-modal-import-shot-meticulous.component.ts @@ -1,4 +1,11 @@ -import { Component, ElementRef, HostListener, Input, OnInit, ViewChild } from '@angular/core'; +import { + Component, + ElementRef, + HostListener, + Input, + OnInit, + ViewChild, +} from '@angular/core'; import { MeticulousDevice } from '../../../classes/preparationDevice/meticulous/meticulousDevice'; import { ModalController } from '@ionic/angular'; @@ -33,8 +40,10 @@ export class BrewModalImportShotMeticulousComponent implements OnInit { @ViewChild('footerContent', { read: ElementRef }) public footerContent: ElementRef; - constructor(private readonly modalController: ModalController, - public readonly uiHelper: UIHelper) {} + constructor( + private readonly modalController: ModalController, + public readonly uiHelper: UIHelper + ) {} public ngOnInit() { this.readHistory(); @@ -43,7 +52,6 @@ export class BrewModalImportShotMeticulousComponent implements OnInit { private async readHistory() { this.history = await this.meticulousDevice?.getHistory(); this.retriggerScroll(); - } @HostListener('window:resize') @HostListener('window:orientationchange', ['$event']) @@ -51,17 +59,13 @@ export class BrewModalImportShotMeticulousComponent implements OnInit { this.retriggerScroll(); } - private retriggerScroll() { setTimeout(async () => { const el = this.historyShotContent.nativeElement; const scrollComponent: AgVirtualSrollComponent = this.shotDataScroll; if (scrollComponent) { - scrollComponent.el.style.height = - (el.offsetHeight - - 20) + - 'px'; + scrollComponent.el.style.height = el.offsetHeight - 20 + 'px'; } }, 150); } @@ -73,8 +77,6 @@ export class BrewModalImportShotMeticulousComponent implements OnInit { return 0; } - - public dismiss(): void { this.modalController.dismiss( { diff --git a/src/app/brew/brew.page.ts b/src/app/brew/brew.page.ts index 4b291ebb..73b55b43 100644 --- a/src/app/brew/brew.page.ts +++ b/src/app/brew/brew.page.ts @@ -58,7 +58,7 @@ export class BrewPage implements OnInit { private readonly changeDetectorRef: ChangeDetectorRef, public uiHelper: UIHelper, public uiBrewHelper: UIBrewHelper, - private readonly uiSettingsStorage: UISettingsStorage, + private readonly uiSettingsStorage: UISettingsStorage ) { this.settings = this.uiSettingsStorage.getSettings(); this.archivedBrewsFilter = this.settings.GET_BREW_FILTER(); @@ -69,7 +69,7 @@ export class BrewPage implements OnInit { this.archivedBrewsFilter = this.settings.brew_filter.ARCHIVED; this.openBrewsFilter = this.settings.brew_filter.OPEN; this.openBrewsCollapsed = this.settings.brew_collapsed.OPEN; - this.archivedBrewsCollapsed = this.settings.brew_collapsed.ARCHIVED; + this.archivedBrewsCollapsed = this.settings.brew_collapsed.ARCHIVED; this.loadBrews(); this.retriggerScroll(); @@ -124,7 +124,7 @@ export class BrewPage implements OnInit { } public isCollapseActive() { - let collapsed: boolean =false; + let collapsed: boolean = false; if (this.brew_segment === 'open') { collapsed = this.openBrewsCollapsed; } else { @@ -218,8 +218,6 @@ export class BrewPage implements OnInit { return instructor.config.uuid; } - - public async showFilter() { let brewFilter: IBrewPageFilter; if (this.brew_segment === 'open') { diff --git a/src/app/preparation/preparation-add-type/preparation-add-type.component.ts b/src/app/preparation/preparation-add-type/preparation-add-type.component.ts index db570736..09385ab7 100644 --- a/src/app/preparation/preparation-add-type/preparation-add-type.component.ts +++ b/src/app/preparation/preparation-add-type/preparation-add-type.component.ts @@ -78,7 +78,11 @@ export class PreparationAddTypeComponent implements OnInit { this.uiToast.showInfoToast('TOAST_PREPARATION_ADDED_SUCCESSFULLY'); } - if (this.data.type === PREPARATION_TYPES.METICULOUS || this.data.type === PREPARATION_TYPES.XENIA || this.data.type === PREPARATION_TYPES.SANREMO_YOU) { + if ( + this.data.type === PREPARATION_TYPES.METICULOUS || + this.data.type === PREPARATION_TYPES.XENIA || + this.data.type === PREPARATION_TYPES.SANREMO_YOU + ) { await this.uiPreparationHelper.connectDevice(newPreparation); } } diff --git a/src/app/preparation/preparation-add/preparation-add.component.ts b/src/app/preparation/preparation-add/preparation-add.component.ts index ada8b095..cd800c9e 100644 --- a/src/app/preparation/preparation-add/preparation-add.component.ts +++ b/src/app/preparation/preparation-add/preparation-add.component.ts @@ -39,7 +39,7 @@ export class PreparationAddComponent implements OnInit { } public individualPreparationVisible(_key) { - if (_key ==='SANREMO_YOU') { + if (_key === 'SANREMO_YOU') { if (this.ENVIRONMENT.FEATURES_ACTIVE.SANREMO_YOU === true) { return true; } diff --git a/src/app/preparation/preparation-connected-device/preparation-connected-device.component.ts b/src/app/preparation/preparation-connected-device/preparation-connected-device.component.ts index 8e9d18f1..f6c2ce88 100644 --- a/src/app/preparation/preparation-connected-device/preparation-connected-device.component.ts +++ b/src/app/preparation/preparation-connected-device/preparation-connected-device.component.ts @@ -52,18 +52,21 @@ export class PreparationConnectedDeviceComponent { if (this.preparation !== undefined) { this.data.initializeByObject(this.preparation); } - if (this.data.connectedPreparationDevice.type === PreparationDeviceType.NONE) { + if ( + this.data.connectedPreparationDevice.type === PreparationDeviceType.NONE + ) { if (this.data.type === PREPARATION_TYPES.METICULOUS) { - this.data.connectedPreparationDevice.type = PreparationDeviceType.METICULOUS; + this.data.connectedPreparationDevice.type = + PreparationDeviceType.METICULOUS; } if (this.data.type === PREPARATION_TYPES.XENIA) { this.data.connectedPreparationDevice.type = PreparationDeviceType.XENIA; } if (this.data.type === PREPARATION_TYPES.SANREMO_YOU) { - this.data.connectedPreparationDevice.type = PreparationDeviceType.SANREMO_YOU; + this.data.connectedPreparationDevice.type = + PreparationDeviceType.SANREMO_YOU; } } - } public dismiss(): void { @@ -117,15 +120,13 @@ export class PreparationConnectedDeviceComponent { if ( this.data.connectedPreparationDevice.type === PreparationDeviceType.METICULOUS - ) - { + ) { if (this.data.connectedPreparationDevice.url.endsWith('/') === true) { this.data.connectedPreparationDevice.url = this.data.connectedPreparationDevice.url.slice(0, -1); } if ( - this.data.connectedPreparationDevice.url.startsWith('http') === - false + this.data.connectedPreparationDevice.url.startsWith('http') === false ) { this.data.connectedPreparationDevice.url = 'http://' + this.data.connectedPreparationDevice.url; @@ -134,15 +135,13 @@ export class PreparationConnectedDeviceComponent { if ( this.data.connectedPreparationDevice.type === PreparationDeviceType.SANREMO_YOU - ) - { + ) { if (this.data.connectedPreparationDevice.url.endsWith('/') === true) { this.data.connectedPreparationDevice.url = this.data.connectedPreparationDevice.url.slice(0, -1); } if ( - this.data.connectedPreparationDevice.url.startsWith('http') === - false + this.data.connectedPreparationDevice.url.startsWith('http') === false ) { this.data.connectedPreparationDevice.url = 'http://' + this.data.connectedPreparationDevice.url; @@ -185,6 +184,4 @@ export class PreparationConnectedDeviceComponent { } public ngOnInit() {} - - } diff --git a/src/app/settings/settings.page.ts b/src/app/settings/settings.page.ts index c5047fb9..5495aa60 100644 --- a/src/app/settings/settings.page.ts +++ b/src/app/settings/settings.page.ts @@ -1227,7 +1227,7 @@ export class SettingsPage { const allXeniaPreps = []; let allPreparations = this.uiPreparationStorage.getAllEntries(); // Just take 60, else the excel will be exploding. - allPreparations = allPreparations.reverse().slice(0,60); + allPreparations = allPreparations.reverse().slice(0, 60); for (const prep of allPreparations) { if ( prep.connectedPreparationDevice.type === PreparationDeviceType.XENIA @@ -1278,7 +1278,7 @@ export class SettingsPage { } } - public importBeansExcel(_type: string='roasted'): void { + public importBeansExcel(_type: string = 'roasted'): void { if (this.platform.is('cordova')) { this.uiAnalytics.trackEvent( SETTINGS_TRACKING.TITLE, @@ -1295,12 +1295,11 @@ export class SettingsPage { this.uiFileHelper.readFileEntryAsArrayBuffer(fileEntry).then( async (_arrayBuffer) => { - if (_type ==='roasted') { + if (_type === 'roasted') { this.uiExcel.importBeansByExcel(_arrayBuffer); - }else { + } else { this.uiExcel.importGreenBeansByExcel(_arrayBuffer); } - }, () => { // Backup, maybe it was a .JSON? @@ -1326,7 +1325,7 @@ export class SettingsPage { } this.uiFileHelper.readFileAsArrayBuffer(path, file).then( async (_arrayBuffer) => { - if (_type ==='roasted') { + if (_type === 'roasted') { this.uiExcel.importBeansByExcel(_arrayBuffer); } else { this.uiExcel.importGreenBeansByExcel(_arrayBuffer); diff --git a/src/classes/devices/argosThermometer.ts b/src/classes/devices/argosThermometer.ts index 5b04c434..8957aadc 100644 --- a/src/classes/devices/argosThermometer.ts +++ b/src/classes/devices/argosThermometer.ts @@ -23,7 +23,9 @@ export class ArgosThermometer extends TemperatureDevice { return ( device && device.name && - device.name.toLowerCase().startsWith(ArgosThermometer.DEVICE_NAME.toLowerCase()) + device.name + .toLowerCase() + .startsWith(ArgosThermometer.DEVICE_NAME.toLowerCase()) ); } @@ -42,7 +44,7 @@ export class ArgosThermometer extends TemperatureDevice { ArgosThermometer.TEMPERATURE_CHAR_UUID, async (_data: any) => { - let newData = new Uint8Array(_data.slice(0,-1)); + let newData = new Uint8Array(_data.slice(0, -1)); this.parseStatusUpdate(newData); }, @@ -55,8 +57,9 @@ export class ArgosThermometer extends TemperatureDevice { 'temperatureRawStatus received is: ' + temperatureRawStatus ); - const temperature_in_f = temperatureRawStatus[-1] << 8 + temperatureRawStatus[-2]; -console.log("New temperature inc" + temperature_in_f); + const temperature_in_f = + temperatureRawStatus[-1] << (8 + temperatureRawStatus[-2]); + console.log('New temperature inc' + temperature_in_f); this.setTemperature(temperature_in_f, temperatureRawStatus); } diff --git a/src/classes/devices/index.ts b/src/classes/devices/index.ts index c6657b1c..34c3e63b 100644 --- a/src/classes/devices/index.ts +++ b/src/classes/devices/index.ts @@ -57,7 +57,7 @@ export enum TemperatureType { BASICGRILL = 'BASICGRILL', MEATER = 'MEATER', COMBUSTION = 'COMBUSTION', - ARGOS = 'ARGOS' + ARGOS = 'ARGOS', } export enum RefractometerType { diff --git a/src/classes/preparation/preparation.ts b/src/classes/preparation/preparation.ts index d163ddfb..d6d35f76 100755 --- a/src/classes/preparation/preparation.ts +++ b/src/classes/preparation/preparation.ts @@ -311,10 +311,6 @@ export class Preparation implements IPreparation { return this.attachments && this.attachments.length > 0; } public hasDeviceConnection(): boolean { - return ( - this.connectedPreparationDevice?.type !== - PreparationDeviceType.NONE - ); + return this.connectedPreparationDevice?.type !== PreparationDeviceType.NONE; } - } diff --git a/src/classes/preparationDevice/meticulous/meticulousDevice.ts b/src/classes/preparationDevice/meticulous/meticulousDevice.ts index 5dae48b3..25a0daf6 100644 --- a/src/classes/preparationDevice/meticulous/meticulousDevice.ts +++ b/src/classes/preparationDevice/meticulous/meticulousDevice.ts @@ -10,7 +10,13 @@ import { catchError, timeout } from 'rxjs/operators'; import { of } from 'rxjs'; import { HistoryListingEntry } from '@meticulous-home/espresso-api/dist/types'; import moment from 'moment'; -import { BrewFlow, IBrewPressureFlow, IBrewRealtimeWaterFlow, IBrewTemperatureFlow, IBrewWeightFlow } from '../../brew/brewFlow'; +import { + BrewFlow, + IBrewPressureFlow, + IBrewRealtimeWaterFlow, + IBrewTemperatureFlow, + IBrewWeightFlow, +} from '../../brew/brewFlow'; declare var cordova; declare var io; @@ -23,7 +29,6 @@ export class MeticulousDevice extends PreparationDevice { private _profiles: Array = []; - private serverURL: string = ''; public static returnBrewFlowForShotData(_shotData) { @@ -141,7 +146,7 @@ export class MeticulousDevice extends PreparationDevice { const profile = profileResponse.data as unknown as Profile; return profile; - /* if (profileResponse instanceof Profile) { + /* if (profileResponse instanceof Profile) { } else { }*/ @@ -150,16 +155,12 @@ export class MeticulousDevice extends PreparationDevice { } public async getHistoryShortListing() { - const response = await this.metApi.getHistoryShortListing(); - if (response && response.data && response.data.history) - { - return response.data.history as Array; + if (response && response.data && response.data.history) { + return response.data.history as Array; } } - - public async searchHistory() { const response = await this.metApi.searchHistory({ query: '', @@ -167,12 +168,11 @@ export class MeticulousDevice extends PreparationDevice { start_date: '', end_date: '', order_by: ['date'], - sort: 'desc', + sort: 'desc', max_results: 10, - dump_data: true - } ); - if (response && response.data && response.data.history) - { + dump_data: true, + }); + if (response && response.data && response.data.history) { return response.data.history as Array; } } @@ -182,7 +182,6 @@ export class MeticulousDevice extends PreparationDevice { const loadProfile = await this.metApi.loadProfileByID(_profileId); const profile = loadProfile.data as unknown as Profile; return profile; - } catch (ex) { console.log(ex.message); } @@ -208,7 +207,6 @@ export class MeticulousDevice extends PreparationDevice { if (this._profiles.length <= 0) { const profiles = await this.metApi.listProfiles(); this._profiles = profiles.data as Array; - } } catch (ex) {} } @@ -218,7 +216,7 @@ export class MeticulousDevice extends PreparationDevice { } public override async deviceConnected(): Promise { - const promise = new Promise(async(resolve, reject) => { + const promise = new Promise(async (resolve, reject) => { const settings: any = await this.metApi.getSettings(); if (settings?.data?.config) { resolve(true); diff --git a/src/classes/preparationDevice/sanremo/sanremoYOUDevice.ts b/src/classes/preparationDevice/sanremo/sanremoYOUDevice.ts index d110aa9d..c36b1dad 100644 --- a/src/classes/preparationDevice/sanremo/sanremoYOUDevice.ts +++ b/src/classes/preparationDevice/sanremo/sanremoYOUDevice.ts @@ -7,12 +7,11 @@ declare var cordova; export class SanremoYOUDevice extends PreparationDevice { public scriptList: Array<{ INDEX: number; TITLE: string }> = []; - - private connectionURL: string =''; + private connectionURL: string = ''; constructor(protected httpClient: HttpClient, _preparation: Preparation) { super(httpClient, _preparation); - this.connectionURL =this.getPreparation().connectedPreparationDevice.url; + this.connectionURL = this.getPreparation().connectedPreparationDevice.url; } public async deviceConnected(): Promise { @@ -104,7 +103,7 @@ export class SanremoYOUDevice extends PreparationDevice { /**{"status":1,"description":"ON","statusPhase":0,"alarms":0,"warnings":2,"tempBoilerCoffe":82.1,"tempBolierServices":100.2,"pumpServicesPress":0.2,"pumpPress":0.0,"counterVol":0,"realtimeFlow":0,"setPressPaddle":0.0}**/ const parsedJSON = JSON.parse(response.data); const temp = parsedJSON.tempBoilerCoffe; - const press = parsedJSON.pumpPress*10; + const press = parsedJSON.pumpPress * 10; this.temperature = temp; this.pressure = press; @@ -119,9 +118,6 @@ export class SanremoYOUDevice extends PreparationDevice { ); } - - - public startShot() { const promise = new Promise((resolve, reject) => { const options = { diff --git a/src/classes/settings/settings.ts b/src/classes/settings/settings.ts index 6ff1711d..a247d49f 100755 --- a/src/classes/settings/settings.ts +++ b/src/classes/settings/settings.ts @@ -98,7 +98,6 @@ export class Settings implements ISettings { ARCHIVED: boolean; }; - public green_bean_sort: { OPEN: IBeanPageSort; ARCHIVED: IBeanPageSort; diff --git a/src/components/brew-information/brew-information.component.ts b/src/components/brew-information/brew-information.component.ts index 28d1a2b5..ea234ce8 100644 --- a/src/components/brew-information/brew-information.component.ts +++ b/src/components/brew-information/brew-information.component.ts @@ -34,7 +34,6 @@ import { BrewTrackingService } from '../../services/brewTracking/brew-tracking.s import { UIHealthKit } from '../../services/uiHealthKit'; import * as htmlToImage from 'html-to-image'; - import { UIFileHelper } from '../../services/uiFileHelper'; import { BrewFlow } from '../../classes/brew/brewFlow'; import { UIBeanStorage } from '../../services/uiBeanStorage'; diff --git a/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts b/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts index d1800295..3f54feab 100644 --- a/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts +++ b/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts @@ -8,15 +8,19 @@ import { NgZone, OnInit, Output, - ViewChild + ViewChild, } from '@angular/core'; import { PREPARATION_STYLE_TYPE } from '../../../enums/preparations/preparationStyleTypes'; import { PreparationDeviceType } from '../../../classes/preparationDevice'; -import { BluetoothScale, SCALE_TIMER_COMMAND, ScaleType } from '../../../classes/devices'; +import { + BluetoothScale, + SCALE_TIMER_COMMAND, + ScaleType, +} from '../../../classes/devices'; import { ModalController, Platform } from '@ionic/angular'; import { CoffeeBluetoothDevicesService, - CoffeeBluetoothServiceEvent + CoffeeBluetoothServiceEvent, } from '../../../services/coffeeBluetoothDevices/coffee-bluetooth-devices.service'; import { TemperatureDevice } from '../../../classes/devices/temperatureBluetoothDevice'; import { PressureDevice } from '../../../classes/devices/pressureBluetoothDevice'; @@ -29,7 +33,7 @@ import { IBrewRealtimeWaterFlow, IBrewTemperatureFlow, IBrewWaterFlow, - IBrewWeightFlow + IBrewWeightFlow, } from '../../../classes/brew/brewFlow'; import { Preparation } from '../../../classes/preparation/preparation'; import { UIPreparationStorage } from '../../../services/uiPreparationStorage'; @@ -65,7 +69,7 @@ declare var Plotly; @Component({ selector: 'brew-brewing-graph', templateUrl: './brew-brewing-graph.component.html', - styleUrls: ['./brew-brewing-graph.component.scss'] + styleUrls: ['./brew-brewing-graph.component.scss'], }) export class BrewBrewingGraphComponent implements OnInit { @ViewChild('smartScaleWeight', { read: ElementRef }) @@ -171,8 +175,7 @@ export class BrewBrewingGraphComponent implements OnInit { public readonly uiBrewHelper: UIBrewHelper, private readonly uiGraphStorage: UIGraphStorage, private readonly textToSpeech: TextToSpeechService - ) { - } + ) {} public ngOnInit() { this.settings = this.uiSettingsStorage.getSettings(); @@ -205,7 +208,7 @@ export class BrewBrewingGraphComponent implements OnInit { if ( this.pressureDeviceConnected() && this.data.getPreparation().style_type === - PREPARATION_STYLE_TYPE.ESPRESSO + PREPARATION_STYLE_TYPE.ESPRESSO ) { await this.__connectPressureDevice(true); isSomethingConnected = true; @@ -217,7 +220,7 @@ export class BrewBrewingGraphComponent implements OnInit { if ( this.brewComponent?.brewBrewingPreparationDeviceEl?.preparationDeviceConnected() && this.brewComponent?.brewBrewingPreparationDeviceEl?.getPreparationDeviceType() === - PreparationDeviceType.METICULOUS + PreparationDeviceType.METICULOUS ) { isSomethingConnected = true; } @@ -360,7 +363,7 @@ export class BrewBrewingGraphComponent implements OnInit { this.temperatureDeviceConnected() === true || (this.pressureDeviceConnected() === true && this.data.getPreparation()?.style_type === - PREPARATION_STYLE_TYPE.ESPRESSO) || + PREPARATION_STYLE_TYPE.ESPRESSO) || this.brewComponent?.brewBrewingPreparationDeviceEl ?.preparationDevice !== undefined ) { @@ -393,8 +396,7 @@ export class BrewBrewingGraphComponent implements OnInit { this.bleManager.getPressureDevice(); try { pressureDevice.updateZero(); - } catch (ex) { - } + } catch (ex) {} } } @@ -402,7 +404,7 @@ export class BrewBrewingGraphComponent implements OnInit { if ( this.brewComponent?.brewBrewingPreparationDeviceEl?.preparationDeviceConnected() && this.brewComponent?.brewBrewingPreparationDeviceEl?.getPreparationDeviceType() === - PreparationDeviceType.METICULOUS + PreparationDeviceType.METICULOUS ) { return 3; } @@ -494,8 +496,7 @@ export class BrewBrewingGraphComponent implements OnInit { setTimeout(() => { try { Plotly.purge(this.profileDiv.nativeElement); - } catch (ex) { - } + } catch (ex) {} this.graphSettings = this.uiHelper.cloneData(this.settings.graph.FILTER); if ( this.data.getPreparation().style_type === @@ -528,11 +529,11 @@ export class BrewBrewingGraphComponent implements OnInit { line: { shape: 'linear', color: '#ebe6dd', - width: 2 + width: 2, }, visible: this.graphSettings.weight, hoverinfo: this.isDetail ? 'all' : 'skip', - showlegend: false + showlegend: false, }; this.flowPerSecondTraceReference = { x: [], @@ -544,11 +545,11 @@ export class BrewBrewingGraphComponent implements OnInit { line: { shape: 'linear', color: '#cbd5d9', - width: 2 + width: 2, }, visible: this.graphSettings.calc_flow, hoverinfo: this.isDetail ? 'all' : 'skip', - showlegend: false + showlegend: false, }; this.realtimeFlowTraceReference = { @@ -561,11 +562,11 @@ export class BrewBrewingGraphComponent implements OnInit { line: { shape: 'linear', color: '#9cb5be', - width: 2 + width: 2, }, visible: this.graphSettings.realtime_flow, hoverinfo: this.isDetail ? 'all' : 'skip', - showlegend: false + showlegend: false, }; this.pressureTraceReference = { @@ -578,11 +579,11 @@ export class BrewBrewingGraphComponent implements OnInit { line: { shape: 'linear', color: '#9be8d3', - width: 2 + width: 2, }, visible: this.graphSettings.pressure, hoverinfo: this.isDetail ? 'all' : 'skip', - showlegend: false + showlegend: false, }; this.temperatureTraceReference = { @@ -595,11 +596,11 @@ export class BrewBrewingGraphComponent implements OnInit { line: { shape: 'linear', color: '#eaad9f', - width: 2 + width: 2, }, visible: this.graphSettings.temperature, hoverinfo: this.isDetail ? 'all' : 'skip', - showlegend: false + showlegend: false, }; const presetFlowProfile = this.reference_profile_raw; @@ -631,7 +632,7 @@ export class BrewBrewingGraphComponent implements OnInit { this.weightTraceReference.x.push( new Date( moment(data.timestamp, 'HH:mm:ss.SSS').toDate().getTime() - - delay + delay ) ); this.weightTraceReference.y.push(data.actual_weight); @@ -640,7 +641,7 @@ export class BrewBrewingGraphComponent implements OnInit { this.flowPerSecondTraceReference.x.push( new Date( moment(data.timestamp, 'HH:mm:ss.SSS').toDate().getTime() - - delay + delay ) ); this.flowPerSecondTraceReference.y.push(data.value); @@ -650,7 +651,7 @@ export class BrewBrewingGraphComponent implements OnInit { this.realtimeFlowTraceReference.x.push( new Date( moment(data.timestamp, 'HH:mm:ss.SSS').toDate().getTime() - - delay + delay ) ); this.realtimeFlowTraceReference.y.push(data.flow_value); @@ -666,7 +667,7 @@ export class BrewBrewingGraphComponent implements OnInit { this.pressureTraceReference.x.push( new Date( moment(data.timestamp, 'HH:mm:ss.SSS').toDate().getTime() - - delay + delay ) ); this.pressureTraceReference.y.push(data.actual_pressure); @@ -681,7 +682,7 @@ export class BrewBrewingGraphComponent implements OnInit { this.temperatureTraceReference.x.push( new Date( moment(data.timestamp, 'HH:mm:ss.SSS').toDate().getTime() - - delay + delay ) ); this.temperatureTraceReference.y.push(data.actual_temperature); @@ -700,11 +701,11 @@ export class BrewBrewingGraphComponent implements OnInit { line: { shape: 'linear', color: '#cdc2ac', - width: 2 + width: 2, }, visible: this.graphSettings.weight, hoverinfo: this.isDetail ? 'all' : 'skip', - showlegend: false + showlegend: false, }; this.flowPerSecondTrace = { x: [], @@ -716,11 +717,11 @@ export class BrewBrewingGraphComponent implements OnInit { line: { shape: 'linear', color: '#7F97A2', - width: 2 + width: 2, }, visible: this.graphSettings.calc_flow, hoverinfo: this.isDetail ? 'all' : 'skip', - showlegend: false + showlegend: false, }; this.realtimeFlowTrace = { @@ -733,11 +734,11 @@ export class BrewBrewingGraphComponent implements OnInit { line: { shape: 'linear', color: '#09485D', - width: 2 + width: 2, }, visible: this.graphSettings.realtime_flow, hoverinfo: this.isDetail ? 'all' : 'skip', - showlegend: false + showlegend: false, }; this.pressureTrace = { @@ -750,11 +751,11 @@ export class BrewBrewingGraphComponent implements OnInit { line: { shape: 'linear', color: '#05C793', - width: 2 + width: 2, }, visible: this.graphSettings.pressure, hoverinfo: this.isDetail ? 'all' : 'skip', - showlegend: false + showlegend: false, }; this.temperatureTrace = { @@ -767,11 +768,11 @@ export class BrewBrewingGraphComponent implements OnInit { line: { shape: 'linear', color: '#CC3311', - width: 2 + width: 2, }, visible: this.graphSettings.temperature, hoverinfo: this.isDetail ? 'all' : 'skip', - showlegend: false + showlegend: false, }; if ( @@ -798,7 +799,7 @@ export class BrewBrewingGraphComponent implements OnInit { this.weightTrace.x.push( new Date( moment(data.timestamp, 'HH:mm:ss.SSS').toDate().getTime() - - delay + delay ) ); this.weightTrace.y.push(data.actual_weight); @@ -807,7 +808,7 @@ export class BrewBrewingGraphComponent implements OnInit { this.flowPerSecondTrace.x.push( new Date( moment(data.timestamp, 'HH:mm:ss.SSS').toDate().getTime() - - delay + delay ) ); this.flowPerSecondTrace.y.push(data.value); @@ -817,7 +818,7 @@ export class BrewBrewingGraphComponent implements OnInit { this.realtimeFlowTrace.x.push( new Date( moment(data.timestamp, 'HH:mm:ss.SSS').toDate().getTime() - - delay + delay ) ); this.realtimeFlowTrace.y.push(data.flow_value); @@ -832,7 +833,7 @@ export class BrewBrewingGraphComponent implements OnInit { this.pressureTrace.x.push( new Date( moment(data.timestamp, 'HH:mm:ss.SSS').toDate().getTime() - - delay + delay ) ); this.pressureTrace.y.push(data.actual_pressure); @@ -846,7 +847,7 @@ export class BrewBrewingGraphComponent implements OnInit { this.temperatureTrace.x.push( new Date( moment(data.timestamp, 'HH:mm:ss.SSS').toDate().getTime() - - delay + delay ) ); this.temperatureTrace.y.push(data.actual_temperature); @@ -889,8 +890,7 @@ export class BrewBrewingGraphComponent implements OnInit { this.updateChart(); } }, 250); - } catch (ex) { - } + } catch (ex) {} }, timeout); } @@ -911,8 +911,7 @@ export class BrewBrewingGraphComponent implements OnInit { weightEl.textContent = this.data.brew_quantity + ' g'; } } - } catch (ex) { - } + } catch (ex) {} }, 350); } @@ -920,8 +919,7 @@ export class BrewBrewingGraphComponent implements OnInit { let chartWidth: number = 300; try { chartWidth = this.canvaContainer.nativeElement.offsetWidth; - } catch (ex) { - } + } catch (ex) {} const chartHeight: number = 150; const tickFormat = '%M:%S'; @@ -990,7 +988,7 @@ export class BrewBrewingGraphComponent implements OnInit { r: 20, b: 20, t: 20, - pad: 2 + pad: 2, }, showlegend: false, dragmode: false, @@ -1004,7 +1002,7 @@ export class BrewBrewingGraphComponent implements OnInit { domain: [0, 1], fixedrange: true, type: 'date', - range: [startRange, endRange] + range: [startRange, endRange], }, yaxis: { title: '', @@ -1014,7 +1012,7 @@ export class BrewBrewingGraphComponent implements OnInit { side: 'left', position: 0.03, rangemode: 'nonnegative', - range: [suggestedMinWeight, suggestedMaxWeight] + range: [suggestedMinWeight, suggestedMaxWeight], }, yaxis2: { title: '', @@ -1027,14 +1025,14 @@ export class BrewBrewingGraphComponent implements OnInit { position: 0.97, fixedrange: true, rangemode: 'nonnegative', - range: [suggestedMinFlow, suggestedMaxFlow] - } + range: [suggestedMinFlow, suggestedMaxFlow], + }, }; const pressureDevice = this.bleManager.getPressureDevice(); if ( (pressureDevice != null && this.getPreparation().style_type === - PREPARATION_STYLE_TYPE.ESPRESSO) || + PREPARATION_STYLE_TYPE.ESPRESSO) || this.brewComponent?.brewBrewingPreparationDeviceEl?.preparationDeviceConnected() || !this.platform.is('cordova') ) { @@ -1048,7 +1046,7 @@ export class BrewBrewingGraphComponent implements OnInit { showgrid: false, position: 0.91, fixedrange: true, - range: [0, 10] + range: [0, 10], }; } const temperatureDevice = this.bleManager.getTemperatureDevice(); @@ -1068,7 +1066,7 @@ export class BrewBrewingGraphComponent implements OnInit { position: 0.8, fixedrange: true, visible: false, - range: [0, 100] + range: [0, 100], }; } } else { @@ -1080,14 +1078,14 @@ export class BrewBrewingGraphComponent implements OnInit { r: 20, b: 20, t: 20, - pad: 2 + pad: 2, }, showlegend: false, xaxis: { tickformat: tickFormat, visible: true, domain: [0, 1], - type: 'date' + type: 'date', }, yaxis: { title: '', @@ -1095,7 +1093,7 @@ export class BrewBrewingGraphComponent implements OnInit { tickfont: { color: '#cdc2ac' }, side: 'left', position: 0.05, - visible: true + visible: true, }, yaxis2: { title: '', @@ -1106,8 +1104,8 @@ export class BrewBrewingGraphComponent implements OnInit { side: 'right', position: 0.95, showgrid: false, - visible: true - } + visible: true, + }, }; layout['yaxis4'] = { @@ -1120,7 +1118,7 @@ export class BrewBrewingGraphComponent implements OnInit { showgrid: false, position: 0.93, range: [0, 12], - visible: true + visible: true, }; layout['yaxis5'] = { @@ -1134,7 +1132,7 @@ export class BrewBrewingGraphComponent implements OnInit { position: 0.8, fixedrange: true, range: [0, 100], - visible: true + visible: true, }; if (this.weightTrace.x && this.weightTrace.x.length > 0) { @@ -1164,14 +1162,14 @@ export class BrewBrewingGraphComponent implements OnInit { if (this.isDetail === true) { const config = { displayModeBar: false, // this is the line that hides the bar. - responsive: true + responsive: true, }; return config; } else { const config = { responsive: false, scrollZoom: false, - displayModeBar: false // this is the line that hides the bar. + displayModeBar: false, // this is the line that hides the bar. }; return config; } @@ -1255,8 +1253,7 @@ export class BrewBrewingGraphComponent implements OnInit { this.brewComponent.timer.reset(); this.checkChanges(); }, - () => { - } + () => {} ); break; @@ -1338,8 +1335,8 @@ export class BrewBrewingGraphComponent implements OnInit { try { return this.uiHelper.toFixedIfNecessary( this.flow_profile_raw.realtimeFlow[ - this.flow_profile_raw.realtimeFlow.length - 1 - ].flow_value, + this.flow_profile_raw.realtimeFlow.length - 1 + ].flow_value, 2 ); } catch (ex) { @@ -1351,7 +1348,7 @@ export class BrewBrewingGraphComponent implements OnInit { this.ngZone.runOutsideAngular(() => { if (this.brewComponent.maximizeFlowGraphIsShown === true) { this.brewComponent.brewTemperatureGraphSubject.next({ - temperature: _temperature + temperature: _temperature, }); } @@ -1359,8 +1356,7 @@ export class BrewBrewingGraphComponent implements OnInit { const temperatureEl = this.temperatureEl.nativeElement; temperatureEl.textContent = _temperature; - } catch (ex) { - } + } catch (ex) {} }); } @@ -1368,7 +1364,7 @@ export class BrewBrewingGraphComponent implements OnInit { this.ngZone.runOutsideAngular(() => { if (this.brewComponent.maximizeFlowGraphIsShown === true) { this.brewComponent.brewPressureGraphSubject.next({ - pressure: _pressure + pressure: _pressure, }); } @@ -1376,8 +1372,7 @@ export class BrewBrewingGraphComponent implements OnInit { const pressureEl = this.pressureEl.nativeElement; pressureEl.textContent = _pressure; - } catch (ex) { - } + } catch (ex) {} }); } @@ -1406,7 +1401,7 @@ export class BrewBrewingGraphComponent implements OnInit { this.brewComponent.brewFlowGraphSubject.next({ scaleWeight: actualScaleWeight, smoothedWeight: actualSmoothedWeightPerSecond, - avgFlow: avgFlow + avgFlow: avgFlow, }); } @@ -1418,8 +1413,7 @@ export class BrewBrewingGraphComponent implements OnInit { weightEl.textContent = actualScaleWeight + ' g'; flowEl.textContent = actualSmoothedWeightPerSecond + ' g/s'; avgFlowEl.textContent = 'Ø ' + avgFlow + ' g/s'; - } catch (ex) { - } + } catch (ex) {} }); } @@ -1455,7 +1449,7 @@ export class BrewBrewingGraphComponent implements OnInit { this.profileDiv.nativeElement, { x: xData, - y: yData + y: yData, }, tracesData ); @@ -1607,8 +1601,7 @@ export class BrewBrewingGraphComponent implements OnInit { this.lastChartLayout.xaxis.range = [delay, delayedTime]; Plotly.relayout(this.profileDiv.nativeElement, this.lastChartLayout); } - } catch (ex) { - } + } catch (ex) {} }); } @@ -1696,7 +1689,7 @@ export class BrewBrewingGraphComponent implements OnInit { temperatureDevice || (pressureDevice && this.data.getPreparation().style_type === - PREPARATION_STYLE_TYPE.ESPRESSO) + PREPARATION_STYLE_TYPE.ESPRESSO) ) { this.initializeFlowChart(false); } @@ -1760,7 +1753,7 @@ export class BrewBrewingGraphComponent implements OnInit { if ( this.brewComponent?.brewBrewingPreparationDeviceEl?.preparationDeviceConnected() && this.brewComponent?.brewBrewingPreparationDeviceEl?.getPreparationDeviceType() === - PreparationDeviceType.XENIA && + PreparationDeviceType.XENIA && this.machineStopScriptWasTriggered === false ) { this.machineStopScriptWasTriggered = true; @@ -1788,8 +1781,9 @@ export class BrewBrewingGraphComponent implements OnInit { if ( this.brewComponent?.brewBrewingPreparationDeviceEl?.preparationDeviceConnected() && this.brewComponent?.brewBrewingPreparationDeviceEl?.getPreparationDeviceType() === - PreparationDeviceType.SANREMO_YOU && - _event !== 'sanremo_you' && this.machineStopScriptWasTriggered === false + PreparationDeviceType.SANREMO_YOU && + _event !== 'sanremo_you' && + this.machineStopScriptWasTriggered === false ) { this.machineStopScriptWasTriggered = true; this.uiLog.log(`Sanremo YOU - Pause button pressed, stop shot`); @@ -1808,7 +1802,7 @@ export class BrewBrewingGraphComponent implements OnInit { if ( this.brewComponent?.brewBrewingPreparationDeviceEl?.preparationDeviceConnected() && this.brewComponent?.brewBrewingPreparationDeviceEl?.getPreparationDeviceType() === - PreparationDeviceType.METICULOUS && + PreparationDeviceType.METICULOUS && _event !== 'meticulous' ) { this.stopFetchingDataFromMeticulous(); @@ -1880,10 +1874,8 @@ export class BrewBrewingGraphComponent implements OnInit { //before we start the interval, we fetch the data once to overwrite, and set them. setSanremoData(); }); - } catch (ex) { - } + } catch (ex) {} }, 100); - } public startFetchingDataFromMeticulous() { @@ -1941,18 +1933,18 @@ export class BrewBrewingGraphComponent implements OnInit { if (hasShotStarted) { this.__setPressureFlow({ actual: shotData.pressure, - old: shotData.pressure + old: shotData.pressure, }); this.__setTemperatureFlow({ actual: shotData.temperature, - old: shotData.temperature + old: shotData.temperature, }); this.__setFlowProfile({ actual: shotData.weight, old: shotData.oldWeight, smoothed: shotData.smoothedWeight, - oldSmoothed: shotData.oldSmoothedWeight + oldSmoothed: shotData.oldSmoothedWeight, }); //this.__setMachineWeightFlow({ actual: shotData.weight, old: shotData.weight,smoothed:100,oldSmoothed:100 }); @@ -1999,8 +1991,7 @@ export class BrewBrewingGraphComponent implements OnInit { 2 ); } - } catch (ex) { - } + } catch (ex) {} }); }, 100); @@ -2011,8 +2002,7 @@ export class BrewBrewingGraphComponent implements OnInit { //before we start the interval, we fetch the data once to overwrite, and set them. setTempAndPressure(); }); - } catch (ex) { - } + } catch (ex) {} }, 500); } @@ -2150,7 +2140,7 @@ export class BrewBrewingGraphComponent implements OnInit { actual: weight, old: 1, smoothed: 1, - oldSmoothed: 1 + oldSmoothed: 1, }); this.setActualSmartInformation(); }, 100); @@ -2170,7 +2160,7 @@ export class BrewBrewingGraphComponent implements OnInit { } else { if ( this.data.getPreparation().style_type === - PREPARATION_STYLE_TYPE.ESPRESSO && + PREPARATION_STYLE_TYPE.ESPRESSO && pressureDevice ) { this.brewComponent.maximizeFlowGraph(); @@ -2232,7 +2222,7 @@ export class BrewBrewingGraphComponent implements OnInit { if ( this.brewComponent?.brewBrewingPreparationDeviceEl?.preparationDeviceConnected() && this.brewComponent?.brewBrewingPreparationDeviceEl?.getPreparationDeviceType() === - PreparationDeviceType.XENIA + PreparationDeviceType.XENIA ) { if (this.data.preparationDeviceBrew.params.scriptStartId > 0) { this.uiLog.log(`Xenia Script - Script start - Trigger script`); @@ -2253,7 +2243,7 @@ export class BrewBrewingGraphComponent implements OnInit { (_jsonResp: any) => { this.uiLog.log( 'Check of xenia answer if script started MA_STATUS: ' + - _jsonResp.MA_Status + _jsonResp.MA_Status ); if (_jsonResp.MA_STATUS === 3) { //Great we started. @@ -2292,7 +2282,7 @@ export class BrewBrewingGraphComponent implements OnInit { } else if ( this.brewComponent?.brewBrewingPreparationDeviceEl?.preparationDeviceConnected() && this.brewComponent?.brewBrewingPreparationDeviceEl?.getPreparationDeviceType() === - PreparationDeviceType.METICULOUS + PreparationDeviceType.METICULOUS ) { const prepDeviceCall: MeticulousDevice = this.brewComponent .brewBrewingPreparationDeviceEl.preparationDevice as MeticulousDevice; @@ -2319,15 +2309,14 @@ export class BrewBrewingGraphComponent implements OnInit { } this.startFetchingDataFromMeticulous(); - } else if ( this.brewComponent?.brewBrewingPreparationDeviceEl?.preparationDeviceConnected() && this.brewComponent?.brewBrewingPreparationDeviceEl?.getPreparationDeviceType() === - PreparationDeviceType.SANREMO_YOU + PreparationDeviceType.SANREMO_YOU ) { const prepDeviceCall: SanremoYOUDevice = this.brewComponent .brewBrewingPreparationDeviceEl.preparationDevice as SanremoYOUDevice; - prepDeviceCall.startShot() .catch((_msg) => { + prepDeviceCall.startShot().catch((_msg) => { this.uiLog.log('We could not start shot on sanremo: ' + _msg); this.uiToast.showInfoToast( 'We could not start shot on sanremo: ' + _msg, @@ -2343,7 +2332,6 @@ export class BrewBrewingGraphComponent implements OnInit { } this.startFetchingDataFromSanremoYOU(); - } } @@ -2539,8 +2527,8 @@ export class BrewBrewingGraphComponent implements OnInit { this.ngZone.runOutsideAngular(() => { this.textToSpeech.speak( this.translate.instant('TEXT_TO_SPEECH.TIME') + - ' ' + - this.data.brew_time + ' ' + + this.data.brew_time ); }); }, 5000); @@ -2585,8 +2573,7 @@ export class BrewBrewingGraphComponent implements OnInit { if (scale) { scale.tare(); } - } catch (ex) { - } + } catch (ex) {} } this.brewComponent.timerStartPressed('AUTO_START_PRESSURE'); @@ -2638,8 +2625,7 @@ export class BrewBrewingGraphComponent implements OnInit { }, this.settings.bluetooth_command_delay); }); } - } catch (ex) { - } + } catch (ex) {} } this.brewComponent.timerStartPressed('AUTO_START_TEMPERATURE'); @@ -2779,12 +2765,17 @@ export class BrewBrewingGraphComponent implements OnInit { old: oldWeight, smoothed: smoothedWeight, oldSmoothed: oldSmoothedWeight, - notMutatedWeight: notMutatedWeight + notMutatedWeight: notMutatedWeight, }; } - - private calculateBrewByWeight(_currentWeightValue: number, _residualLagTime: number, _targetWeight: number, _brewByWeightActive: boolean, _scale: BluetoothScale) { + private calculateBrewByWeight( + _currentWeightValue: number, + _residualLagTime: number, + _targetWeight: number, + _brewByWeightActive: boolean, + _scale: BluetoothScale + ) { let weight: number = this.uiHelper.toFixedIfNecessary( _currentWeightValue, 1 @@ -2793,10 +2784,7 @@ export class BrewBrewingGraphComponent implements OnInit { if (this.flowProfileTempAll.length > 0) { const oldFlowProfileTemp = this.flowProfileTempAll[this.flowProfileTempAll.length - 1]; - weight = this.uiHelper.toFixedIfNecessary( - oldFlowProfileTemp.weight, - 1 - ); + weight = this.uiHelper.toFixedIfNecessary(oldFlowProfileTemp.weight, 1); } } @@ -2804,12 +2792,10 @@ export class BrewBrewingGraphComponent implements OnInit { this.flow_profile_raw.realtimeFlow && this.flow_profile_raw.realtimeFlow.length > 0 ) { - const targetWeight = _targetWeight; const brewByWeightActive: boolean = _brewByWeightActive; - let n = 3; if (this.flowNCalculation > 0) { n = this.flowNCalculation; @@ -2818,7 +2804,6 @@ export class BrewBrewingGraphComponent implements OnInit { } const lag_time = this.uiHelper.toFixedIfNecessary(1 / n, 2); - let average_flow_rate = 0; let lastFlowValue = 0; @@ -2848,7 +2833,7 @@ export class BrewBrewingGraphComponent implements OnInit { weight, lag_time + _residualLagTime, weight + average_flow_rate * (lag_time + _residualLagTime) >= - targetWeight, + targetWeight, average_flow_rate * (lag_time + _residualLagTime), _residualLagTime, average_flow_rate, @@ -2857,8 +2842,7 @@ export class BrewBrewingGraphComponent implements OnInit { let thresholdHit: boolean = false; if (brewByWeightActive) { thresholdHit = - weight + - average_flow_rate * (lag_time + _residualLagTime) >= + weight + average_flow_rate * (lag_time + _residualLagTime) >= targetWeight; } else { thresholdHit = weight >= targetWeight; @@ -2870,50 +2854,37 @@ export class BrewBrewingGraphComponent implements OnInit { private triggerStopShotOnXenia(_actualScaleWeight) { const prepDeviceCall: XeniaDevice = this.brewComponent - .brewBrewingPreparationDeviceEl - .preparationDevice as XeniaDevice; - if ( - this.data.preparationDeviceBrew.params - .scriptAtWeightReachedId > 0 - ) { + .brewBrewingPreparationDeviceEl.preparationDevice as XeniaDevice; + if (this.data.preparationDeviceBrew.params.scriptAtWeightReachedId > 0) { this.uiLog.log( `Xenia Script - Weight Reached: ${_actualScaleWeight} - Trigger custom script` ); prepDeviceCall .startScript( - this.data.preparationDeviceBrew.params - .scriptAtWeightReachedId + this.data.preparationDeviceBrew.params.scriptAtWeightReachedId ) .catch((_msg) => { this.uiToast.showInfoToast( 'We could not start script at weight: ' + _msg, false ); - this.uiLog.log( - 'We could not start script at weight: ' + _msg - ); + this.uiLog.log('We could not start script at weight: ' + _msg); }); this.writeExecutionTimeToNotes( 'Weight reached script', - this.data.preparationDeviceBrew.params - .scriptAtWeightReachedId, + this.data.preparationDeviceBrew.params.scriptAtWeightReachedId, this.getScriptName( - this.data.preparationDeviceBrew.params - .scriptAtWeightReachedId + this.data.preparationDeviceBrew.params.scriptAtWeightReachedId ) ); } else { - this.uiLog.log( - `Xenia Script - Weight Reached - Trigger stop script` - ); + this.uiLog.log(`Xenia Script - Weight Reached - Trigger stop script`); prepDeviceCall.stopScript().catch((_msg) => { this.uiToast.showInfoToast( 'We could not stop script at weight: ' + _msg, false ); - this.uiLog.log( - 'We could not stop script at weight: ' + _msg - ); + this.uiLog.log('We could not stop script at weight: ' + _msg); }); this.writeExecutionTimeToNotes( 'Stop script (Weight reached)', @@ -2926,9 +2897,7 @@ export class BrewBrewingGraphComponent implements OnInit { // This will be just called once, we stopped the shot and now we check if we directly shall stop or not if ( - this.settings - .bluetooth_scale_espresso_stop_on_no_weight_change === - false + this.settings.bluetooth_scale_espresso_stop_on_no_weight_change === false ) { this.stopFetchingAndSettingDataFromXenia(); this.brewComponent.timer.pauseTimer('xenia'); @@ -2939,31 +2908,17 @@ export class BrewBrewingGraphComponent implements OnInit { private triggerStopShotOnSanremoYOU(_actualScaleWeight) { const prepDeviceCall: SanremoYOUDevice = this.brewComponent - .brewBrewingPreparationDeviceEl - .preparationDevice as SanremoYOUDevice; - - this.uiLog.log( - `Sanremo YOU Stop: ${_actualScaleWeight}` - ); - prepDeviceCall - .stopShot( + .brewBrewingPreparationDeviceEl.preparationDevice as SanremoYOUDevice; - ) - .catch((_msg) => { - this.uiToast.showInfoToast( - 'We could not stop at weight: ' + _msg, - false - ); - this.uiLog.log( - 'We could not start script at weight: ' + _msg - ); - }); + this.uiLog.log(`Sanremo YOU Stop: ${_actualScaleWeight}`); + prepDeviceCall.stopShot().catch((_msg) => { + this.uiToast.showInfoToast('We could not stop at weight: ' + _msg, false); + this.uiLog.log('We could not start script at weight: ' + _msg); + }); // This will be just called once, we stopped the shot and now we check if we directly shall stop or not if ( - this.settings - .bluetooth_scale_espresso_stop_on_no_weight_change === - false + this.settings.bluetooth_scale_espresso_stop_on_no_weight_change === false ) { this.stopFetchingDataFromSanremoYOU(); this.brewComponent.timer.pauseTimer('sanremo_you'); @@ -2972,7 +2927,6 @@ export class BrewBrewingGraphComponent implements OnInit { } } - public attachToScaleWeightChange() { const scale: BluetoothScale = this.bleManager.getScale(); const preparationStyleType = this.data.getPreparation().style_type; @@ -2986,24 +2940,26 @@ export class BrewBrewingGraphComponent implements OnInit { this.machineStopScriptWasTriggered = false; - const prepDeviceConnected = this.brewComponent.brewBrewingPreparationDeviceEl.preparationDeviceConnected(); + const prepDeviceConnected = + this.brewComponent.brewBrewingPreparationDeviceEl.preparationDeviceConnected(); let residual_lag_time = 1.35; let targetWeight = 0; let brewByWeightActive: boolean = false; let preparationDeviceType: PreparationDeviceType; if (prepDeviceConnected) { - preparationDeviceType = this.brewComponent.brewBrewingPreparationDeviceEl.getPreparationDeviceType(); + preparationDeviceType = + this.brewComponent.brewBrewingPreparationDeviceEl.getPreparationDeviceType(); switch (preparationDeviceType) { case PreparationDeviceType.XENIA: - const prepXeniaDeviceCall: XeniaDevice = this.brewComponent - .brewBrewingPreparationDeviceEl - .preparationDevice as XeniaDevice; + .brewBrewingPreparationDeviceEl.preparationDevice as XeniaDevice; residual_lag_time = prepXeniaDeviceCall.getResidualLagTime(); - targetWeight = this.data.preparationDeviceBrew.params - .scriptAtWeightReachedNumber; - brewByWeightActive = this.data.preparationDeviceBrew?.params?.brew_by_weight_active; + targetWeight = + this.data.preparationDeviceBrew.params + .scriptAtWeightReachedNumber; + brewByWeightActive = + this.data.preparationDeviceBrew?.params?.brew_by_weight_active; break; case PreparationDeviceType.SANREMO_YOU: const prepSanremoDeviceCall: SanremoYOUDevice = this.brewComponent @@ -3013,11 +2969,9 @@ export class BrewBrewingGraphComponent implements OnInit { targetWeight = this.data.preparationDeviceBrew.params.stopAtWeight; brewByWeightActive = true; break; - } } - this.scaleFlowSubscription = scale.flowChange.subscribe((_valChange) => { let _val; if (this.ignoreScaleWeight === false) { @@ -3031,10 +2985,20 @@ export class BrewBrewingGraphComponent implements OnInit { } if (this.brewComponent.timer.isTimerRunning() && prepDeviceConnected) { - - if (preparationDeviceType === PreparationDeviceType.XENIA && this.data.preparationDeviceBrew.params.scriptAtWeightReachedNumber > 0 && this.isFirstXeniaScriptSet()) { + if ( + preparationDeviceType === PreparationDeviceType.XENIA && + this.data.preparationDeviceBrew.params.scriptAtWeightReachedNumber > + 0 && + this.isFirstXeniaScriptSet() + ) { /**We call this function before the if, because we still log the data**/ - const thresholdHit = this.calculateBrewByWeight(_val.actual, residual_lag_time, targetWeight, brewByWeightActive, scale); + const thresholdHit = this.calculateBrewByWeight( + _val.actual, + residual_lag_time, + targetWeight, + brewByWeightActive, + scale + ); if (this.machineStopScriptWasTriggered === false) { if (thresholdHit) { @@ -3042,11 +3006,18 @@ export class BrewBrewingGraphComponent implements OnInit { this.triggerStopShotOnXenia(_val.actual); } } - } else if (this.brewComponent.brewBrewingPreparationDeviceEl.getPreparationDeviceType() === - PreparationDeviceType.SANREMO_YOU) { - + } else if ( + this.brewComponent.brewBrewingPreparationDeviceEl.getPreparationDeviceType() === + PreparationDeviceType.SANREMO_YOU + ) { /**We call this function before the if, because we still log the data**/ - const thresholdHit = this.calculateBrewByWeight(_val.actual, residual_lag_time, targetWeight, brewByWeightActive, scale); + const thresholdHit = this.calculateBrewByWeight( + _val.actual, + residual_lag_time, + targetWeight, + brewByWeightActive, + scale + ); if (this.machineStopScriptWasTriggered === false) { if (thresholdHit) { this.machineStopScriptWasTriggered = true; @@ -3054,7 +3025,6 @@ export class BrewBrewingGraphComponent implements OnInit { } } } - } if (this.ignoreScaleWeight === false) { this.__setFlowProfile(_val); @@ -3067,7 +3037,7 @@ export class BrewBrewingGraphComponent implements OnInit { old: oldFlowProfileTemp.oldWeight, smoothed: oldFlowProfileTemp.smoothedWeight, oldSmoothed: oldFlowProfileTemp.oldSmoothedWeight, - notMutatedWeight: _val.notMutatedWeight + notMutatedWeight: _val.notMutatedWeight, }; this.__setFlowProfile(passVal); } @@ -3114,8 +3084,7 @@ export class BrewBrewingGraphComponent implements OnInit { _flowProfile ); resolve(jsonParsed); - } catch (ex) { - } + } catch (ex) {} } } else { resolve(BeanconquerorFlowTestDataDummySecondDummy); @@ -3130,8 +3099,7 @@ export class BrewBrewingGraphComponent implements OnInit { try { const jsonParsed = await this.uiFileHelper.getJSONFile(flowProfilePath); this.flow_profile_raw = jsonParsed; - } catch (ex) { - } + } catch (ex) {} } private async deleteFlowProfile() { @@ -3141,8 +3109,7 @@ export class BrewBrewingGraphComponent implements OnInit { 'brews/' + this.data.config.uuid + '_flow_profile.json'; await this.uiFileHelper.deleteFile(flowProfilePath); } - } catch (ex) { - } + } catch (ex) {} } private __setMachineWaterFlow(_flow: any) { @@ -3163,7 +3130,7 @@ export class BrewBrewingGraphComponent implements OnInit { old: old, flowTime: this.flowTime, flowTimeSecond: this.flowTime + '.' + this.flowSecondTick, - flowTimestamp: this.uiHelper.getActualTimeWithMilliseconds() + flowTimestamp: this.uiHelper.getActualTimeWithMilliseconds(), }; const realtimeWaterFlow: IBrewRealtimeWaterFlow = @@ -3207,7 +3174,7 @@ export class BrewBrewingGraphComponent implements OnInit { actual: actual, old: old, flowTime: this.flowTime, - flowTimeSecond: this.flowTime + '.' + this.flowSecondTick + flowTimeSecond: this.flowTime + '.' + this.flowSecondTick, }; if (!isSmartScaleConnected) { @@ -3260,7 +3227,7 @@ export class BrewBrewingGraphComponent implements OnInit { actual: actual, old: old, flowTime: this.flowTime, - flowTimeSecond: this.flowTime + '.' + this.flowSecondTick + flowTimeSecond: this.flowTime + '.' + this.flowSecondTick, }; if (!isSmartScaleConnected) { @@ -3328,7 +3295,7 @@ export class BrewBrewingGraphComponent implements OnInit { flowTimeSecond: this.flowTime + '.' + this.flowSecondTick, flowTimestamp: this.uiHelper.getActualTimeWithMilliseconds(), dateUnixTime: undefined, - notMutatedWeight: notMutatedWeight + notMutatedWeight: notMutatedWeight, }; flowObj.dateUnixTime = new Date(flowObj.unixTime); @@ -3429,7 +3396,7 @@ export class BrewBrewingGraphComponent implements OnInit { lastVal - firstVal < 0.5 || (this.flowProfileArr.length > 2 && this.flowProfileArr[this.flowProfileArr.length - 2] - firstVal < - 0.5) + 0.5) ) { // Threshold for filter is bigger, 0.5g // Threshshold, weight changes because of strange thing happening. @@ -3593,8 +3560,7 @@ export class BrewBrewingGraphComponent implements OnInit { timeStampDelta = flowObj.unixTime - this.flowProfileTempAll[this.flowProfileTempAll.length - n].unixTime; - } catch (ex) { - } + } catch (ex) {} } realtimeWaterFlow.timestampdelta = timeStampDelta; @@ -3606,8 +3572,7 @@ export class BrewBrewingGraphComponent implements OnInit { this.flowProfileTempAll[this.flowProfileTempAll.length - n] .smoothedWeight) * (1000 / timeStampDelta); - } catch (ex) { - } + } catch (ex) {} if (Number.isNaN(calcFlowValue)) { calcFlowValue = 0; } @@ -3621,7 +3586,7 @@ export class BrewBrewingGraphComponent implements OnInit { if ( this.data.getPreparation().style_type === - PREPARATION_STYLE_TYPE.ESPRESSO && + PREPARATION_STYLE_TYPE.ESPRESSO && flowObj.weight >= this.settings.bluetooth_scale_first_drip_threshold ) { // If the drip timer is showing, we can set the first drip and not doing a reference to the normal weight. @@ -3669,14 +3634,14 @@ export class BrewBrewingGraphComponent implements OnInit { if ( this.brewComponent.brewBrewingPreparationDeviceEl.preparationDeviceConnected() && this.brewComponent.brewBrewingPreparationDeviceEl.getPreparationDeviceType() === - PreparationDeviceType.XENIA + PreparationDeviceType.XENIA ) { this.stopFetchingAndSettingDataFromXenia(); } if ( this.brewComponent.brewBrewingPreparationDeviceEl.preparationDeviceConnected() && this.brewComponent.brewBrewingPreparationDeviceEl.getPreparationDeviceType() === - PreparationDeviceType.SANREMO_YOU + PreparationDeviceType.SANREMO_YOU ) { this.stopFetchingDataFromSanremoYOU(); } @@ -3686,7 +3651,7 @@ export class BrewBrewingGraphComponent implements OnInit { if ( this.brewComponent.brewBrewingPreparationDeviceEl.preparationDeviceConnected() && this.brewComponent.brewBrewingPreparationDeviceEl.getPreparationDeviceType() === - PreparationDeviceType.METICULOUS + PreparationDeviceType.METICULOUS ) { isMeticulous = true; } @@ -3746,7 +3711,7 @@ export class BrewBrewingGraphComponent implements OnInit { if ( this.settings.bluetooth_scale_espresso_stop_on_no_weight_change === - false || + false || this.weightTrace.y.length < 50 || this.data.brew_time <= 5 ) { @@ -3867,8 +3832,7 @@ export class BrewBrewingGraphComponent implements OnInit { this.lastChartLayout.width = this.canvaContainer.nativeElement.offsetWidth; Plotly.relayout(this.profileDiv.nativeElement, this.lastChartLayout); - } catch (ex) { - } + } catch (ex) {} }, 50); } } @@ -3934,8 +3898,8 @@ export class BrewBrewingGraphComponent implements OnInit { component: BrewChooseGraphReferenceComponent, id: BrewChooseGraphReferenceComponent.COMPONENT_ID, componentProps: { - brew: this.data - } + brew: this.data, + }, }); // will force rerender :D @@ -3973,7 +3937,7 @@ export class BrewBrewingGraphComponent implements OnInit { if ( this.brewComponent?.brewBrewingPreparationDeviceEl?.preparationDeviceConnected() && this.brewComponent?.brewBrewingPreparationDeviceEl?.getPreparationDeviceType() === - PreparationDeviceType.XENIA && + PreparationDeviceType.XENIA && this.data.preparationDeviceBrew.params.scriptAtFirstDripId > 0 ) { if (this.isFirstXeniaScriptSet()) { @@ -4009,7 +3973,7 @@ export class BrewBrewingGraphComponent implements OnInit { !this.smartScaleConnected() && this.brewComponent?.brewBrewingPreparationDeviceEl?.preparationDeviceConnected() && this.brewComponent?.brewBrewingPreparationDeviceEl?.getPreparationDeviceType() === - PreparationDeviceType.XENIA + PreparationDeviceType.XENIA ) { // If scale is not connected but the device, we can now choose that still the script is executed if existing. if (this.isFirstXeniaScriptSet()) { @@ -4026,12 +3990,12 @@ export class BrewBrewingGraphComponent implements OnInit { .catch((_msg) => { this.uiToast.showInfoToast( 'We could not start script at first drip - manual triggered: ' + - _msg, + _msg, false ); this.uiLog.log( 'We could not start script at first drip - manual triggered: ' + - _msg + _msg ); }); this.writeExecutionTimeToNotes( @@ -4050,7 +4014,7 @@ export class BrewBrewingGraphComponent implements OnInit { if ( this.brewComponent?.brewBrewingPreparationDeviceEl?.preparationDeviceConnected() && this.brewComponent?.brewBrewingPreparationDeviceEl?.getPreparationDeviceType() === - PreparationDeviceType.XENIA + PreparationDeviceType.XENIA ) { if (this.data.preparationDeviceBrew.params.scriptStartId > 0) { return true; diff --git a/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.ts b/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.ts index 1b87af23..f84204e2 100644 --- a/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.ts +++ b/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.ts @@ -40,7 +40,10 @@ import { import { BrewModalImportShotMeticulousComponent } from '../../../app/brew/brew-modal-import-shot-meticulous/brew-modal-import-shot-meticulous.component'; import { ModalController } from '@ionic/angular'; import { HistoryListingEntry } from '@meticulous-home/espresso-api/dist/types'; -import { SanremoYOUDevice, SanremoYOUParams } from '../../../classes/preparationDevice/sanremo/sanremoYOUDevice'; +import { + SanremoYOUDevice, + SanremoYOUParams, +} from '../../../classes/preparationDevice/sanremo/sanremoYOUDevice'; @Component({ selector: 'brew-brewing-preparation-device', templateUrl: './brew-brewing-preparation-device.component.html', @@ -51,7 +54,8 @@ export class BrewBrewingPreparationDeviceComponent implements OnInit { @Input() public isEdit: boolean = false; @Output() public dataChange = new EventEmitter(); @Input() public brewComponent: BrewBrewingComponent; - public preparationDevice: XeniaDevice | MeticulousDevice | SanremoYOUDevice = undefined; + public preparationDevice: XeniaDevice | MeticulousDevice | SanremoYOUDevice = + undefined; public preparation: Preparation = undefined; public settings: Settings = undefined; @@ -72,8 +76,7 @@ export class BrewBrewingPreparationDeviceComponent implements OnInit { private readonly changeDetectorRef: ChangeDetectorRef, private readonly modalController: ModalController, public readonly uiBrewHelper: UIBrewHelper - ) { - } + ) {} public ngOnInit() { this.settings = this.uiSettingsStorage.getSettings(); @@ -109,8 +112,7 @@ export class BrewBrewingPreparationDeviceComponent implements OnInit { await this.instanceXeniaPreparationDevice(connectedDevice, _brew); } else if (connectedDevice instanceof MeticulousDevice) { await this.instanceMeticulousPreparationDevice(connectedDevice, _brew); - } - else if (connectedDevice instanceof SanremoYOUDevice) { + } else if (connectedDevice instanceof SanremoYOUDevice) { await this.instanceSanremoYOUPreparationDevice(connectedDevice, _brew); } this.checkChanges(); @@ -305,12 +307,14 @@ export class BrewBrewingPreparationDeviceComponent implements OnInit { ) { this.data.preparationDeviceBrew.type = PreparationDeviceType.SANREMO_YOU; this.data.preparationDeviceBrew.params = new SanremoYOUParams(); - await connectedDevice.deviceConnected().then(() => { - this.preparationDevice = connectedDevice as SanremoYOUDevice; - }, () => { - //Not connected - }); - + await connectedDevice.deviceConnected().then( + () => { + this.preparationDevice = connectedDevice as SanremoYOUDevice; + }, + () => { + //Not connected + } + ); } public async importShotFromMeticulous() { @@ -332,7 +336,6 @@ export class BrewBrewingPreparationDeviceComponent implements OnInit { } private generateShotFlowProfileFromMeticulousData(_historyData) { - const newMoment = moment(new Date()).startOf('day'); let firstDripTimeSet: boolean = false; @@ -399,7 +402,6 @@ export class BrewBrewingPreparationDeviceComponent implements OnInit { this.brewComponent.brewBrewingGraphEl.flow_profile_raw = newBrewFlow; this.brewComponent.brewBrewingGraphEl.initializeFlowChart(true); - } public getPreparationDeviceType() { diff --git a/src/components/graph-display-card/graph-display-card.component.ts b/src/components/graph-display-card/graph-display-card.component.ts index 6651cc52..1eaab91d 100644 --- a/src/components/graph-display-card/graph-display-card.component.ts +++ b/src/components/graph-display-card/graph-display-card.component.ts @@ -63,7 +63,9 @@ export class GraphDisplayCardComponent implements OnInit { } else if (this.flowProfileData) { this.flow_profile_raw = this.uiHelper.cloneData(this.flowProfileData); } else if (this.meticulousHistoryData) { - this.flow_profile_raw = MeticulousDevice.returnBrewFlowForShotData(this.meticulousHistoryData.data); + this.flow_profile_raw = MeticulousDevice.returnBrewFlowForShotData( + this.meticulousHistoryData.data + ); } setTimeout(() => { this.initializeFlowChart(); diff --git a/src/components/photo-add/photo-add.component.ts b/src/components/photo-add/photo-add.component.ts index dcaebba0..ff93088a 100644 --- a/src/components/photo-add/photo-add.component.ts +++ b/src/components/photo-add/photo-add.component.ts @@ -2,10 +2,11 @@ import { Component, ElementRef, EventEmitter, - Input, OnDestroy, + Input, + OnDestroy, OnInit, Output, - ViewChild + ViewChild, } from '@angular/core'; import { UIImage } from '../../services/uiImage'; import { Brew } from '../../classes/brew/brew'; @@ -24,7 +25,7 @@ import { TranslateService } from '@ngx-translate/core'; templateUrl: './photo-add.component.html', styleUrls: ['./photo-add.component.scss'], }) -export class PhotoAddComponent implements OnInit,OnDestroy { +export class PhotoAddComponent implements OnInit, OnDestroy { @Input() public data: Brew | Bean | GreenBean | Mill | Preparation; @Output() public dataChange = new EventEmitter< Brew | Bean | GreenBean | Mill | Preparation @@ -47,9 +48,7 @@ export class PhotoAddComponent implements OnInit,OnDestroy { this.updateSlider(); }, 250); } - public ngOnDestroy() { - - } + public ngOnDestroy() {} public addImage(): void { this.uiImage.showOptionChooser().then((_option) => { if (_option === 'CHOOSE') { diff --git a/src/enums/beans/beanSortAfter.ts b/src/enums/beans/beanSortAfter.ts index abc8786c..5b042736 100755 --- a/src/enums/beans/beanSortAfter.ts +++ b/src/enums/beans/beanSortAfter.ts @@ -5,6 +5,6 @@ export enum BEAN_SORT_AFTER { ROASTING_DATE = 'ROASTING DATE', CREATION_DATE = 'CREATION DATE', PURCHASE_DATE = 'PURCHASE DATE', - RATING ='RATING', - BEAN_AGE ='BEAN AGE' + RATING = 'RATING', + BEAN_AGE = 'BEAN AGE', } diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts index 63744692..073bb229 100644 --- a/src/environments/environment.prod.ts +++ b/src/environments/environment.prod.ts @@ -2,6 +2,6 @@ export const environment = { production: true, API_URL: 'https://backend.beanconqueror.com/', FEATURES_ACTIVE: { - SANREMO_YOU: false - } + SANREMO_YOU: false, + }, }; diff --git a/src/environments/environment.ts b/src/environments/environment.ts index af970cf9..0c448127 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -6,8 +6,8 @@ export const environment = { production: false, API_URL: 'https://backend.beanconqueror.com/', FEATURES_ACTIVE: { - SANREMO_YOU: false - } + SANREMO_YOU: false, + }, }; /* diff --git a/src/interfaces/preparationDevices/sanremoYOU/iSanremoYOUParams.ts b/src/interfaces/preparationDevices/sanremoYOU/iSanremoYOUParams.ts index b5ce33ea..a04a7335 100644 --- a/src/interfaces/preparationDevices/sanremoYOU/iSanremoYOUParams.ts +++ b/src/interfaces/preparationDevices/sanremoYOU/iSanremoYOUParams.ts @@ -1,5 +1,4 @@ export interface ISanremoYOUParams { stopAtWeight: number; residualLagTime: number; - } diff --git a/src/popover/data-corruption-found/data-corruption-found.component.spec.ts b/src/popover/data-corruption-found/data-corruption-found.component.spec.ts index 8ffd97dd..6962cefa 100644 --- a/src/popover/data-corruption-found/data-corruption-found.component.spec.ts +++ b/src/popover/data-corruption-found/data-corruption-found.component.spec.ts @@ -9,8 +9,8 @@ describe('DataCorruptionFoundComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - declarations: [ DataCorruptionFoundComponent ], - imports: [IonicModule.forRoot()] + declarations: [DataCorruptionFoundComponent], + imports: [IonicModule.forRoot()], }).compileComponents(); fixture = TestBed.createComponent(DataCorruptionFoundComponent); diff --git a/src/popover/data-corruption-found/data-corruption-found.component.ts b/src/popover/data-corruption-found/data-corruption-found.component.ts index 4c859d78..30fc8e56 100644 --- a/src/popover/data-corruption-found/data-corruption-found.component.ts +++ b/src/popover/data-corruption-found/data-corruption-found.component.ts @@ -1,13 +1,12 @@ import { Component, Input, OnInit } from '@angular/core'; import { ModalController, Platform } from '@ionic/angular'; - @Component({ selector: 'app-data-corruption-found', templateUrl: './data-corruption-found.component.html', styleUrls: ['./data-corruption-found.component.scss'], }) -export class DataCorruptionFoundComponent implements OnInit { +export class DataCorruptionFoundComponent implements OnInit { public static POPOVER_ID: string = 'data-corruption-found-popover'; @Input() public actualUIStorageDataObj: any = undefined; @@ -18,20 +17,16 @@ export class DataCorruptionFoundComponent implements OnInit { constructor( private readonly modalController: ModalController, private readonly platform: Platform - ) { - - } + ) {} public ngOnInit() { try { - this.disableHardwareBack = this.platform.backButton.subscribeWithPriority( 9999, (processNextHandler) => { // Don't do anything. } ); - } catch (ex) {} } @@ -49,7 +44,6 @@ export class DataCorruptionFoundComponent implements OnInit { ); } - public async import() { try { this.disableHardwareBack.unsubscribe(); @@ -64,5 +58,4 @@ export class DataCorruptionFoundComponent implements OnInit { DataCorruptionFoundComponent.POPOVER_ID ); } - } diff --git a/src/services/coffeeBluetoothDevices/coffee-bluetooth-devices.service.ts b/src/services/coffeeBluetoothDevices/coffee-bluetooth-devices.service.ts index be3849ef..a23dd3d4 100644 --- a/src/services/coffeeBluetoothDevices/coffee-bluetooth-devices.service.ts +++ b/src/services/coffeeBluetoothDevices/coffee-bluetooth-devices.service.ts @@ -1048,18 +1048,16 @@ export class CoffeeBluetoothDevicesService { id: deviceTemperature.id, type: TemperatureType.COMBUSTION, }); - } - else if (ArgosThermometer.test(deviceTemperature)) { + } else if (ArgosThermometer.test(deviceTemperature)) { this.logger.log( 'BleManager - We found a Argos Thermometer device ' + - JSON.stringify(deviceTemperature) + JSON.stringify(deviceTemperature) ); supportedDevices.push({ id: deviceTemperature.id, type: TemperatureType.ARGOS, }); } - } resolve(supportedDevices); }); @@ -1104,8 +1102,7 @@ export class CoffeeBluetoothDevicesService { type: TemperatureType.COMBUSTION, }); return; - } - else if (ArgosThermometer.test(deviceTemperature)) { + } else if (ArgosThermometer.test(deviceTemperature)) { this.logger.log( 'BleManager - We found a Argos Thermometer device ' ); diff --git a/src/services/uiExcel.ts b/src/services/uiExcel.ts index c9cd4e8a..d8f3f7f3 100755 --- a/src/services/uiExcel.ts +++ b/src/services/uiExcel.ts @@ -721,14 +721,12 @@ export class UIExcel { } } - public async importGreenBeansByExcel(_arrayBuffer) { try { /* data is an ArrayBuffer */ const wb = XLSX.read(_arrayBuffer); const data = XLSX.utils.sheet_to_json(wb.Sheets['Green Beans']); - let varietyInformationWhereAdded: boolean = false; const addedBeans: Array = []; const toAddBeans: Array = []; @@ -784,7 +782,6 @@ export class UIExcel { bean.url = websiteEntry.toString(); } - const flavourProfileEntry = entry['Flavour profile']; if (flavourProfileEntry) { bean.aromatics = flavourProfileEntry.toString(); @@ -820,74 +817,83 @@ export class UIExcel { const informationCertificationEntry = entry['1. Bean certification']; if (informationCertificationEntry) { - beanInformation.certification = informationCertificationEntry.toString(); - hasOneBeanInformation=true; + beanInformation.certification = + informationCertificationEntry.toString(); + hasOneBeanInformation = true; } const informationCountryEntry = entry['1. Country']; if (informationCountryEntry) { beanInformation.country = informationCountryEntry.toString(); - hasOneBeanInformation=true; + hasOneBeanInformation = true; } const informationElevationEntry = entry['1. Elevation']; if (informationElevationEntry) { beanInformation.elevation = informationElevationEntry.toString(); - hasOneBeanInformation=true; + hasOneBeanInformation = true; } const informationFarmEntry = entry['1. Farm']; if (informationFarmEntry) { beanInformation.farm = informationFarmEntry.toString(); - hasOneBeanInformation=true; + hasOneBeanInformation = true; } const informationFarmerEntry = entry['1. Farmer']; if (informationFarmerEntry) { beanInformation.farmer = informationFarmerEntry.toString(); - hasOneBeanInformation=true; + hasOneBeanInformation = true; } const informationHarvestedEntry = entry['1. Harvested']; if (informationHarvestedEntry) { beanInformation.harvest_time = informationHarvestedEntry.toString(); - hasOneBeanInformation=true; + hasOneBeanInformation = true; } const informationPercentageEntry = entry['1. Percentage']; - if (informationPercentageEntry && Number(informationPercentageEntry) > 0) { + if ( + informationPercentageEntry && + Number(informationPercentageEntry) > 0 + ) { beanInformation.percentage = Number(informationPercentageEntry); - hasOneBeanInformation=true; + hasOneBeanInformation = true; } const informationProcessingEntry = entry['1. Processing']; if (informationProcessingEntry) { beanInformation.processing = informationProcessingEntry.toString(); - hasOneBeanInformation=true; + hasOneBeanInformation = true; } const informationRegionEntry = entry['1. Region']; if (informationRegionEntry) { beanInformation.region = informationRegionEntry.toString(); - hasOneBeanInformation=true; + hasOneBeanInformation = true; } const informationVarietyEntry = entry['1. Variety']; if (informationVarietyEntry) { beanInformation.variety = informationVarietyEntry.toString(); - hasOneBeanInformation=true; + hasOneBeanInformation = true; } const informationFobPriceEntry = entry['1. Fob Price']; if (informationFobPriceEntry && Number(informationFobPriceEntry) > 0) { beanInformation.fob_price = Number(informationFobPriceEntry); - hasOneBeanInformation=true; + hasOneBeanInformation = true; } const informationPurchasingPriceEntry = entry['1. Purchasing Price']; - if (informationPurchasingPriceEntry && Number(informationPurchasingPriceEntry) > 0) { - beanInformation.purchasing_price = Number(informationPurchasingPriceEntry); - hasOneBeanInformation=true; + if ( + informationPurchasingPriceEntry && + Number(informationPurchasingPriceEntry) > 0 + ) { + beanInformation.purchasing_price = Number( + informationPurchasingPriceEntry + ); + hasOneBeanInformation = true; } if (hasOneBeanInformation) { @@ -902,7 +908,6 @@ export class UIExcel { toAddBeans.push(bean); } - if (varietyInformationWhereAdded) { if (this.settings.bean_manage_parameters.bean_information === false) { this.settings.bean_manage_parameters.bean_information = true; @@ -910,7 +915,6 @@ export class UIExcel { } } - /** * Add all beans afterwards to not have some added and then going into an exception */ @@ -918,24 +922,33 @@ export class UIExcel { try { const newBean: GreenBean = await this.uiGreenBeanStorage.add(addBean); addedBeans.push(newBean); - } catch(ex) { - - } + } catch (ex) {} } - if (addedBeans.length>0) { + if (addedBeans.length > 0) { try { - await this.uiAlert.showMessage('GREEN_BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION','IMPORT_SUCCESSFULLY',undefined,true); - }catch(ex) { - - } - + await this.uiAlert.showMessage( + 'GREEN_BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION', + 'IMPORT_SUCCESSFULLY', + undefined, + true + ); + } catch (ex) {} } else { - this.uiAlert.showMessage('BEANS_IMPORTED_UNSUCCESSFULLY_WRONG_EXCELFILE','IMPORT_UNSUCCESSFULLY','OK',false); + this.uiAlert.showMessage( + 'BEANS_IMPORTED_UNSUCCESSFULLY_WRONG_EXCELFILE', + 'IMPORT_UNSUCCESSFULLY', + 'OK', + false + ); } - } catch (ex) { - this.uiAlert.showMessage(ex.message,this.translate.instant('IMPORT_UNSUCCESSFULLY'),this.translate.instant('OK'),false); + this.uiAlert.showMessage( + ex.message, + this.translate.instant('IMPORT_UNSUCCESSFULLY'), + this.translate.instant('OK'), + false + ); } } @@ -1063,74 +1076,83 @@ export class UIExcel { const informationCertificationEntry = entry['1. Bean certification']; if (informationCertificationEntry) { - beanInformation.certification = informationCertificationEntry.toString(); - hasOneBeanInformation=true; + beanInformation.certification = + informationCertificationEntry.toString(); + hasOneBeanInformation = true; } const informationCountryEntry = entry['1. Country']; if (informationCountryEntry) { beanInformation.country = informationCountryEntry.toString(); - hasOneBeanInformation=true; + hasOneBeanInformation = true; } const informationElevationEntry = entry['1. Elevation']; if (informationElevationEntry) { beanInformation.elevation = informationElevationEntry.toString(); - hasOneBeanInformation=true; + hasOneBeanInformation = true; } const informationFarmEntry = entry['1. Farm']; if (informationFarmEntry) { beanInformation.farm = informationFarmEntry.toString(); - hasOneBeanInformation=true; + hasOneBeanInformation = true; } const informationFarmerEntry = entry['1. Farmer']; if (informationFarmerEntry) { beanInformation.farmer = informationFarmerEntry.toString(); - hasOneBeanInformation=true; + hasOneBeanInformation = true; } const informationHarvestedEntry = entry['1. Harvested']; if (informationHarvestedEntry) { beanInformation.harvest_time = informationHarvestedEntry.toString(); - hasOneBeanInformation=true; + hasOneBeanInformation = true; } const informationPercentageEntry = entry['1. Percentage']; - if (informationPercentageEntry && Number(informationPercentageEntry) > 0) { + if ( + informationPercentageEntry && + Number(informationPercentageEntry) > 0 + ) { beanInformation.percentage = Number(informationPercentageEntry); - hasOneBeanInformation=true; + hasOneBeanInformation = true; } const informationProcessingEntry = entry['1. Processing']; if (informationProcessingEntry) { beanInformation.processing = informationProcessingEntry.toString(); - hasOneBeanInformation=true; + hasOneBeanInformation = true; } const informationRegionEntry = entry['1. Region']; if (informationRegionEntry) { beanInformation.region = informationRegionEntry.toString(); - hasOneBeanInformation=true; + hasOneBeanInformation = true; } const informationVarietyEntry = entry['1. Variety']; if (informationVarietyEntry) { beanInformation.variety = informationVarietyEntry.toString(); - hasOneBeanInformation=true; + hasOneBeanInformation = true; } const informationFobPriceEntry = entry['1. Fob Price']; if (informationFobPriceEntry && Number(informationFobPriceEntry) > 0) { beanInformation.fob_price = Number(informationFobPriceEntry); - hasOneBeanInformation=true; + hasOneBeanInformation = true; } const informationPurchasingPriceEntry = entry['1. Purchasing Price']; - if (informationPurchasingPriceEntry && Number(informationPurchasingPriceEntry) > 0) { - beanInformation.purchasing_price = Number(informationPurchasingPriceEntry); - hasOneBeanInformation=true; + if ( + informationPurchasingPriceEntry && + Number(informationPurchasingPriceEntry) > 0 + ) { + beanInformation.purchasing_price = Number( + informationPurchasingPriceEntry + ); + hasOneBeanInformation = true; } if (hasOneBeanInformation) { @@ -1162,7 +1184,9 @@ export class UIExcel { const unfrozenDateEntry = entry['Unfrozen Date']; if (unfrozenDateEntry && Number(unfrozenDateEntry) > 0) { isOneEntryFrozen = true; - bean.unfrozenDate = this.getJsDateFromExcel(Number(unfrozenDateEntry)); + bean.unfrozenDate = this.getJsDateFromExcel( + Number(unfrozenDateEntry) + ); } toAddBeans.push(bean); } @@ -1181,7 +1205,6 @@ export class UIExcel { } } - /** * Add all beans afterwards to not have some added and then going into an exception */ @@ -1189,30 +1212,39 @@ export class UIExcel { try { const newBean: Bean = await this.uiBeanStorage.add(addBean); addedBeans.push(newBean); - } catch(ex) { - - } + } catch (ex) {} } - if (addedBeans.length>0) { + if (addedBeans.length > 0) { try { - await this.uiAlert.showMessage('BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION','IMPORT_SUCCESSFULLY',undefined,true); - }catch(ex) { - - } + await this.uiAlert.showMessage( + 'BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION', + 'IMPORT_SUCCESSFULLY', + undefined, + true + ); + } catch (ex) {} this.uiBeanHelper.showBeans(addedBeans); } else { - this.uiAlert.showMessage('BEANS_IMPORTED_UNSUCCESSFULLY_WRONG_EXCELFILE','IMPORT_UNSUCCESSFULLY','OK',false); + this.uiAlert.showMessage( + 'BEANS_IMPORTED_UNSUCCESSFULLY_WRONG_EXCELFILE', + 'IMPORT_UNSUCCESSFULLY', + 'OK', + false + ); } - } catch (ex) { - this.uiAlert.showMessage(ex.message,this.translate.instant('IMPORT_UNSUCCESSFULLY'),this.translate.instant('OK'),false); + this.uiAlert.showMessage( + ex.message, + this.translate.instant('IMPORT_UNSUCCESSFULLY'), + this.translate.instant('OK'), + false + ); } } private getJsDateFromExcel(_dateNumber) { - return new Date((_dateNumber - (25567+2))*86400*1000).toISOString(); - + return new Date((_dateNumber - (25567 + 2)) * 86400 * 1000).toISOString(); } /* Export button */ public async export() { diff --git a/src/services/uiExportImportHelper.ts b/src/services/uiExportImportHelper.ts index 2b6cfa9f..19c1d846 100644 --- a/src/services/uiExportImportHelper.ts +++ b/src/services/uiExportImportHelper.ts @@ -243,46 +243,58 @@ export class UIExportImportHelper { }); } private async checkBackupAndSeeIfDataAreCorrupted(_actualUIStorageDataObj) { - try { - this.uiLog.log("checkBackupAndSeeIfDataAreCorrupted - Check if we got a deep corruption"); + this.uiLog.log( + 'checkBackupAndSeeIfDataAreCorrupted - Check if we got a deep corruption' + ); const dataObj = _actualUIStorageDataObj.DATA; const parsedJSON: any = await this.readBackupZIPFile(); if (parsedJSON) { let somethingCorrupted = false; if (parsedJSON.BEANS?.length > dataObj.BEANS) { somethingCorrupted = true; - } else if (parsedJSON.BREWS?.length > dataObj.BREWS) - { + } else if (parsedJSON.BREWS?.length > dataObj.BREWS) { somethingCorrupted = true; - } else if (parsedJSON.PREPARATION?.length > dataObj.PREPARATION) - { + } else if (parsedJSON.PREPARATION?.length > dataObj.PREPARATION) { somethingCorrupted = true; - } - else if (parsedJSON.MILL?.length > dataObj.MILL) - { + } else if (parsedJSON.MILL?.length > dataObj.MILL) { somethingCorrupted = true; } - this.uiLog.log("checkBackupAndSeeIfDataAreCorrupted- Check over - if we got a deep corruption - Result: " + somethingCorrupted); + this.uiLog.log( + 'checkBackupAndSeeIfDataAreCorrupted- Check over - if we got a deep corruption - Result: ' + + somethingCorrupted + ); if (somethingCorrupted) { - const importBackup = await this.showDataCorruptionPopover(dataObj,parsedJSON); + const importBackup = await this.showDataCorruptionPopover( + dataObj, + parsedJSON + ); if (importBackup) { await this.importBackupJSON(parsedJSON); } } else { - this.uiLog.log("checkBackupAndSeeIfDataAreCorrupted - Check over - we didn't find any corrupted data"); + this.uiLog.log( + "checkBackupAndSeeIfDataAreCorrupted - Check over - we didn't find any corrupted data" + ); } } else { - this.uiLog.log("checkBackupAndSeeIfDataAreCorrupted - We didn't found any json backup data so we can't do any checks"); + this.uiLog.log( + "checkBackupAndSeeIfDataAreCorrupted - We didn't found any json backup data so we can't do any checks" + ); } - } catch(ex) { - this.uiLog.log("Check over - if we got a deep corruption - Result exception: " + JSON.stringify(ex)); + } catch (ex) { + this.uiLog.log( + 'Check over - if we got a deep corruption - Result exception: ' + + JSON.stringify(ex) + ); } - } - public async showDataCorruptionPopover(_actualUIStorageDataObj,_backupDataObj) { + public async showDataCorruptionPopover( + _actualUIStorageDataObj, + _backupDataObj + ) { const modal = await this.modalController.create({ component: DataCorruptionFoundComponent, id: DataCorruptionFoundComponent.POPOVER_ID, @@ -292,8 +304,10 @@ export class UIExportImportHelper { }, }); await modal.present(); - const returnData = await modal.onWillDismiss(); - this.uiLog.log('Data corruption, choose to import: ' + returnData?.data?.import); + const returnData = await modal.onWillDismiss(); + this.uiLog.log( + 'Data corruption, choose to import: ' + returnData?.data?.import + ); if (returnData?.data?.import) { //User choose to import backup, go return true; @@ -332,7 +346,9 @@ export class UIExportImportHelper { } resolve(null); } else { - await this.checkBackupAndSeeIfDataAreCorrupted(actualUIStorageDataObj); + await this.checkBackupAndSeeIfDataAreCorrupted( + actualUIStorageDataObj + ); resolve(null); } } else { diff --git a/src/services/uiHelper.ts b/src/services/uiHelper.ts index 4805deb9..8026bcb1 100755 --- a/src/services/uiHelper.ts +++ b/src/services/uiHelper.ts @@ -147,7 +147,7 @@ export class UIHelper { return moment(_unix).format(format); } - public formatTimeNumber(_time: number|string, _format?: string): string { + public formatTimeNumber(_time: number | string, _format?: string): string { let format: string = this.getSettingsStorageInstance().getSettings().date_format + ', HH:mm:ss'; diff --git a/src/services/uiPreparationHelper.ts b/src/services/uiPreparationHelper.ts index bc28ca0b..3fd8ca61 100644 --- a/src/services/uiPreparationHelper.ts +++ b/src/services/uiPreparationHelper.ts @@ -23,9 +23,7 @@ import { HttpClient } from '@angular/common/http'; import { PreparationDevice } from '../classes/preparationDevice/preparationDevice'; import { PreparationSortToolsComponent } from '../app/preparation/preparation-sort-tools/preparation-sort-tools.component'; import PREPARATION_TRACKING from '../data/tracking/preparationTracking'; -import { - PreparationConnectedDeviceComponent -} from '../app/preparation/preparation-connected-device/preparation-connected-device.component'; +import { PreparationConnectedDeviceComponent } from '../app/preparation/preparation-connected-device/preparation-connected-device.component'; import { UIAnalytics } from './uiAnalytics'; /** @@ -103,9 +101,6 @@ export class UIPreparationHelper { await modal.onWillDismiss(); } - - - public async editPreparationTool( _preparation: Preparation, _preparationTool: PreparationTool From 3e87507a3877f0eaa41dcb04e642161940572d80 Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Tue, 27 Aug 2024 21:28:00 +0200 Subject: [PATCH 21/55] Changing to .toPng as screenshot after images where not rendered anymore --- .../brew-information/brew-information.component.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/brew-information/brew-information.component.ts b/src/components/brew-information/brew-information.component.ts index ea234ce8..a1455d89 100644 --- a/src/components/brew-information/brew-information.component.ts +++ b/src/components/brew-information/brew-information.component.ts @@ -399,12 +399,13 @@ export class BrewInformationComponent implements OnInit { await this.uiAlert.showLoadingSpinner(); if (this.platform.is('ios')) { htmlToImage - .toJpeg(this.cardEl.nativeElement) + .toPng(this.cardEl.nativeElement) .then((_dataURL) => { + console.log(_dataURL); // On iOS we need to do this a second time, because the rendering doesn't render everything (strange thing) setTimeout(() => { htmlToImage - .toJpeg(this.cardEl.nativeElement) + .toPng(this.cardEl.nativeElement) .then(async (_dataURLSecond) => { await this.uiAlert.hideLoadingSpinner(); setTimeout(() => { @@ -421,7 +422,7 @@ export class BrewInformationComponent implements OnInit { }); } else { htmlToImage - .toJpeg(this.cardEl.nativeElement) + .toPng(this.cardEl.nativeElement) .then(async (_dataURL) => { await this.uiAlert.hideLoadingSpinner(); setTimeout(() => { From 3d66ac2098df5d52b308d59163e573e7eeb1cdd4 Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Tue, 27 Aug 2024 22:27:36 +0200 Subject: [PATCH 22/55] #779 - Fixing the range for pressure graph --- .../graph-detail/graph-detail.component.ts | 1 - src/app/settings/settings.page.html | 19 +++++++++++++++++++ src/app/settings/settings.page.ts | 9 +++++++++ src/assets/i18n/en.json | 5 ++++- src/classes/settings/settings.ts | 10 ++++++++++ .../brew-brewing-graph.component.ts | 6 +++++- .../graph-display-card.component.ts | 1 - src/interfaces/settings/iSettings.ts | 4 ++++ 8 files changed, 51 insertions(+), 4 deletions(-) diff --git a/src/app/graph-section/graph/graph-detail/graph-detail.component.ts b/src/app/graph-section/graph/graph-detail/graph-detail.component.ts index 4bc1b9dd..a0ead927 100644 --- a/src/app/graph-section/graph/graph-detail/graph-detail.component.ts +++ b/src/app/graph-section/graph/graph-detail/graph-detail.component.ts @@ -183,7 +183,6 @@ export class GraphDetailComponent implements OnInit { side: 'right', showgrid: false, position: 0.93, - range: [0, 10], visible: true, }; diff --git a/src/app/settings/settings.page.html b/src/app/settings/settings.page.html index 9ead758f..fc663f98 100644 --- a/src/app/settings/settings.page.html +++ b/src/app/settings/settings.page.html @@ -986,6 +986,25 @@

{{"PRESSURE_LOG" | translate}}

+ + + + {{"PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS" | translate }} +

{{"PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_DESCRIPTION" | translate}}

+
+ + + {{"PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_RANGE" | translate}} {{this.uiHelper.toFixedIfNecessary(settings.graph_pressure.lower,2)}} - {{this.uiHelper.toFixedIfNecessary(settings.graph_pressure.upper,2)}} + + + + + + + +
+
diff --git a/src/app/settings/settings.page.ts b/src/app/settings/settings.page.ts index 5495aa60..fcae3d57 100644 --- a/src/app/settings/settings.page.ts +++ b/src/app/settings/settings.page.ts @@ -1210,6 +1210,15 @@ export class SettingsPage { this.uiExcel.export(); } + public pinFormatter(value: any) { + const parsedFloat = parseFloat(value); + if (isNaN(parsedFloat)) { + return `${0}`; + } + const newValue = +parsedFloat.toFixed(2); + return `${newValue}`; + } + public doWeHaveBrewByWeights(): boolean { const allPreparations = this.uiPreparationStorage.getAllEntries(); for (const prep of allPreparations) { diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 5990d359..8e12661a 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -1370,5 +1370,8 @@ "GREEN_BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "All found entries in the excel list, have been transformed into new green beans, have great roasts!", "PREPARATION_TYPE_SANREMO_YOU": "Sanremo YOU", "PREPARATION_TYPE_XENIA": "Xenia", - "BEAN_POPUP_YOU_DONT_SEE_EVERYTHING_DESCRIPTION": "You're adding a bean which has variety information in it, but those parameters are not activated. Do you want to activate them now?" + "BEAN_POPUP_YOU_DONT_SEE_EVERYTHING_DESCRIPTION": "You're adding a bean which has variety information in it, but those parameters are not activated. Do you want to activate them now?", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS": "Define the axes for the pressure graph", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_DESCRIPTION": "Set the starting and end size of the axes for pressure", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_RANGE": "Pressure axes" } diff --git a/src/classes/settings/settings.ts b/src/classes/settings/settings.ts index a247d49f..3a4839c1 100755 --- a/src/classes/settings/settings.ts +++ b/src/classes/settings/settings.ts @@ -140,6 +140,11 @@ export class Settings implements ISettings { }; }; + public graph_pressure: { + upper: number; + lower: number; + }; + public wake_lock: boolean; public security_check_when_going_back: boolean; @@ -381,6 +386,11 @@ export class Settings implements ISettings { }, }; + this.graph_pressure = { + upper: 0, + lower: 9, + }; + this.brew_rating = 5; this.brew_rating_steps = 1; this.bean_rating = 5; diff --git a/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts b/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts index 3f54feab..c0aa6771 100644 --- a/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts +++ b/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts @@ -928,6 +928,7 @@ export class BrewBrewingGraphComponent implements OnInit { if (this.isDetail === false) { let graph_weight_settings; let graph_flow_settings; + if ( this.data.getPreparation().style_type === PREPARATION_STYLE_TYPE.ESPRESSO @@ -1036,6 +1037,9 @@ export class BrewBrewingGraphComponent implements OnInit { this.brewComponent?.brewBrewingPreparationDeviceEl?.preparationDeviceConnected() || !this.platform.is('cordova') ) { + const graph_pressure_settings = this.settings.graph_pressure; + const suggestedMinPressure: number = graph_pressure_settings.lower; + const suggestedMaxPressure: number = graph_pressure_settings.upper; layout['yaxis4'] = { title: '', titlefont: { color: '#05C793' }, @@ -1046,7 +1050,7 @@ export class BrewBrewingGraphComponent implements OnInit { showgrid: false, position: 0.91, fixedrange: true, - range: [0, 10], + range: [suggestedMinPressure, suggestedMaxPressure], }; } const temperatureDevice = this.bleManager.getTemperatureDevice(); diff --git a/src/components/graph-display-card/graph-display-card.component.ts b/src/components/graph-display-card/graph-display-card.component.ts index 1eaab91d..9896b7fb 100644 --- a/src/components/graph-display-card/graph-display-card.component.ts +++ b/src/components/graph-display-card/graph-display-card.component.ts @@ -169,7 +169,6 @@ export class GraphDisplayCardComponent implements OnInit { fixedrange: true, showgrid: false, position: 0.93, - range: [0, 10], visible: true, }; diff --git a/src/interfaces/settings/iSettings.ts b/src/interfaces/settings/iSettings.ts index 2aa0afac..35d8d7c1 100755 --- a/src/interfaces/settings/iSettings.ts +++ b/src/interfaces/settings/iSettings.ts @@ -135,6 +135,10 @@ export interface ISettings { lower: number; }; }; + graph_pressure: { + upper: number; + lower: number; + }; welcome_page_showed: boolean; From 7cc183650c9084b86854d71ca20cbed14cf79bbb Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Wed, 28 Aug 2024 21:50:39 +0200 Subject: [PATCH 23/55] Lokalise update --- src/assets/i18n/de.json | 33 - src/assets/i18n/en.json | 33 - src/assets/i18n/es.json | 33 - src/assets/i18n/fr.json | 37 +- src/assets/i18n/id.json | 33 - src/assets/i18n/it.json | 45 +- src/assets/i18n/nl_NL.json | 1323 ++++++++++++++++++++++++++++++++++++ src/assets/i18n/pl.json | 33 - src/assets/i18n/tr.json | 33 - src/assets/i18n/zh.json | 33 - 10 files changed, 1331 insertions(+), 305 deletions(-) create mode 100644 src/assets/i18n/nl_NL.json diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json index 90777414..d63be163 100644 --- a/src/assets/i18n/de.json +++ b/src/assets/i18n/de.json @@ -1273,39 +1273,6 @@ "SHOW_MINUTES": "Minuten anzeigen", "BEANS_UNARCHIVE": "Archivierung aufheben", "TOAST_BEAN_UNARCHIVED_SUCCESSFULLY": "Bean wurde dearchiviert", - "UPDATE_TEXT_TITLE_TITLE": { - "7.4.0": { - "TITLE": "Version 7.4.0: Das ist neu", - "DESCRIPTION": [ - "Bohnen<\/b>", - "Es ist jetzt möglich Kaffeebohnen einzufrieren. Aktiviere diese Funktion in den Einstellungen", - "Bestes und offenes Datum für Bohnen hinzugefügt. Diese Parameter müssen aktiviert werden.", - "Bohnen können nun direkt aus der Auswahlübersicht hinzugefügt werden", - "", - " Thermometer <\/b>", - "Unterstützung des Combustion Inc. Thermometers – Danke für das Gerät!", - "Unterstützung des Meater-Thermometers (nicht Meater 2 oder Meater +) – Danke an Yannick!", - "", - "Wasserbereich<\/b>", - "Pure Coffee Water hinzugefügt", - "Empirical Water hinzugefügt", - "", - "Brühmethoden Tools<\/b>", - "Sortiere deine Zubereitungsmethoden", - "", - "Mühlen<\/b>", - "Mühlenbilder werden in der Übersicht angezeigt", - "", - "Einstellungen<\/b>", - "Sicherheitsmeldung, wenn du die Bewertung für Brühen oder Bohnen geändert hast und das Maximum niedriger ist als ein bereits bewerteter Eintrag", - "", - "Sonstiges<\/b>", - "Der direkte Sekundenfokus beim Öffnen des Timers wurde ausgebaut", - "Einige technische Änderungen im Code", - "Kleine Optimierungen" - ] - } - }, "WATER_TYPE_PURE_COFFEE_WATER": "Pure Coffee Water", "WATER_TYPE_EMPIRICAL_WATER_GLACIAL": "empirical water GLACIAL", "WATER_TYPE_EMPIRICAL_WATER_SPRING": "empirical water SPRING", diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index b73e2994..6408bd8f 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -1273,39 +1273,6 @@ "SHOW_MINUTES": "Show minutes", "BEANS_UNARCHIVE": "Unarchive", "TOAST_BEAN_UNARCHIVED_SUCCESSFULLY": "Bean has been unarchived", - "UPDATE_TEXT_TITLE_TITLE": { - "7.4.0": { - "TITLE": "Version 7.4.0: What's new", - "DESCRIPTION": [ - "Beans<\/b>", - "Its now possible to freeze your coffee beans, activate this feature in the settings", - "Added best and open date for beans, you need to activate this parameter", - "Beans can now be directly added from the select overview", - "", - "Thermometer<\/b>", - "Support of the Combustion Inc. Thermometer - Thanks for the Device!", - "Support of the Meater Thermometer (not Meater 2 or Meater +) - Thanks to Yannick!", - "", - "Water Section<\/b>", - "Added Pure Coffee Water", - "Added Empirical Water", - "", - "Preparation Tools<\/b>", - "Sort now your preparation tools", - "", - "Grinder<\/b>", - "Grinder images are now shown in the select overview", - "", - "Settings<\/b>", - "Security message if you changed the rating for brews or beans, and the maximum is lower then an already rated entry", - "", - "Other<\/b>", - "Reverted the direct seconds focus when opening the timer", - "Some technical changes in the code", - "Small tweaks" - ] - } - }, "WATER_TYPE_PURE_COFFEE_WATER": "Pure Coffee Water", "WATER_TYPE_EMPIRICAL_WATER_GLACIAL": "empirical water GLACIAL", "WATER_TYPE_EMPIRICAL_WATER_SPRING": "empirical water SPRING", diff --git a/src/assets/i18n/es.json b/src/assets/i18n/es.json index 9736cc49..38d1a467 100644 --- a/src/assets/i18n/es.json +++ b/src/assets/i18n/es.json @@ -1273,39 +1273,6 @@ "SHOW_MINUTES": "Mostrar minutos", "BEANS_UNARCHIVE": "Desarchivar", "TOAST_BEAN_UNARCHIVED_SUCCESSFULLY": "Café desarchivado", - "UPDATE_TEXT_TITLE_TITLE": { - "7.4.0": { - "TITLE": "Versión 7.4.0: Novedades", - "DESCRIPTION": [ - "Cafés<\/b>", - "Ahora es posible congelar tus cafés, puedes activar esta opción en ajustes.", - "Añadidas fechas de apertura y de consumo óptimo para tus cafés (desactivadas por defecto).", - "Ahora puedes añadir un nuevo café desde el menú de selección de cafés de una preparación.", - "", - "Termómetro<\/b>", - "Añadido soporte para el termómetro Combustion Inc. - Gracias por el aporte!", - "Añadido soporte para el termómetro Meater (no Meater 2 o Meater+) - Gracias a Yannick!", - "", - "Sección de agua<\/b>", - "Añadida agua Pure Coffee Water", - "Añadida agua Empirical Water", - "", - "Métodos de preparación<\/b>", - "Ahora puedes ordenar tus métodos de preparación", - "", - "Molinos<\/b>", - "Ahora se muestran las imágenes de los molinos en el menú de selección.", - "", - "Ajustes<\/b>", - "Se muestra una alerta si cambias la puntuación de una preparación o un café, y el máximo es menor que otra entrada ya puntuada.", - "", - "Otros<\/b>", - "El campo \"segundos\" ya no se selecciona automáticamente al abrir el selector de tiempo.", - "Cambios técnicos en el código.", - "Pequeños ajustes y mejoras." - ] - } - }, "WATER_TYPE_PURE_COFFEE_WATER": "Pure Coffee Water", "WATER_TYPE_EMPIRICAL_WATER_GLACIAL": "empirical water GLACIAL", "WATER_TYPE_EMPIRICAL_WATER_SPRING": "empirical water SPRING", diff --git a/src/assets/i18n/fr.json b/src/assets/i18n/fr.json index 8f71307e..17a5ccbe 100644 --- a/src/assets/i18n/fr.json +++ b/src/assets/i18n/fr.json @@ -479,7 +479,7 @@ "CUSTOM_MANAGE_PARAMETERS": "Gérer", "CUSTOM_SORT_PARAMETERS": "Trier", "BREW_PARAMETER_CUSTOMIZE_TITLE": "Paramètres personnalisés pour chaque méthode de préparation", - "BREW_PARAMETER_CUSTOMIZE_DESCRIPTION": "Tu veux choisir des paramètres personnalisés pour chaque méthode de préparation ? Navigues jusqu'à \"Méthodes\", ouvre le menu de la méthode de préparation spécifique et choisissez \"Personnaliser les paramètres\". Là, tu peux choisir quels paramètres seront utilisés pour cette préparation !", + "BREW_PARAMETER_CUSTOMIZE_DESCRIPTION": "Tu veux choisir des paramètres personnalisés pour chaque méthode de préparation ? Navigue jusqu'à \"Méthodes\", ouvre le menu de la méthode de préparation spécifique et choisissez \"Personnaliser les paramètres\". Là, tu peux choisir quels paramètres seront utilisés pour cette préparation !", "BREW_DATA_BREW_QUANTITY_TOOLTIP": "Quantité d'eau (non utilisable pour l'espresso)", "BREW_DATA_COFFEE_FIRST_DRIP_TIME_TOOLTIP": "Premier temps d'égouttage du café (espresso uniquement)", "BREW_DATA_PREPARATION_METHOD_TOOLTIP": "Préparation (uniquement personnalisable lorsqu'elle est active)", @@ -1148,7 +1148,7 @@ }, "SHOT": { "UPLOAD_SUCCESSFULLY": "Infusion téléchargé sur Visualizer.", - "UPLOAD_UNSUCCESSFULLY": "L'infusion n'a pas pu être téléchargé sur Visualizer." + "UPLOAD_UNSUCCESSFULLY": "L'infusion n'a pas pu être téléchargée sur Visualizer." }, "URL": "URL du serveur", "USERNAME": "Nom d'utilisateur", @@ -1273,39 +1273,6 @@ "SHOW_MINUTES": "Afficher les minutes", "BEANS_UNARCHIVE": "Désarchiver", "TOAST_BEAN_UNARCHIVED_SUCCESSFULLY": "Le grain a été désarchivé", - "UPDATE_TEXT_TITLE_TITLE": { - "7.4.0": { - "TITLE": "Version 7.4.0: c'est nouveau", - "DESCRIPTION": [ - "Grains<\/b>", - "Il est possible de congeler des grains de café. Active cette fonction dans les réglages", - "Ajout de la meilleure date et de la date d'ouverture pour les grains. Ces paramètres doivent être activés.", - "Les grains peuvent désormais être ajoutés directement à partir de l’aperçu de la sélection", - "", - " Thermomètre <\/b>", - "Support pour les thermomètres de Combustion Inc. – Merci pour l'appareil !", - "Support du thermomètre Meater (pas Meater 2 ou Meater +) – Merci à Yannick !", - "", - " Zone d'eau <\/b>", - "Ajouté Pure Coffee Water", - "Ajouté Empirical Water", - "", - " Outils de méthode d'infusion <\/b>", - "Trier tes méthodes de préparation", - "", - "Moulins<\/b>", - "Les images des moulins sont affichées dans l'aperçu", - "", - "Paramètres<\/b>", - "Message de sécurité si tu as modifié l'évaluation des infusions ou des grains et que le maximum est inférieur à une entrée déjà évaluée.", - "", - "Autres<\/b>", - "La mise au point directe en secondes lors de l'ouverture de la minuterie a été rétablie", - "Quelques changements techniques dans le code", - "Petites optimisations" - ] - } - }, "WATER_TYPE_PURE_COFFEE_WATER": "Pure Coffee Water", "WATER_TYPE_EMPIRICAL_WATER_GLACIAL": "empirical water GLACIAL", "WATER_TYPE_EMPIRICAL_WATER_SPRING": "empirical water SPRING", diff --git a/src/assets/i18n/id.json b/src/assets/i18n/id.json index fc9969cd..50594012 100644 --- a/src/assets/i18n/id.json +++ b/src/assets/i18n/id.json @@ -1273,39 +1273,6 @@ "SHOW_MINUTES": "Tampilkan menit", "BEANS_UNARCHIVE": "Tidak diarsipkan", "TOAST_BEAN_UNARCHIVED_SUCCESSFULLY": "Bean tidak diarsipkan", - "UPDATE_TEXT_TITLE_TITLE": { - "7.4.0": { - "TITLE": "Versi 7.4.0: Apa yang baru", - "DESCRIPTION": [ - "Biji<\/b>", - "Sekarang dimungkinkan untuk mencatat pembekuan biji kopi Anda, aktifkan fitur ini di pengaturan", - "Menambahkan tanggal terbaik dan tanggal membuka kopi, Anda perlu mengaktifkan parameter ini", - "Biji sekarang dapat langsung ditambahkan dari pilihan", - "", - "Thermometer<\/b>", - "Dukungan Termometer Combustion Inc. - Terima kasih untuk Perangkatnya!", - "Dukungan Termometer Meater (bukan Meater 2 atau Meater +) - Terima kasih kepada Yannick!", - "", - "Water Section<\/b>", - "Penambahan Pure Coffee Water", - "Penambahan Empirical Water", - "", - "Preparation Tools<\/b>", - "Jenis alat-alat seduhan dapat diurutkan", - "", - "Grinder<\/b>", - "Gambar grinder sekarang ditampilkan dalam pilihan", - "", - "Pengaturan<\/b>", - "Pesan keamanan jika Anda mengubah peringkat untuk seduhan atau biji kopi, dan nilai maksimumnya lebih rendah dari entri yang sudah diberi peringkat", - "", - "Lain-lain<\/b>", - "Mengembalikan fokus detik langsung saat membuka pengatur waktu", - "Beberapa perubahan teknis pada kode", - "Perubahan kecil" - ] - } - }, "WATER_TYPE_PURE_COFFEE_WATER": "Pure Coffee Water", "WATER_TYPE_EMPIRICAL_WATER_GLACIAL": "empirical water GLACIAL", "WATER_TYPE_EMPIRICAL_WATER_SPRING": "empirical water SPRING", diff --git a/src/assets/i18n/it.json b/src/assets/i18n/it.json index 0face123..522b9323 100644 --- a/src/assets/i18n/it.json +++ b/src/assets/i18n/it.json @@ -44,13 +44,13 @@ "PAGE_HOME_DIFFERENT_PREPARATION_METHODS": "metodi di preparazione", "PAGE_HOME_DIFFERENT_MILLS": "macinacaffè", "PAGE_HOME_SUPPORTER": "App Supporter", - "PAGE_HOME_START_BREW": "", + "PAGE_HOME_START_BREW": "Inizia la preparazione", "PAGE_BEANS_LIST_OBTAINABLE": "Disponibili", "PAGE_BEANS_LIST_YOU_GOT_NO_FRESH_BEANS": "Non c'è più caffè!", "PAGE_BEANS_LIST_YOU_GOT_NO_FINISHED_BEANS": "Nessun caffè archiviato", "PAGE_MILL_LIST_NO_MILL_EXISTING": "Nessun macinacaffè", "PAGE_PREPARATION_LIST_NO_PREPARATION_EXISTING": "Nessun metodo di preparazione", - "PAGE_CONTACT_SUGGESTIONS_QUESTIONS_WISHES": "Suggerimenti, domande, bug o richieste?", + "PAGE_CONTACT_SUGGESTIONS_QUESTIONS_WISHES": "Suggerimenti, domande, bugs o richieste?", "PAGE_THANKS_THANKS_FOR_YOUR_SUPPORT": "Grazie per il sostegno!", "PAGE_SETTINGS_LANGUAGE": "Impostazioni della lingua", "PAGE_SETTINGS_LANGUAGE_GERMAN": "Deutsch", @@ -61,8 +61,8 @@ "PAGE_SETTINGS_GENERAL_SETTINGS": "Impostazioni generali", "PAGE_SETTINGS_TRANSFER": "Trasferisci dati", "PAGE_SETTINGS_PRESET_LAST_BREW": "Attiva parametri preimpostati", - "PAGE_SETTINGS_DISPLAY": "", - "PAGE_SETTINGS_DISPLAY_SINGLE_PAGE": "", + "PAGE_SETTINGS_DISPLAY": "Visualizza", + "PAGE_SETTINGS_DISPLAY_SINGLE_PAGE": "Pagina singola", "PAGE_SETTINGS_DISPLAY_PAGING": "Paginazione", "PAGE_SETTINGS_STARTUP_VIEW": "Pagina iniziale", "PAGE_SETTINGS_ANALYTICS_INFORMATION": "Analytics", @@ -76,10 +76,10 @@ "PAGE_STATISTICS_TOTAL_GROUND_BEANS": "Totale caffè macinato", "PAGE_STATISTICS_MONEY_SPENT_FOR_COFFEE": "Soldi spesi in caffè", "PAGE_STATISTICS_DRUNKEN_BREWS": "Numero totale preparazioni", - "PAGE_STATISTICS_BREW_PROCESSES": "Preparazioni", + "PAGE_STATISTICS_BREW_PROCESSES": "Preparazioni totali", "PAGE_STATISTICS_DRUNKEN_QUANTITY": "Quantità consumata", "PAGE_STATISTICS_BEAN_WEIGHT_USED": "Totale caffè macinato", - "PAGE_BREW_TEXT_INFORMATION_FROM_ROASTER": "", + "PAGE_BREW_TEXT_INFORMATION_FROM_ROASTER": "Info sulla torrefazione", "PAGE_ABOUT_NO_VERSION_AVAILABLE": "Nessuna versione disponibile", "PAGE_ABOUT_APP_VERSION": "Versione App", "PAGE_LICENCES_WEBSITE": "Sito web", @@ -1273,39 +1273,6 @@ "SHOW_MINUTES": "Mostra minuti", "BEANS_UNARCHIVE": "Annulla l'archiviazione", "TOAST_BEAN_UNARCHIVED_SUCCESSFULLY": "Il caffè è stato disarchiviato", - "UPDATE_TEXT_TITLE_TITLE": { - "7.4.0": { - "TITLE": "Versione 7.4.0: Cosa c'è di nuovo", - "DESCRIPTION": [ - "Caffè<\/b>", - "Ora è possibile tenere traccia del caffè congelato, puoi attivare questa funzione nelle impostazioni", - "Aggiunta della data suggerita e di apertura per i caffè, è necessario attivare questo parametro nelle impostazioni", - "Ora è possibile aggiungere del caffè direttamente dalla selezione", - "", - " Termometro <\/b>", - "Supporto del termometro Combustion Inc. - Grazie per il dispositivo!", - "Supporto del termometro Meater (non Meater 2 o Meater +) - Grazie a Yannick!", - "", - "Sezione Acqua<\/b>", - "Aggiunta l'acqua Pure Coffee Water", - "Aggiunta l'acqua Empirical Water", - "", - "Accessori preparazione<\/b>", - "Ordina ora i tuoi strumenti di preparazione", - "", - "Macinacaffè<\/b>", - "Le immagini del macinacaffè sono mostrate nella selezione", - "", - "Impostazioni<\/b>", - "Messaggio di sicurezza se hai modificato la valutazione e il massimo è inferiore a cioè che è già valutato", - "", - "Altro<\/b>", - "Rimossa la recente aggiunta di auto-focus sui secondi del timer della preparazione", - "Piccole migliorie nel codice", - "Piccole migliorie" - ] - } - }, "WATER_TYPE_PURE_COFFEE_WATER": "Pure Coffee Water", "WATER_TYPE_EMPIRICAL_WATER_GLACIAL": "empirical water GLACIAL", "WATER_TYPE_EMPIRICAL_WATER_SPRING": "empirical water SPRING", diff --git a/src/assets/i18n/nl_NL.json b/src/assets/i18n/nl_NL.json new file mode 100644 index 00000000..31e2cd3e --- /dev/null +++ b/src/assets/i18n/nl_NL.json @@ -0,0 +1,1323 @@ +{ + "NAV_MENU": "Menu", + "NAV_HOME": "Thuis", + "NAV_SETTINGS": "Instellingen", + "NAV_BREWS": "Brouwsels", + "NAV_BEANS": "Bonen", + "NAV_PREPARATION": "Methoden", + "NAV_MILL": "Malers", + "NAV_ABOUT_US": "Over ons", + "NAV_CONTACT": "Contact", + "NAV_PRIVACY": "Privacy", + "NAV_CREDITS": "Credits", + "NAV_TERMS": "Algemene voorwaarden", + "NAV_THANKS": "Bedankt!", + "NAV_LICENCES": "OSS-licenties", + "NAV_STATISTICS": "Statistieken", + "NAV_IMPRESSUM": "Impressum", + "NAV_COOKIE": "Cookies", + "NAV_LOGS": "Logboeken", + "NAV_BREW_PARAMS": "Brouwparameters", + "NAV_INFORMATION_TO_APP": "Over Beanconqueror", + "NAV_WATER_SECTION": "Water", + "NAV_HELPER": "Calculaties", + "POPOVER_BREWS_OPTION_REPEAT": "Herhalen", + "POPOVER_BREWS_OPTION_DETAIL": "Details", + "DETAIL": "Details", + "POPOVER_BREWS_OPTION_EDIT": "Bewerking", + "POPOVER_BREWS_OPTION_DELETE": "Verwijder", + "POPOVER_BREWS_OPTION_PHOTO_GALLERY": "Fotogalerij", + "POPOVER_BREWS_OPTION_CUPPING": "Cupping", + "POPOVER_BREWS_OPTION_MAP_COORDINATES": "Toon op kaart", + "POPOVER_BREWS_OPTION_FAST_REPEAT": "Snel brouwen herhalen", + "PAGE_BREWS_NO_ENTRIES": "Nog geen brouwsels toegevoegd", + "PAGE_BREWS_NO_ARCHIVED_ENTRIES": "Je hebt nog geen brouwsels gearchiveerd", + "CANT_START_NEW_BREW_TITLE": "Er ontbreekt hier iets...", + "CANT_START_NEW_BREW_DESCRIPTION": "Om te beginnen, maak een beschrijving voor één type bonen, één zetmethode en één grinder. Gebruik het menu om naar de verschillende categorieën te navigeren om deze informatie toe te voegen.", + "PAGE_HOME_WELCOME_GREETINGS": "Leuk dat je er bent!", + "PAGE_HOME_TOTAL_BREW": "Brouwen", + "PAGE_HOME_TOTAL_BREWS": "Brouwsels", + "PAGE_HOME_BEAN_EXPLORED": "Bonen onderzocht", + "PAGE_HOME_BEANS_EXPLORED": "Bonen onderzocht", + "PAGE_HOME_LAST_BREWS": "Laatste brouwsels", + "PAGE_HOME_LAST_BREW": "Laatste brouwsel", + "PAGE_HOME_DIFFERENT_PREPARATION_METHODS": "Verschillende bereidingsmethoden", + "PAGE_HOME_DIFFERENT_MILLS": "Verschillende malers", + "PAGE_HOME_SUPPORTER": "App Supporter", + "PAGE_HOME_START_BREW": "Begin met brouwen", + "PAGE_BEANS_LIST_OBTAINABLE": "Beschikbaar", + "PAGE_BEANS_LIST_YOU_GOT_NO_FRESH_BEANS": "Je hebt geen verse bonen meer!", + "PAGE_BEANS_LIST_YOU_GOT_NO_FINISHED_BEANS": "Je hebt geen gearchiveerde bonen.", + "PAGE_MILL_LIST_NO_MILL_EXISTING": "Je hebt nog geen grinders toegevoegd.", + "PAGE_PREPARATION_LIST_NO_PREPARATION_EXISTING": "Je hebt nog geen zetmethodes toegevoegd.", + "PAGE_CONTACT_SUGGESTIONS_QUESTIONS_WISHES": "Suggesties, vragen, bugs of verzoeken?", + "PAGE_THANKS_THANKS_FOR_YOUR_SUPPORT": "Bedankt voor je steun!", + "PAGE_SETTINGS_LANGUAGE": "Taalinstellingen", + "PAGE_SETTINGS_LANGUAGE_GERMAN": "Duits", + "PAGE_SETTINGS_LANGUAGE_ENGLISH": "Engels", + "PAGE_SETTINGS_LANGUAGE_SPANISH": "Spaans", + "PAGE_SETTINGS_LANGUAGE_TURKISH": "Turks", + "PAGE_SETTINGS_LANGUAGE_CHINESE": "Chinees", + "PAGE_SETTINGS_GENERAL_SETTINGS": "Algemene instellingen", + "PAGE_SETTINGS_TRANSFER": "Gegevens overdragen", + "PAGE_SETTINGS_PRESET_LAST_BREW": "Vooraf ingestelde waarden?", + "PAGE_SETTINGS_DISPLAY": "Weergave", + "PAGE_SETTINGS_DISPLAY_SINGLE_PAGE": "Eén pagina", + "PAGE_SETTINGS_DISPLAY_PAGING": "Paging", + "PAGE_SETTINGS_STARTUP_VIEW": "Startpagina", + "PAGE_SETTINGS_ANALYTICS_INFORMATION": "Analytics", + "PAGE_SETTINGS_ANALYTICS_INFORMATION_TOOLTIP": "Druk op i voor meer informatie", + "PAGE_SETTINGS_TRACK_BREW_COORDINATES": "Geolocatie van brouwsel opslaan", + "PAGE_SETTINGS_FAST_REPEAT": "Snel herhalen", + "PAGE_SETTINGS_TRACK_CAFFEINE_CONSUMPTION": "bewaar cafeïneconsumptie", + "PAGE_SETTINGS_WAKE_LOCK": "Houd het display actief tijdens het brouwen", + "PAGE_SETTINGS_CURRENCY": "Valuta", + "PAGE_STATISTICS_DIFFERENT_PREPARATION_METHOD": "Bereidingsmethoden", + "PAGE_STATISTICS_TOTAL_GROUND_BEANS": "Totaal gemalen bonen", + "PAGE_STATISTICS_MONEY_SPENT_FOR_COFFEE": "Geld uitgegeven aan bonen", + "PAGE_STATISTICS_DRUNKEN_BREWS": "Totaal aantal brouwsels", + "PAGE_STATISTICS_BREW_PROCESSES": "Totaal brouwsels", + "PAGE_STATISTICS_DRUNKEN_QUANTITY": "Verbruikte hoeveelheid", + "PAGE_STATISTICS_BEAN_WEIGHT_USED": "Totaal gemalen bonen", + "PAGE_BREW_TEXT_INFORMATION_FROM_ROASTER": "Informatie over de brander", + "PAGE_ABOUT_NO_VERSION_AVAILABLE": "Geen Versie beschikbaar", + "PAGE_ABOUT_APP_VERSION": "App Versie", + "PAGE_LICENCES_WEBSITE": "Website", + "BEAN_DATA_ROAST_NAME": "Mate van branding", + "BEAN_DATA_CUSTOM_ROAST_NAME": "Aangepaste mate van branding", + "BEAN_DATA_ROASTING_DATE": "Brand datum", + "BEAN_DATA_ROASTER": "Brander", + "BEAN_DATA_VARIETY": "Variëteit", + "BEAN_DATA_PROCESSING": "Verwerking", + "BEAN_DATA_COUNTRY": "Land", + "BEAN_DATA_MIX": "Melange", + "BEAN_DATA_AROMATICS": "Smaakprofiel", + "BEAN_DATA_WEIGHT": "Gewicht", + "BEAN_DATA_COST": "kosten", + "BEAN_DATA_NAME": "Naam", + "BEAN_DATA_REGION": "Regio", + "BEAN_DATA_FARM": "Boerderij", + "BEAN_DATA_FARMER": "Boer", + "BEAN_DATA_ELEVATION": "Elevatie", + "BEAN_DATA_HARVEST_TIME": "Geoogst", + "BEAN_DATA_PERCENTAGE": "Percentage", + "BEAN_DATA_CERTIFICATION": "Bonencertificering", + "BEAN_DATA_ROASTING_TYPE": "type branding", + "BEAN_DATA_DECAFFEINATED": "Cafeïnevrij", + "BEAN_DATA_URL": "Website", + "BEAN_DATA_EAN": "EAN \/ Artikelnummer", + "BEAN_DATA_PURCHASING_PRICE": "Aankoopprijs", + "BEAN_DATA_FOB_PRICE": "FOB-prijs", + "BEAN_DATA_CUPPING_POINTS": "Cupping-punten", + "BREW_DATA_CUSTOM_BREW_TIME": "Aangepaste brouwtijd", + "BREW_CREATION_DATE": "Aanmaakdatum", + "REPEAT": "Herhalen", + "EDIT": "Bewerken", + "DELETE": "Verwijderen", + "FINISHED": "Archief", + "NOTES": "Notities", + "ADD_PHOTO": "Foto toevoegen", + "CANCEL": "Annuleren", + "GENERATE": "Genereren", + "SAVE": "Opslaan", + "ADD_SOMETHING": "Toevoegen", + "CONTACT": "Contact", + "NAME": "Naam", + "IMPORT": "import", + "EXPORT": "Export", + "VIEW": "Weergave", + "ARCHIVE": "archief", + "CURRENT": "huidig", + "BACK": "Terug", + "CLOSE": "sluiten", + "DAY": "Dag", + "BREW_DATA_TEMPERATURE_TIME": "Temperatuur Tijd", + "BREW_DATA_SURF_TIME": "Surftijd", + "BREW_DATA_TIME": "Tijd", + "BREW_DATA_GRIND_SIZE": "Maalinstelling", + "BREW_DATA_GRIND_WEIGHT": "Gemalen koffie (gr)", + "BREW_DATA_IN_OUT_BR": "In\/Uit (BR)", + "BREW_DATA_NOTES": "Notities", + "BREW_DATA_PREPARATION_METHOD": "Bereidingswijze", + "BREW_DATA_MILL": "Maler", + "BREW_DATA_MILL_SPEED": "Maalsnelheid (rpm)", + "BREW_DATA_MILL_TIMER": "Maaltijd", + "BREW_DATA_BREW_QUANTITY": "Hoeveelheid water", + "BREW_DATA_BEAN_TYPE": "Boon type", + "BREW_DATA_BREW_TEMPERATURE": "Brouwtemperatuur", + "BREW_DATA_PRESSURE_PROFILE": "Profiel", + "BREW_DATA_COFFEE_TYPE": "Type koffie", + "BREW_DATA_COFFEE_CONCENTRATION": "Koffie Concentratie", + "BREW_DATA_COFFEE_FIRST_DRIP_TIME": "Eerste druppeltijd", + "BREW_DATA_COFFEE_BLOOMING_TIME": "Bloeitijd \/ Pre-infusie", + "BREW_DATA_ATTACHMENTS": "Bijlagen \/ Foto's", + "BREW_DATA_RATING": "Beoordeling", + "BREW_DATA_CALCULATED_COFFEE_BREW_TIME": "Koffiezettijd", + "BREW_DATA_TDS": "Totaal opgeloste vaste stoffen %", + "BREW_DATA_CALCULATED_EXTRACTION_YIELD": "Extractie Opbrengst %", + "BREW_INFORMATION_BREW_RATIO": "Brouw Verhouding", + "BREW_INFORMATION_BEAN_AGE": "Boon leeftijd", + "BREW_INFORMATION_BREW_QUANTITY_TYPE_NAME": "Hoeveelheidstype", + "BREW_DATA_TDS_EY": "TDS \/ %EY", + "BREW_DATA_BREW_BEVERAGE_QUANTITY": "Hoeveelheid drank", + "BREW_DATA_PREPARATION_METHOD_TOOL": "bereidings gereedschappen", + "BREW_DATA_WATER": "Water", + "BREW_DATA_BEAN_WEIGHT_IN": "Bonengewicht (g)", + "BREW_DATA_VESSEL": "Serveerkan", + "BREW_DATA_VESSEL_WEIGHT": "Serveerkan gewicht", + "BREW_DATA_VESSEL_NAME": "Serveerkan naam", + "BREW_DATA_FLAVOR": "Smaak", + "BREW_DATA_FLOW_PROFILE": "Stroom", + "ONE_DAY": "Dag", + "DAYS": "Dagen", + "ONE_HOUR": "uur", + "HOURS": "uren", + "ONE_MINUTE": "minuut", + "MINUTES": "minuten", + "WITHOUT_COFFEE": "zonder koffie", + "NOT_FOUND": "Niet gevonden", + "INVALID_FILE_FORMAT": "Ongeldig bestandsformaat", + "FILE_NOT_FOUND_INFORMATION": "Bestand niet gevonden", + "ERROR_ON_FILE_READING": "Fout bij het lezen van bestandsgegevens", + "IMPORT_SUCCESSFULLY": "Importeren succesvol", + "IMPORT_UNSUCCESSFULLY_DATA_NOT_CHANGED": "Import mislukt, er zijn geen gegevens gewijzigd", + "INVALID_FILE_DATA": "Ongeldige bestandsinhoud", + "DOWNLOADED": "Gedownload", + "FILE_DOWNLOADED_SUCCESSFULLY": "Bestand ' {{fileName}} ' is succesvol gedownload naar uw downloadmap!", + "NO": "Nee", + "YES": "Ja", + "SURE_QUESTION": "Weet je het zeker?", + "DELETE_BREW_QUESTION": "Brouwsel verwijderen?", + "DELETE_BEAN_QUESTION": "Boon verwijderen? Alle bijbehorende brouwsels worden ook verwijderd!", + "DELETE_GREEN_BEAN_QUESTION": "Groene bonen verwijderen? Alle bijbehorende geroosterde bonen, evenals brouwsels worden verwijderd!", + "DELETE_MILL_QUESTION": "Grinder verwijderen? Alle bijbehorende brouwsels worden ook verwijderd!", + "DELETE_PREPARATION_METHOD_QUESTION": "Bereidingswijze verwijderen? Alle bijbehorende brouwsels worden ook verwijderd!", + "DELETE_PREPARATION_TOOL_QUESTION": "Bereidingstool verwijderen? Alle brouwsels die aan deze tool zijn gekoppeld, worden bijgewerkt.", + "APP_COULD_NOT_STARTED_CORRECTLY_BECAUSE_MISSING_FILESYSTEM": "App kon niet correct worden gestart vanwege ontbrekend bestandssysteem", + "CARE": "Zorg", + "ERROR_OCCURED": "Er is een fout opgetreden", + "CSV_FILE_NOT_DOWNLOADED": "CSV-bestand kon niet worden gedownload!", + "CSV_FILE_DOWNLOADED_SUCCESSFULLY": "CSV-bestand ' {{fileName}} ' is succesvol gedownload naar uw downloadmap!", + "ADD_BREW": "Voeg brouwsel toe", + "CHOOSE": "Kies", + "CHOOSE_PHOTO_OR_LIBRARY": "Maak een foto of kies uit de fotogalerij.", + "RECORD": "Maak een foto", + "PAGE_BEAN_INFORMATION": "Boon informatie", + "PAGE_BEAN_INFORMATION_GOOD_BREWS": "Goed", + "PAGE_BEAN_INFORMATION_BAD_BREWS": "Slecht", + "PAGE_BEAN_INFORMATION_COUNT_BREWS": "Totaal aantal brouwsels", + "INFORMATION": "Informatie", + "PAGE_BEAN_BREW_CHART_TITLE": "Brouwoverzicht voor deze boon", + "PAGE_BEAN_INFORMATION_AWESOME_BREWS": "Geweldig", + "PAGE_BEAN_INFORMATION_NORMAL_BREWS": "Normaal", + "PAGE_BEAN_INFORMATION_NOT_RATED_BREWS": "Niet beoordeeld", + "PAGE_PREPARATION_INFORMATION_BREWS_DONE": "Brouwsels met deze bereidingswijze", + "PAGE_PREPARATION_INFORMATION_BREWED_QUANTITY": "Gebrouwen hoeveelheid", + "PAGE_PREPARATION_INFORMATION_GRIND_WEIGHT": "Verbruikt gewicht van bonen", + "PAGE_PREPARATION_INFORMATION_TIME_SPENT_BREWING": "Totale bereidingstijd", + "PAGE_PREPARATION_INFORMATION": "Informatie over de bereiding", + "SECONDS": "Seconden", + "PAGE_MILL_INFORMATION": "Maler informatie", + "PAGE_MILL_INFORMATION_BREWS_DONE": "Brouwsels met deze maler", + "PAGE_MILL_INFORMATION_GRIND_WEIGHT": "Verbruikt gewicht van bonen", + "PAGE_HELPER_WATER_HARDNESS": "Waterhardheid", + "PAGE_HELPER_WATER_HARDNESS_CA_CONTENTS": "Calciumgehalte mg\/l", + "PAGE_HELPER_WATER_HARDNESS_MG_CONTENTS": "Magnesiumgehalte mg\/l", + "PAGE_HELPER_WATER_HARDNESS_GERMAN_HARDNESS": "Duitse hardheidsgraad", + "PAGE_HELPER_WATER_HARDNESS_TOTAL_HARDNESS": "Totale hardheid", + "PAGE_HELPER_BREW_RATIO": "Brouw Verhouding", + "PAGE_HELPER_BREW_RATIO_GROUND_COFFEE": "Gemalen koffie (gr)", + "PAGE_HELPER_BREW_RATIO_WATER": "Vloeistof (gr\/ml)", + "PAGE_HELPER_BREW_RATIO_CALCULATED": "Berekende brouwverhouding", + "PAGE_SETTINGS_SHOW_ARCHIVED_BREWS": "Gearchiveerde brouwsels weergeven", + "PAGE_SETTINGS_SHOW_ARCHIVED_BEANS": "Gearchiveerde bonen weergeven", + "PAGE_SETTINGS_SHOW_ARCHIVED_GREEN_BEANS": "Toon gearchiveerde groene bonen", + "CUPPING_SCORE": "Score", + "CUPPING_SCORE_DRY_FRAGRANCE": "Droge geur", + "CUPPING_SCORE_WET_AROMA": "Nat aroma", + "CUPPING_SCORE_BRIGHTNESS": "Helderheid", + "CUPPING_SCORE_FLAVOR": "Smaak", + "CUPPING_SCORE_BODY": "Body", + "CUPPING_SCORE_FINISH": "Afwerking", + "CUPPING_SCORE_SWEETNESS": "Zoetheid", + "CUPPING_SCORE_CLEAN_CUP": "Clean Cup", + "CUPPING_SCORE_COMPLEXITY": "Complexiteit", + "CUPPING_SCORE_UNIFORMITY": "Uniformiteit", + "CUPPING_SCORE_CUPPERS_CORRECTION": "Cuppers-correctie", + "CUPPING_SCORE_DRY_FRAGRANCE_TOOLTIP": "Verwijst naar het aroma van de droge gemalen koffie voordat er heet water aan wordt toegevoegd.", + "CUPPING_SCORE_WET_AROMA_TOOLTIP": "De geur van nat koffiedik, nadat er heet water aan is toegevoegd.", + "CUPPING_SCORE_BRIGHTNESS_TOOLTIP": "Zuurgraad is de smaak van scherpe hoge tonen in de koffie, veroorzaakt door een set van chlorogeen, citroenzuur, kininezuur, azijnzuur en andere, voornamelijk waargenomen in de voorkant van de mond en op de tong. (Het is een goede kwaliteit; niet gerelateerd aan bitterheid in koffie, en niet direct verantwoordelijk voor maagklachten). Zuurgraad wordt gewaardeerd door veel kopers, en is direct gerelateerd aan de kwaliteit van de kop, aangezien zuurgraad het product is van aanplantingen op grote hoogte.", + "CUPPING_SCORE_FLAVOR_TOOLTIP": "Dit is de algehele indruk in de mond, inclusief alle andere beoordelingen. Er zijn 4 \"primaire smaak\"-groeperingen (zuur, zoet, zout, bitter) en veel \"secundaire smaken\".", + "CUPPING_SCORE_BODY_TOOLTIP": "Vaak \"mondgevoel\" genoemd, is body het gevoel van gewicht en dikte van de gezette koffie, veroorzaakt door het percentage oplosbare vaste stoffen in de kop, inclusief alle organische verbindingen die worden geëxtraheerd (de zetmethode en de hoeveelheid gemalen koffie die wordt gebruikt, hebben hier grote invloed op). We beoordelen Body op een lagere schaal omdat licht gebodyde koffiesoorten zeker niet slecht zijn en in sommige origines past de lichtere body het beste bij het algehele karakter van de kop.", + "CUPPING_SCORE_FINISH_TOOLTIP": "De aanhoudende of opkomende smaken die komen nadat de mond is schoongemaakt. Dit omvat de tijd dat de koffie je mond verlaat tot minuten daarna...een reden dat je veel cuppers zult vinden die hun nasmaakscores herzien terwijl ze een minuut of twee later nog steeds een positieve smaak ervaren.", + "CUPPING_SCORE_SWEETNESS_TOOLTIP": "Zoetheid is bijna altijd een gewenste kwaliteit in koffie, zelfs als het op eufemistische wijze wordt beschreven, zoals \"rustieke zoetheid\" of \"bitterzoetheid\". U zult merken dat verfijnde zoetheid (denk aan Europees gebak, fijn snoepgoed, witte suiker, pure zoetheid) hoog scoort, evenals complexe zoetheid van fruitsuikers (fructose). Moutige zoetheid (maltose) is minder traditioneel, maar wel wenselijk en honing kan variëren van heel puur en schoon tot complex, rustiek, bijna gistachtig. Kortom, als zoetheid een sleutel is tot de kop, zal het goed worden beoordeeld.", + "CUPPING_SCORE_CLEAN_CUP_TOOLTIP": "Let op dat \"clean cup\" niet letterlijk betekent dat er geen vuil op de koffie zit. Het gaat alleen om smaak en rauwe, funky koffies die \"onrein\" zijn en de smaak kan ook heel wenselijk zijn, zoals nat-gepelde Indonesische koffies uit Sumatra, of droog verwerkte Ethiopische en Jemenitische types.", + "CUPPING_SCORE_COMPLEXITY_TOOLTIP": "Complexiteit complimenteert de scores voor \"smaak\" en \"afwerking\", om een veelheid of gelaagdheid van veel smaken te communiceren. Het betekent dat er veel te ontdekken valt in de kop. Aan de andere kant kunnen simpele koffies een opluchting zijn na overmatige blootstelling aan veel krachtige, intense, complexe koffies.", + "CUPPING_SCORE_UNIFORMITY_TOOLTIP": "Uniformiteit verwijst naar verschillen tussen kopjes. Koffiesoorten die met een droog proces zijn bereid, kunnen van nature minder uniform zijn dan koffiesoorten die met een nat proces zijn bereid. We zouden nooit een partij met fantastische smaken vermijden als deze af en toe afwijkt. Dit wordt gescoord tijdens het cuppingprotocol, waarbij meerdere kopjes worden gemaakt van elke partij die wordt beoordeeld.", + "CUPPING_SCORE_CUPPERS_CORRECTION_TOOLTIP": "Dit is aangepast van het SCAA-systeem en de Cup of Excellence-score (soms noemen ze het \"Overall Points\"). Het stelt een cupper in staat om ervoor te zorgen dat de totale score de algehele indruk van de cup correct weergeeft. U zou deze aanpak kunnen bekritiseren en het \"vervalsen\" van het totaal kunnen beschouwen. In zekere zin zou u gelijk hebben ... maar het zou veel erger zijn om de categoriescores te veranderen om het gewenste totaal te bereiken (om een koffie een 9 te geven voor zuurgraad terwijl u weet dat het een 7 is), of omgekeerd om een koffie die absoluut een 90 verdient, op 84 te laten eindigen. Het specifieke Cupper's Correction-nummer doet er niet toe, of het nu een 5 of een 8 is ... het idee is dat de totale score een correcte indruk geeft van de kwaliteit van de koffie.", + "CUPPING_SCORE_TOOLTIP": "100-95 = Verbazingwekkend, 90-94 = Uitstekend, 85-89 = Zeer goed, 80-84 = Goed, 75-79 = Redelijk, 70-74 = Slecht", + "DETAIL_BREW": "Brouw details", + "DETAIL_BEAN": "Boon details", + "DETAIL_MILL": "Maler details", + "DETAIL_PREPARATION": "Bereidings details", + "EDIT_BREW": "Bewerk brouwsel", + "ADD_BEAN": "Boon toevoegen", + "EDIT_BEAN": "Bewerken boon", + "ADD_PREPARATION": "Bereidingsmethode toevoegen", + "EDIT_PREPARATION": "Bewerk bereidingsmethode", + "ADD_MILL": "Voeg maler toe", + "EDIT_MILL": "Maler bewerken", + "USE_FILTER": "Filter toepassen", + "RESET_FILTER": "Filter resetten", + "COFFEE_GRAMS_GRINDED": "Gram gemalen", + "BEANS_USED": "Bonen geconsumeerd", + "BREW_HEADER_BEFORE_BREW": "Voor het brouwen", + "BREW_HEADER_WHILE_BREW": "Tijdens het brouwen", + "BREW_HEADER_AFTER_BREW": "Na het brouwen", + "BREW_HEADER_CUPPING": "Proeven", + "BEANS_CONSUMED": "Archieveer", + "NAV_MANAGE_PARAMETERS": "Parameters beheren", + "NAV_SORT_PARAMETERS": "Sorteer parameters", + "NAV_DEFAULT_PARAMETERS": "Standaardparameters definiëren", + "PAGE_SORT_PARAMETERS_DESCRIPTION": "Versleep parameters om te bepalen in welke volgorde ze worden weergegeven.", + "PAGE_MANAGE_PARAMETERS_DESCRIPTION": "Geef aan welke parameters moeten worden weergegeven bij het bewerken van brouwinformatie.", + "PAGE_DEFAULT_PARAMETERS_DESCRIPTION": "Markeer welke parameters standaard moeten worden ingesteld op de laatst gebruikte waarde.", + "SORT_PARAMETERS_BEFORE": "Voor het brouwen", + "SORT_PARAMETERS_MEANWHILE": "Tijdens het brouwen", + "SORT_PARAMETERS_AFTER": "Na het brouwen", + "MORE_INFORMATION": "Meer informatie", + "UNDERSTOOD": "Begrepen", + "WELCOME_PAGE_ACTIVATE_ANALYTICS_TITLE": "Analyse en tracking", + "WELCOME_PAGE_ACTIVATE_ANALYTICS_DESCRIPTION": "We willen de app, de website en onze toekomstige diensten voor u voortdurend verbeteren. Om dit te doen, moeten we wat gegevens bijhouden over hoe u de app en de functies ervan gebruikt. Maar we beloven dat we nooit persoonlijke gegevens zullen bijhouden. Om deze beloften waar te maken, gebruiken we Matomo, een open source service gericht op gegevensbeveiliging en privacy die op onze eigen server wordt gehost - dit zorgt ervoor dat alleen wij eigenaar zijn van de gegevens. Onze website biedt alle informatie over de parameters die we bijhouden en bovendien kunt u de broncode bekijken die 100% open source is. Heeft u vragen? Neem dan gerust contact met ons op.", + "ANALYTICS_INFORMATION_TITLE": "Analyse en tracking", + "ANALYTICS_INFORMATION_DESCRIPTION": "Zoals u weet, is de veiligheid van uw gegevens en uw privacy onze topprioriteit. Daarom zijn we overgestapt van Google Analytics naar de open source service Matomo, die zich richt op gegevensbeveiliging en privacy. Deze service wordt gehost op onze eigen server. Dit betekent dat wij volledig eigenaar zijn van de gegevens. De bijgehouden parameters zijn niet gewijzigd en we beloven nog steeds dat we nooit persoonlijke gegevens zullen bijhouden. Op onze website vindt u alle informatie over de parameters die we bijhouden. Bovendien kunt u de broncode bekijken, die 100% open source is. Heeft u vragen? Neem dan gerust contact met ons op.", + "ACTIVATE": "Activeren", + "DO_NOT_ACTIVE": "Niet activeren", + "WELCOME_PAGE_BEAN_TITLE": "Boon", + "WELCOME_PAGE_BEAN_DESCRIPTION": "Koffie zetten zonder bonen is een beetje lastig. Voeg je eerste boon toe om te beginnen!", + "WELCOME_PAGE_BEAN_ADD": "Boon toevoegen", + "SKIP": "Overslaan", + "WELCOME_PAGE_PREPARATION_TITLE": "Bereidingswijze", + "WELCOME_PAGE_PREPARATION_DESCRIPTION": "V60, Aeropress, Espresso - er zijn veel manieren om koffie te zetten. Voeg er minstens één toe.", + "WELCOME_PAGE_PREPARATION_ADD": "Bereidingswijze toevoegen", + "WELCOME_PAGE_MILL_TITLE": "Maler", + "WELCOME_PAGE_MILL_DESCRIPTION": "Bijna klaar, maar je hebt iets nodig om je bonen te malen! Voeg minstens één maler toe.", + "WELCOME_PAGE_MILL_ADD": "Voeg Maler toe", + "WELCOME_PAGE_TITLE": "Welkom!", + "WELCOME_PAGE_BEAN_HEADLINE": "Eerste boon", + "WELCOME_PAGE_PREPARATION_HEADLINE": "Bereidingswijze toevoegen", + "WELCOME_PAGE_MILL_HEADLINE": "Eerste Maler", + "WELCOME_PAGE_LETS_START_HEADLINE": "Nu kunnen we beginnen met brouwen!", + "WELCOME_PAGE_LETS_START_TITLE": "Nu kunnen we beginnen met brouwen!", + "WELCOME_PAGE_LETS_START_DESCRIPTION": "Gefeliciteerd, je bent klaar om de beste koffie van je leven te zetten. Veel plezier en verspreid de liefde voor goede koffie!", + "PREPARATION_TYPE": "Bereidingsmethode", + "PREPARATION_TYPE_NAME": "Naam", + "ARCHIVED": "Gearchiveerd", + "PAGE_SETTINGS_SHOW_ARCHIVED_PREPARATIONS": "Toon gearchiveerde bereidingsmethoden", + "PAGE_SETTINGS_SHOW_ARCHIVED_MILLS": "Toon gearchiveerde malers", + "PAGE_MILL_LIST_NO_ARCHIVED_MILL_EXISTING": "Er zijn nog geen malers gearchiveerd.", + "PAGE_PREPARATION_LIST_NO_ARCHIVED_PREPARATION_EXISTING": "Je hebt geen gearchiveerde bereidingswijzen.", + "TOAST_BREW_ADDED_SUCCESSFULLY": "Brouwsel succesvol toegevoegd", + "TOAST_BREW_REPEATED_SUCCESSFULLY": "Brouwsel succesvol herhaald", + "TOAST_BEAN_ADDED_SUCCESSFULLY": "Boon succesvol toegevoegd", + "TOAST_MILL_ADDED_SUCCESSFULLY": "Maler succesvol toegevoegd", + "TOAST_PREPARATION_ADDED_SUCCESSFULLY": "Bereidingswijze succesvol toegevoegd", + "TOAST_WATER_ADDED_SUCCESSFULLY": "Water succesvol toegevoegd", + "TOAST_BREW_DELETED_SUCCESSFULLY": "Brouwsel is verwijderd", + "TOAST_BEAN_DELETED_SUCCESSFULLY": "Boon is verwijderd", + "TOAST_GREEN_BEAN_DELETED_SUCCESSFULLY": "Groene boon is verwijderd", + "TOAST_MILL_DELETED_SUCCESSFULLY": "Maler is verwijderd", + "TOAST_WATER_DELETED_SUCCESSFULLY": "Water is verwijderd", + "TOAST_PREPARATION_DELETED_SUCCESSFULLY": "Bereidingswijze is verwijderd", + "TOAST_BREW_EDITED_SUCCESSFULLY": "Brouwsel is bewerkt", + "TOAST_BEAN_EDITED_SUCCESSFULLY": "Boon is bewerkt", + "TOAST_MILL_EDITED_SUCCESSFULLY": "Maler is bewerkt", + "TOAST_PREPARATION_EDITED_SUCCESSFULLY": "Bereiding is bewerkt", + "TOAST_WATER_EDITED_SUCCESSFULLY": "Water is bewerkt", + "TOAST_BEAN_ARCHIVED_SUCCESSFULLY": "Boon is gearchiveerd", + "TOAST_MILL_ARCHIVED_SUCCESSFULLY": "Maler is gearchiveerd", + "TOAST_PREPARATION_ARCHIVED_SUCCESSFULLY": "Bereidingsmethode is gearchiveerd", + "TOAST_WATER_ARCHIVED_SUCCESSFULLY": "Water is gearchiveerd", + "BEAN_WEIGHT_ALREADY_USED": "{{gramUsed}} g van {{gramTotal}} g ( {{leftOver}} g)", + "PREPARATION_TYPE_CUSTOM_PREPARATION": "Aangepaste bereidingsmethode", + "PREPARATION_TYPE_AEROPRESS": "Aeropress", + "PREPARATION_TYPE_V60": "V60", + "PREPARATION_TYPE_CHEMEX": "Chemex", + "PREPARATION_TYPE_BIALETTI": "Bialetti", + "PREPARATION_TYPE_PORTAFILTER": "Espresso machine", + "PREPARATION_TYPE_KALITA_WAVE": "Kalita Wave", + "PREPARATION_TYPE_FRENCH_PRESS": "French Press", + "PREPARATION_TYPE_SWANNECK": "Swan Neck", + "PREPARATION_TYPE_DRIPPER": "Dripper", + "PREPARATION_TYPE_DELTER_PRESS": "Delter Press", + "PREPARATION_TYPE_COLD_BREW": "Koud brouwsel", + "PREPARATION_TYPE_AEROPRESS_INVERTED": "Aeropress Omgekeerd", + "PREPARATION_TYPE_TURKISH": "Turks", + "PREPARATION_TYPE_BLUE_DRIPPER": "Blue Dripper", + "PREPARATION_TYPE_ADD_CUSTOM": "Aangepaste methode toevoegen", + "PREPARATION_TYPE_GINA": "Gina", + "PREPARATION_TYPE_KONO": "Kono", + "PREPARATION_TYPE_ORIGAMI": "Origami", + "PREPARATION_TYPE_CAFELAT": "Cafelat", + "PREPARATION_TYPE_OREA": "Orea", + "PREPARATION_TYPE_COLD_DRIP": "Cold Drip", + "PREPARATION_TYPE_HAND_LEVER": "Handmatige hendel", + "PREPARATION_TYPE_FLAIR": "Flair", + "PREPARATION_TYPE_APRIL_BREWER": "April Brewer", + "PREPARATION_TYPE_ESPRO_BLOOM": "Espreo Bloom", + "PREPARATION_TYPE_FELLOW_STAGG": "Fellow Stagg", + "PREPARATION_TYPE_HSIAO_50": "Hsiao 50", + "PREPARATION_TYPE_KARLSBADER_KANNE": "Karlsbader", + "PREPARATION_TYPE_MOCCA_MASTER": "Mocca Master", + "PREPARATION_TYPE_SIPHON": "Sifon", + "CHOOSE_BEANS": "Selecteer bonen", + "CHOOSE_BEAN": "Selecteer boon", + "CHOOSE_WATERS": "Selecteer wateren", + "CHOOSE_WATER": "Selecteer water", + "CHOOSE_PREPARATIONS": "Selecteer bereidingsmethoden", + "CHOOSE_PREPARATION": "Selecteer bereidingsmethode", + "CHOOSE_MILLS": "Selecteer molens", + "CHOOSE_MILL": "Selecteer molen", + "BEAN": { + "PLACE_HOLDER": { + "BEAN_DATA_NAME": "Naam toevoegen", + "BEAN_DATA_ROAST_NAME": "Voeg de mate van branding toe", + "BEAN_DATA_ROASTING_DATE": "Wanneer werden de bonen geroosterd?", + "BEAN_DATA_ROASTER": "Wie heeft de bonen geroosterd?", + "BEAN_DATA_VARIETY": "Voeg de koffievariant toe", + "BEAN_DATA_PROCESSING": "Koffieverwerking, bijvoorbeeld gewassen", + "BEAN_DATA_COUNTRY": "Waar komt de boon vandaan?", + "BEAN_DATA_MIX": "Wat is de melange ratio?", + "BEAN_DATA_AROMATICS": "Voeg smaken toe", + "BEAN_DATA_WEIGHT": "Gewicht van de bonen", + "BEAN_DATA_COST": "Hoeveel hebben de bonen gekost?", + "BEAN_DATA_REGION": "Regio toevoegen", + "BEAN_DATA_FARM": "Voeg de boerderij toe", + "BEAN_DATA_FARMER": "Voeg de boer toe", + "BEAN_DATA_ELEVATION": "Op welke hoogte werden de bonen geteeld", + "BEAN_DATA_HARVEST_TIME": "wanneer zijn de bonen geoogst?", + "BEAN_DATA_BUY_DATE": "Wanneer zijn de bonen gekocht?", + "BEAN_DATA_PERCENTAGE": "Voeg het percentage van deze boon in de melange toe", + "BEAN_DATA_CERTIFICATION": "Voeg de bonencertificering toe (bijv. fairtrade, bio)", + "BEAN_DATA_ROASTING_TYPE": "Voeg brand type toe", + "BEAN_DATA_DECAFFEINATED": "Is deze koffie cafeïnevrij", + "BEAN_DATA_URL": "Voeg de website-url toe", + "BEAN_DATA_EAN": "Voeg het EAN- of artikelnummer toe", + "BEAN_DATA_CUPPING_POINTS": "Voeg de cuppingpunten toe", + "BEAN_DATA_PURCHASING_PRICE": "Voeg de aankoopprijs toe", + "BEAN_DATA_FOB_PRICE": "Voeg de FOB-prijs toe", + "NOTES": "Voeg notities toe over deze bonen", + "CHOOSE_DATA_ROASTER": "Kies een brander", + "CHOOSE_DATA_ROASTING_TYPE": "Kies type branding", + "BEAN_DATA_BEST_DATE": "Wanneer is de beste datum om de bonen te gebruiken?", + "BEAN_DATA_OPEN_DATE": "Wanneer is de zak bonen geopend?", + "FROZEN_NOTES": "Zijn er opmerkingen over de bevroren koffie? Bijvoorbeeld in welke vriezer je het hebt bewaard." + } + }, + "PREPARATION": { + "PLACE_HOLDER": { + "PREPARATION_TYPE_NAME": "Voeg een naam toe", + "NOTES": "Voeg notities toe over deze bereidingsmethode" + } + }, + "MILL": { + "PLACE_HOLDER": { + "NAME": "Voeg een naam toe", + "NOTES": "Voeg notities toe over deze grinder" + } + }, + "BREW": { + "PLACE_HOLDER": { + "BREW_DATA_GRIND_SIZE": "Voer de maalinstelling in", + "BREW_DATA_GRIND_WEIGHT": "Vul de hoeveelheid koffie in (gr)", + "BREW_DATA_BREW_TEMPERATURE": "Voer de brouwtemperatuur in", + "BREW_DATA_PREPARATION_METHOD": "Selecteer bereidingsmethode", + "BREW_DATA_BEAN_TYPE": "Selecteer bonen", + "BREW_DATA_MILL": "Selecteer een molen", + "BREW_DATA_MILL_SPEED": "Voer de maalsnelheid in", + "BREW_DATA_MILL_TIMER": "Voer de maal tijd in", + "BREW_DATA_PRESSURE_PROFILE": "Druk\/stroomprofiel, brouwmethodologie", + "BREW_DATA_TEMPERATURE_TIME": "Voer de tijd in die de machine heeft mogen opwarmen", + "BREW_DATA_COFFEE_BLOOMING_TIME": "Hoe lang is de bloom?", + "BREW_DATA_COFFEE_FIRST_DRIP_TIME": "Wanneer verscheen de eerste druppel?", + "BREW_DATA_BREW_QUANTITY": "Hoeveel water heb je gebruikt om te brouwen?", + "BREW_DATA_COFFEE_TYPE": "Voer de koffiestijl in (bijv. ristretto)", + "BREW_DATA_COFFEE_CONCENTRATION": "Voer de koffie concentratie in", + "BREW_DATA_TDS": "Wat was de gemeten hoeveelheid opgeloste vaste stoffen (TDS)?", + "BREW_DATA_NOTES": "Voeg notities toe over dit brouwsel", + "BREW_DATA_BREW_BEVERAGE_QUANTITY": "Voeg totale drank hoeveelheid toe", + "BREW_DATA_PREPARATION_METHOD_TOOL": "Kies je bereiding tools", + "BREW_DATA_WATER": "Kies het gebruikte water", + "BREW_DATA_BEAN_WEIGHT_IN": "Welk gewicht aan bonen heb je gebruikt?" + } + }, + "ROASTED_BEFORE": "Geroosterd voor", + "DAY_OLD": "dag oud", + "DAYS_OLD": "dagen oud", + "BEANS_AMOUNT_USED": "Verbruikt", + "CUPPING_BREW": "Proeven", + "COFFEE_DRUNKEN_QUANTITY": "Koffie dronken", + "IMAGE_DELETED": "Afbeelding is verwijderd", + "IMAGE_NOT_DELETED": "Afbeelding kon niet worden verwijderd", + "EXTERNAL_STORAGE_NOT_SUPPORTED": "Sorry, externe opslag wordt niet ondersteund", + "BEANS_ARCHIVED": "Gearchiveerd", + "TAB_ARCHIVE": "Archief", + "TODAY": "Vandaag", + "PLEASE_WAIT": "Even geduld aub...", + "PREPARATION_STYLE_POUR_OVER": "Pourover", + "PREPARATION_STYLE_ESPRESSO": "Espresso", + "PREPARATION_STYLE_FULL_IMMERSION": "Immersie", + "PREPARATION_STYLE_PERCOLATION": "Percolatie", + "PREPARATION_TYPE_STYLE": "Bereidingsstijl", + "PAGE_SETTINGS_FAST_REPEAT_DESCRIPTION": "Activeert een nieuw menu-item - hiermee kun je een brouwsel kopieren.", + "PAGE_SETTINGS_TRACK_BREW_COORDINATES_DESCRIPTION": "Sla geolocatie gegevens op voor elk brouwsel.", + "PAGE_SETTINGS_TRACK_CAFFEINE_CONSUMPTION_DESCRIPTION": "Bewaar de hoeveelheid geconsumeerde cafeïne", + "UPDATE_TITLE": "Wat is er nieuw?", + "NEXT": "Volgende", + "CUSTOM_PARAMETERS": "Parameters aanpassen", + "CUSTOM_DEFAULT_PARAMETERS": "Standaard", + "CUSTOM_MANAGE_PARAMETERS": "Beheer", + "CUSTOM_SORT_PARAMETERS": "Sorteer", + "BREW_PARAMETER_CUSTOMIZE_TITLE": "Aangepaste parameters voor elke bereidingsmethode", + "BREW_PARAMETER_CUSTOMIZE_DESCRIPTION": "Wilt u voor elke bereidingsmethode aangepaste parameters kiezen? Navigeer naar \"Methoden\", open het menu van de specifieke bereidingsmethode en kies \"Parameters aanpassen\". Daar kunt u kiezen welke parameters voor deze bereiding worden gebruikt!", + "BREW_DATA_BREW_QUANTITY_TOOLTIP": "Hoeveelheid water (niet bruikbaar voor espresso)", + "BREW_DATA_COFFEE_FIRST_DRIP_TIME_TOOLTIP": "Eerste druppel tijd van de koffie (alleen espresso)", + "BREW_DATA_PREPARATION_METHOD_TOOLTIP": "Voorbereiding (alleen aanpasbaar indien actief)", + "PAGE_SETTINGS_GENERAL": "Algemene instellingen", + "EDIT_PREPARATION_CUSTOM_PARAMETERS": "Parameters aanpassen", + "ENABLE_PREPARATION_CUSTOM_PARAMETERS": "Aangepaste parameters gebruiken", + "BEAN_ADD_ANOTHER_SORT": "Voeg een andere boon type toe", + "BEAN_SORT": "Boon Type", + "BEAN_SORT_INFORMATION": "Informatie over de variëteit", + "BEAN_SORT_MORE_INFORMATION": "Meer informatie", + "NAVIGATE_TO_PREPARATION_METHODS": "Navigeer naar bereidingsmethoden", + "PREPARATION_TOOLS": "bereidingsmiddelen", + "PREPARATION_TOOLS_INFORMATION": "Voeg verschillende mandjes toe, WDT-gereedschappen voor uw espressomachine; stoffen, papieren of gaasfilters voor uw filterkoffie, enz.", + "PREPARATION_TOOLS_PLACEHOLDER": "Papieren of stoffen filter, VST 20g, 14g mandje, enz.", + "PREPARATION_PARAMETERS_CUSTOMIZED": "Aangepaste parameters", + "BEANS_WEIGHT_AVAILABLE": "Beschikbare bonen", + "SORT_ORDER": "Sorteervolgorde wijzigen", + "ASCENDING": "Oplopend", + "DESCENDING": "Aflopend", + "SORT_AFTER": "Sorteren na", + "BEAN_SORT_NAME_OF_BEAN": "Boon naam", + "BEAN_SORT_ROASTER": "Brander", + "BEAN_SORT_ROASTING_DATE": "Datum van roosteren", + "BEAN_TAB_ROAST_INFORMATION": "Geroosterde informatie", + "BEAN_TAB_GENERAL_INFORMATION": "Algemeen", + "BEAN_TAB_SORT_INFORMATION": "Informatie over de variëteit", + "PAGE_SETTINGS_MANAGE_ARCHIVE": "Archief beheren", + "LAST_USE": "Laatst gebruikt", + "SEARCH": "Zoeken", + "OVERVIEW": "Overzicht", + "BEAN_HEADER_ADDITIONALE_INFORMATION": "Aanvullende informatie", + "THREE_DEE_TOUCH_ACTION_BREW": "Brouw", + "THREE_DEE_TOUCH_ACTION_BEAN": "Boon", + "THREE_DEE_TOUCH_ACTION_PREPARATION": "Bereidingsmethode", + "THREE_DEE_TOUCH_ACTION_MILL": "Koffiemolen", + "PAGE_CREDITS_NOT_EXISTING": "Geen inhoud", + "TIMER_HOUR": "Uren", + "TIMER_MINUTES": "Minuten", + "TIMER_SECONDS": "Seconden", + "EXCEL": { + "BEAN": { + "CREATION_DATE": "Aanmaakdatum", + "ID": "Bonen-ID" + }, + "PREPARATION": { + "CREATION_DATE": "Aanmaakdatum", + "ID": "bereidingsmiddelen" + }, + "GRINDER": { + "CREATION_DATE": "Aanmaakdatum", + "ID": "Molen-ID" + } + }, + "EXCEL_EXPORT": "Excel-export", + "HEALTH_KIT_QUESTION_TITLE": "cafeïneconsumptie opslaan", + "HEALTH_KIT_QUESTION_MESSAGE": "Na activering wordt de geschatte cafeïne van elk brouwsel automatisch opgeslagen in Apple Health.", + "NAV_ROASTING_SECTION": "Bonen roosteren", + "ROASTING_SECTION": { + "NAV_GREEN_BEANS": "Groene bonen", + "NAV_ROASTING_MACHINE": "Roostermachine", + "ROASTING_MACHINE": { + "TOTAL_ROAST_QUANTITY": "Totaalgewicht geroosterd", + "TOTAL_ROAST_COUNT": "Aantal brandingen" + }, + "GREEN_BEAN": { + "ADD": "Toevoegen", + "EDIT": "Bewerking", + "DETAIL": "Groene bonen details", + "ROASTABLE": "Roosterbaar", + "NO_ROASTS_YET": "Nog geen gebrande bonen" + }, + "BEAN": { + "DROP_TEMPERATURE": "Eindtemperatuur van de gebrande boon", + "ROAST_LENGTH": "brand lengte", + "ROASTER_MACHINE": "Roostermachine", + "GREEN_BEAN_WEIGHT": "Gewicht van groene bonen", + "OUTSIDE_TEMPERATURE": "Omgevingstemperatuur", + "HUMIDITY": "Vochtigheid", + "FIRST_CRACK_MINUTE": "eerst kraak minuut", + "FIRST_CRACK_TEMPERATURE": "eerste kraak temperatuur", + "SECOND_CRACK_MINUTE": "Tweede kraak minuut", + "SECOND_CRACK_TEMPERATURE": "tweede kraak temperatuur", + "PLACE_HOLDER": { + "DROP_TEMPERATURE": "Eindtemperatuur van de gebrande boon", + "ROAST_LENGTH": "brand lengte", + "ROASTER_MACHINE": "Roostermachine", + "GREEN_BEAN_WEIGHT": "Gewicht van groene bonen", + "OUTSIDE_TEMPERATURE": "Omgevingstemperatuur", + "HUMIDITY": "Vochtigheid", + "FIRST_CRACK_MINUTE": "Eerste kraak minuut", + "FIRST_CRACK_TEMPERATURE": "Eerste kraak temperatuur", + "SECOND_CRACK_TEMPERATURE": "Tweede kraak temperatuur", + "SECOND_CRACK_MINUTE": "Tweede kraak minuut" + } + } + }, + "PAGE_SETTINGS_MANAGE_SECTIONS": "Meer secties", + "PAGE_SETTINGS_SHOW_ROASTING_SECTION": "Activeer brandersectie", + "PAGE_SETTINGS_SHOW_WATER_SECTION": "Activeer water sectie", + "PAGE_SETTINGS_SHOW_CUPPING_SECTION": "Activeer cupping-sectie", + "BEAN_DATA_BUY_DATE": "Aankoopdatum", + "BEAN_SORT_CREATION_DATE": "Aanmaakdatum", + "BEAN_SORT_PURCHASE_DATE": "Aankoopdatum", + "BEAN_ROAST_COUNT": "Roost aantal", + "TRANSFER_ROAST": "Geroosterd", + "BEAN_TAB_LINKED_ROASTS": "Geroosterd", + "BEAN_DATA_WEIGHT_AFTER_ROASTING": "Gewicht na het roosteren", + "TOAST_GREEN_BEAN_ADDED_SUCCESSFULLY": "Groene bonen toegevoegd", + "TOAST_GREEN_BEAN_EDITED_SUCCESSFULLY": "Groene boon bewerkt", + "TOAST_GREEN_BEAN_ARCHIVED_SUCCESSFULLY": "Groene bonen gearchiveerd", + "TOAST_ROASTING_MACHINE_ADDED_SUCCESSFULLY": "Roostermachine toegevoegd", + "TOAST_ROASTING_MACHINE_EDITED_SUCCESSFULLY": "Brandmachine bewerkt", + "TOAST_ROASTING_MACHINE_ARCHIVED_SUCCESSFULLY": "Brander gearchiveerd", + "DELETE_ROASTING_MACHINE_QUESTION": "Brander verwijderen? Alle gebrande bonen waarnaar verwezen wordt, worden bijgewerkt en niet verwijderd.", + "TOAST_ROASTING_MACHINE_DELETED_SUCCESSFULLY": "Brander verwijderd", + "EDIT_ROASTING_MACHINE": "Bewerk", + "DETAIL_ROASTING_MACHINE": "Details van de brander", + "DELETE_WATER_QUESTION": "Water verwijderen? Alle gerefereerde brouwsels worden bijgewerkt en niet verwijderd", + "ROASTING_MACHINE": { + "PLACE_HOLDER": { + "NAME": "Voeg een naam toe", + "NOTES": "Opmerkingen toevoegen voor deze brander" + } + }, + "NAV_ROASTING_MACHINE": "koffierooster machines", + "PAGE_ROASTING_MACHINE_LIST_NO_MACHINES_EXISTING": "U hebt geen koffiebrander toegevoegd", + "PAGE_ROASTING_MACHINE_LIST_NO_ARCHIVED_MACHINES_EXISTING": "je hebt geen koffiebrander gearchiveerd", + "CHOOSE_ROASTING_MACHINES": "koffiebrander", + "CHOOSE_ROASTING_MACHINE": "koffiebrander", + "POPOVER_BREWS_OPTION_TOGGLE_FAVOURITE": "Favoriet", + "TOAST_BREW_FAVOURITE_ADDED": "Favoriet toegevoegd", + "TOAST_BREW_FAVOURITE_REMOVED": "Favoriet verwijderd", + "BREW_FILTER_JUST_FAVOURITE": "Favorieten", + "STATISTICS_PREPARATION_USAGES": "Bereidingsmethode gebruik", + "STATISTICS_PREPARATION_TIMELINE_USAGES": "Gebruiksgeschiedenis bereidingsmethode", + "STATISTICS_GRINDER_TIMELINE_USAGES": "Gebruiksgeschiedenis van de Maler", + "ACCEPT": "Accepteer", + "STATISTIC_TAB_GENERAL": "Algemeen", + "STATISTIC_TAB_BREWS": "Brouwsels", + "STATISTIC_TAB_BEANS": "Bonen", + "STATISTIC_TAB_PREPARATIONS": "Voorbereidingen", + "STATISTIC_TAB_GRINDERS": "Malers", + "PAGE_STATISTICS_BREW_PER_DAYPROCESSES": "Brouwsels per dag", + "PAGE_STATISTICS_BREW_TIME": "Brouwtijd", + "PAGE_STATISTICS_PHOTOS_TAKEN": "Foto's gemaakt", + "PAGE_SETTINGS_IMAGE_QUALITY": "afbeelding kwaliteit", + "PAGE_SETTINGS_IMAGE_QUALITY_TOOLTIP": "Bepaal in welke kwaliteit uw afbeeldingen moeten worden opgeslagen. Dit kan uw dataverbruik verlagen.", + "PAGE_SETTINGS_BREW_RATING": "Brouw beoordeling", + "PAGE_SETTINGS_BREW_RATING_TOOLTIP": "Is de standaard '-1 tot 5' niet de juiste beoordeling voor u? U kunt in plaats daarvan een '-1 tot 100' schaal gebruiken", + "UPDATE_ENTRY_OF": "Update item {{index}} van {{count}}", + "WEBSITE": "Website", + "SHARE": "Deel", + "ANDROID_FILE_ACCESS_NEEDED_TITLE": "Toegang tot gedeeld bestand vereist", + "ANDROID_FILE_ACCESS_NEEDED_DESCRIPTION": "Om de app volledig te laten werken, vragen we u om bestandstoegang te autoriseren. Anders ontstaan er problemen bij het gebruik van de app. Dit is specifiek nodig voor het automatische back-upsysteem.", + "COULD_NOT_ACCESS_FILE": "We konden het gekozen bestand niet openen", + "WRONG_FILE_FORMAT": "U hebt een niet-ondersteund bestandsformaat gekozen", + "SCAN_BEAN": "Scan pakket", + "CLEAR": "leeg maken", + "BEAN_LOOKS_LIKE_CONSUMED": "Het lijkt erop dat deze bonen op zijn. Wil je ze archiveren?", + "CUPPING_1": "Fruit", + "CUPPING_2": "Citrus", + "CUPPING_3": "Citroen & limonade", + "CUPPING_4": "Limoen", + "CUPPING_5": "Grapefruit", + "CUPPING_6": "Clementine", + "CUPPING_7": "Mandarijn", + "CUPPING_8": "mandarijn sinaasappel", + "CUPPING_9": "sinasappel", + "CUPPING_10": "Appel\/peer", + "CUPPING_11": "Groene appel", + "CUPPING_12": "Rode appel", + "CUPPING_13": "Meloen", + "CUPPING_14": "Watermeloen", + "CUPPING_15": "Honingdauw meloen", + "CUPPING_16": "Cantaloupe meloen", + "CUPPING_17": "Druif", + "CUPPING_18": "Witte druif", + "CUPPING_19": "Groene druif", + "CUPPING_20": "Rode druif", + "CUPPING_21": "Concord-druif", + "CUPPING_22": "Tropisch fruit", + "CUPPING_23": "Litchi", + "CUPPING_24": "Stervrucht", + "CUPPING_25": "Tamarinde", + "CUPPING_26": "Passievrucht", + "CUPPING_27": "Ananas", + "CUPPING_28": "Mango", + "CUPPING_29": "Papaja", + "CUPPING_30": "Kiwi", + "CUPPING_31": "Banaan", + "CUPPING_32": "Kokosnoot", + "CUPPING_33": "Steenvrucht", + "CUPPING_34": "Perzik", + "CUPPING_35": "Nectarine", + "CUPPING_36": "Abrikoos", + "CUPPING_37": "Pruim", + "CUPPING_38": "Kers", + "CUPPING_39": "Zwarte kers", + "CUPPING_40": "Bes", + "CUPPING_41": "Cranberry", + "CUPPING_42": "Framboos", + "CUPPING_43": "Aardbei", + "CUPPING_44": "Bosbes", + "CUPPING_45": "Rode bes", + "CUPPING_46": "Zwarte bes", + "CUPPING_47": "Gedroogd fruit", + "CUPPING_48": "Gouden rozijn", + "CUPPING_49": "Rozijn", + "CUPPING_50": "Gedroogde vijg", + "CUPPING_51": "Gedroogde dadels", + "CUPPING_52": "Gedroogde Pruim", + "CUPPING_53": "Zoet & gebrand", + "CUPPING_54": "Chocolade", + "CUPPING_55": "Cacaonibs", + "CUPPING_56": "Donkere chocolade", + "CUPPING_57": "Bakkers chocolade", + "CUPPING_58": "Bitterzoete chocolade", + "CUPPING_59": "Cacaopoeder", + "CUPPING_60": "Melkchocolade", + "CUPPING_61": "Noot", + "CUPPING_62": "Walnoot", + "CUPPING_63": "Pinda", + "CUPPING_64": "Cashew", + "CUPPING_65": "Pecannoot", + "CUPPING_66": "Hazelnoot", + "CUPPING_67": "Amandel", + "CUPPING_68": "Graan & Graanproducten", + "CUPPING_69": "Zoet broodgebak", + "CUPPING_70": "Granola", + "CUPPING_71": "Graham-cracker", + "CUPPING_72": "Rogge", + "CUPPING_73": "Tarwe", + "CUPPING_74": "Gerst", + "CUPPING_75": "Vers brood", + "CUPPING_76": "Zoet & Suikerachtig", + "CUPPING_77": "Vanille", + "CUPPING_78": "Noga", + "CUPPING_79": "Honing", + "CUPPING_80": "Boter", + "CUPPING_81": "Room", + "CUPPING_82": "Marshmallow", + "CUPPING_83": "Rietsuiker", + "CUPPING_84": "Bruine suiker", + "CUPPING_85": "Karamel", + "CUPPING_86": "Ahornsiroop", + "CUPPING_87": "Melasse", + "CUPPING_88": "Cola", + "CUPPING_89": "Geroosterd", + "CUPPING_90": "Toast", + "CUPPING_91": "Verbrande suiker", + "CUPPING_92": "Rokerig", + "CUPPING_93": "Koolstof", + "CUPPING_94": "Plantaardig, hartig en kruidig", + "CUPPING_95": "Kruiden", + "CUPPING_96": "Zwarte peper", + "CUPPING_97": "Witte peper", + "CUPPING_98": "Kaneel", + "CUPPING_99": "Koriander", + "CUPPING_100": "Gember", + "CUPPING_101": "Nootmuskaat", + "CUPPING_102": "Kerrie", + "CUPPING_103": "Drop-anijs", + "CUPPING_104": "Kruidnagel", + "CUPPING_105": "Hartig", + "CUPPING_106": "Leerachtig", + "CUPPING_107": "Vleesachtig", + "CUPPING_108": "Sojasaus", + "CUPPING_109": "Zongedroogde tomaat", + "CUPPING_110": "Tomaat", + "CUPPING_111": "Plantaardig aards kruid", + "CUPPING_112": "Grond", + "CUPPING_113": "Vers hout", + "CUPPING_114": "Ceder", + "CUPPING_115": "Tabak", + "CUPPING_116": "Hooi \/ stro", + "CUPPING_117": "Bladgroenten", + "CUPPING_118": "Olijf", + "CUPPING_119": "Groene peper", + "CUPPING_120": "Pompoen", + "CUPPING_121": "Paddestoel", + "CUPPING_122": "Zoete erwt", + "CUPPING_123": "Sneeuwerwt", + "CUPPING_124": "Grassig", + "CUPPING_125": "Dille", + "CUPPING_126": "Salie", + "CUPPING_127": "Munt", + "CUPPING_128": "Groene thee", + "CUPPING_129": "Zwarte thee", + "CUPPING_130": "Hop", + "CUPPING_131": "Bergamot", + "CUPPING_132": "Bloemrijk", + "CUPPING_133": "Bloemen", + "CUPPING_134": "Hibiscus", + "CUPPING_135": "Rozenbottels", + "CUPPING_136": "Lavendel", + "CUPPING_137": "Magnolia", + "CUPPING_138": "Jasmijn kamperfoelie", + "CUPPING_139": "Oranjebloesem", + "CUPPING_140": "Citroengras", + "WATER_SECTION": { + "NAV_WATER": "Water", + "YOU_GOT_NO_ARCHIVED_WATER": "Je hebt nog geen water gearchiveerd", + "YOU_GOT_NO_WATER": "Je hebt nog geen water toegevoegd", + "CATEGORY_INFORMATION": "Water informatie", + "CATEGORY_GENERAL": "Algemeen", + "WATER_BOTTLE_EXPLANATION": "Waterflessen vermelden de concentratie meestal in eenheden van ppm = mg\/L", + "USED_TIMES": "Aantal keren gebruikt", + "AMOUNT": "Gebruikte hoeveelheid", + "WATER": { + "GENERAL_HARDNESS": "Algemene hardheid (GH)", + "TOTAL_ALKALINITY": "Totale alkaliteit (KH)", + "CALCIUM": "Kalium (Ca)", + "MAGNESIUM": "Magnesium (Mg)", + "SODIUM": "Natrium (Na)", + "TDS": "Totaal opgeloste vaste stoffen (TDS)", + "UNITS": "Eenheden", + "PLACE_HOLDER": { + "GENERAL_HARDNESS": "Algemene hardheid", + "TOTAL_ALKALINITY": "Totale alkaliteit", + "CALCIUM": "Kalium (Ca)", + "MAGNESIUM": "Magnesium (Mg)", + "SODIUM": "Natrium (Na)", + "TDS": "Totaal opgeloste vaste stoffen (TDS)", + "NAME": "Voeg waternaam toe", + "NOTES": "Voeg wat notities toe voor je water", + "POTASSIUM": "Kalium (K)", + "CHLORIDE": "Chloride (Cl)", + "SULFATE": "Sulfaat (SO4)" + }, + "WATER_UNIT": { + "UNKNOWN": "Onbekend", + "PPM": "ppm als CaCO3", + "MG_L": "mg\/L", + "MMOL_L": "mmol\/L", + "DH": "°dH" + }, + "POTASSIUM": "Kalium (K)", + "CHLORIDE": "Chloride (Cl)", + "SULFATE": "Sulfaat (SO4)" + } + }, + "BREW_BRIX_CALCULATION": "Graden Brix", + "SET_TDS": "TDS instellen", + "TOTAL_WEIGHT": "Totaal gewicht", + "CALCULATED_WEIGHT": "Berekend gewicht", + "SET_WEIGHT": "Gewicht instellen", + "ADD_FLAVORS_AROMAS_TITLE": "Aroma's \/ Smaken", + "CUSTOM_FLAVORS_AROMAS": "Individueel aroma", + "CUSTOM_FLAVORS_AROMAS_PLACEHOLDER": "Voeg je individuele aroma toe", + "PREDEFINED_FLAVORS_AROMAS": "Veel voorkomende aroma's", + "ADD_AROMA_FLAVOR": "Aroma's\/smaken toevoegen", + "BEAN_WEIGHT_IN_PLACEHOLDER": "Bonen uit de verpakking gehaald", + "VESSEL_PLACEHOLDER": "Naam van het Serveerkan en het leeggewicht ervan", + "GRIND_WEIGHT_PLACEHOLDER": "Gewicht van de gemalen bonen die voor het brouwen worden gebruikt", + "PRESET_BREW_TITLE": "Gebruik laatste brouwsel als voorinstelling", + "CUPPING_BREW_TAB_AROMA": "Aroma", + "CUPPING_BREW_TAB_TASTING": "Systematische cupping", + "WATER_PLACEHOLDER": "Activeer het watergedeelte in het instellingenmenu voor volledige functionaliteit", + "PAGE_SETTINGS_SCALES": "Weegschalen", + "CONNECT": "Verbinden", + "DISCONNECT": "Loskoppelen", + "SCALE": { + "BLUETOOTH_SCAN_RUNNING": "Zoeken naar weegschaal tot 60s", + "BLUETOOTH_NOT_ENABLED": "Bluetooth niet geactiveerd, activeer het om het goed te laten werken", + "CONNECTION_NOT_ESTABLISHED": "Weegschaal niet gevonden, of verbinding kon niet tot stand worden gebracht", + "CONNECTED_SUCCESSFULLY": "Weegschaal verbonden", + "DISCONNECTED_SUCCESSFULLY": "Weegschaal losgekoppeld", + "DISCONNECTED_UNPLANNED": "Weegschaal onverwachts losgekoppeld", + "REQUEST_PERMISSION": { + "LOCATION": "Om Bluetooth-weegschalen te vinden, heeft de app toegang nodig tot de locatie.", + "BLUETOOTH": "Om bluetooth-weegschalen te vinden, heeft de app toegang tot bluetooth nodig" + }, + "INFORMATION_DESCRIPTION": "Ondersteunde weegschalen zijn: Decent Scale, Acaia, Felicita, Hiroia Jimmy, Skale 2, DiFluid Microbalance, Smartchef Scale, Blackcoffee.io, Bookoo Mini Scale en Eureka Precisa. Let op: Als de Eureka Precisa een negatieve waarde ontvangt, stopt de timer" + }, + "QR": { + "WRONG_QRCODE_DESCRIPTION": "Ongeldige QR-code of onbekende inhoud", + "WRONG_QRCODE_TITLE": "Fout", + "WRONG_LINK_DESCRIPTION": "Ongeldige QR-link", + "WRONG_LINK_TITLE": "Fout", + "SERVER": { + "ERROR_OCCURED": "Er is een fout opgetreden. De QR-code kon niet worden gelezen. Probeer het opnieuw.", + "BEAN_NOT_APPROVED": "Boon is nog niet goedgekeurd, probeer het later nog eens" + }, + "BEAN_SUCCESSFULLY_SCANNED": "Bean succesvol gescand", + "BEAN_SUCCESSFULLY_REFRESHED": "Boon succesvol bijgewerkt", + "IMAGES_GETTING_DOWNLOADED": "Afbeeldingen downloaden" + }, + "RETRY_CONNECT": "Verbinding opnieuw proberen", + "SMART_SCALE_STAY_CONNECTED_ON_APP_MINIMIZE": "Houd de weegschaal verbonden, zelfs als de app op de achtergrond draait", + "BREW_FLOW_WEIGHT": "Gewicht", + "BREW_FLOW_WEIGHT_PER_SECOND": "Stroom (afgevlakt)", + "ROAST_TYPE_UNKNOWN": "onbekend", + "ROAST_TYPE_CINNAMON_ROAST": "Cinnamon Branding", + "ROAST_TYPE_AMERICAN_ROAST": "Amerikaans Branding", + "ROAST_TYPE_NEW_ENGLAND_ROAST": "Nieuw-Engeland Branding", + "ROAST_TYPE_HALF_CITY_ROAST": "Half City Branding", + "ROAST_TYPE_MODERATE_LIGHT_ROAST": "Matig-lichte branding", + "ROAST_TYPE_CITY_ROAST": "City Branding", + "ROAST_TYPE_CITY_PLUS_ROAST": "City+ Branding", + "ROAST_TYPE_FULL_CITY_ROAST": "Full City Branding", + "ROAST_TYPE_FULL_CITY_PLUS_ROAST": "Full City + Branding", + "ROAST_TYPE_ITALIAN_ROAST": "Ialiaanse Branding", + "ROAST_TYPE_VIEANNA_ROAST": "Weense Branding", + "ROAST_TYPE_FRENCH_ROAST": "Franse Branding", + "ROAST_TYPE_CUSTOM_ROAST": "Aangepast", + "BEAN_MIX_UNKNOWN": "onbekend", + "BEAN_MIX_SINGLE_ORIGIN": "Single Origin", + "BEAN_MIX_BLEND": "Melange", + "BEAN_ROASTING_TYPE_FILTER": "Filter", + "BEAN_ROASTING_TYPE_ESPRESSO": "Espresso", + "BEAN_ROASTING_TYPE_OMNI": "Omni", + "BEAN_ROASTING_TYPE_UNKNOWN": "Onbekend", + "SMART_SCALE_LOG": "Activeer logbestanden voor slimme weegschaal (alleen voor foutopsporing)", + "TOAST_PREPARATION_TOOL_EDITED_SUCCESSFULLY": "Voorbereidingstool bewerkt", + "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE": "Bluetooth-weegschaal", + "PAGE_SETTINGS_TAB_GENERAL": "Algemeen", + "SMART_SCALE_TARE_ON_BREW": "Tarraweegschaal op nieuw brouwsel", + "SMART_SCALE_TARE_ON_START_TIMER": "Tarra weegschaal bij het starten van de timer", + "PAGE_SETTINGS_BREW_RATING_STEPS": "Beoordelingsstappen", + "BREW_AVG_FLOW_WEIGHT_PER_SECOND": "Ø Stroom", + "CUSTOM_LIST_VIEW_PARAMETERS": "Parameters voor lijstweergave", + "NAV_LIST_VIEW_CUSTOM_PARAMETERS": "Parameters voor lijstweergave", + "PAGE_LIST_VIEW_CUSTOM_PARAMETERS_DESCRIPTION": "Bepaal welke parameters moeten worden weergegeven op de tegels van de lijstweergave", + "BREW_DATA_VESSEL_NAME_WEIGHT": "Serveerkan Naam\/Gewicht", + "IGNORE_NEGATIVE_VALUES": "Negeer negatieve gewichtswaarden", + "IGNORE_ANOMALY_VALUES": "Negeer afwijkende waarden", + "IGNORE_ANOMALY_VALUES_TOOLTIP": "Bijvoorbeeld: Een kopje rond bewegen op de weegschaal", + "TOAST_BEAN_FAVOURITE_ADDED": "Favoriet toegevoegd", + "TOAST_BEAN_FAVOURITE_REMOVED": "Favoriet verwijderd", + "QR_CODE_SCANNER_INFORMATION_TITLE": "QR-code", + "QR_CODE_SCANNER_INFORMATION_DESCRIPTION": "Alle gescande boneninformatie komt rechtstreeks van de branderij. Mocht u onjuiste of misleidende informatie aantreffen, laat het mij dan weten via e-mail: info@beanconqueror.com.", + "DONT_SHOW_AGAIN": "Niet meer weergeven", + "ARCHIVED_TOOLS": "Gearchiveerd gereedschap", + "UNARCHIVE": "Herstel", + "PAGE_SETTINGS_HIDE_ARCHIVED_BREWS_DASHBOARD": "Toon gearchiveerde brouwsels op dashboard", + "PAGE_SETTINGS_HIDE_ARCHIVED_BREWS_DASHBOARD_DESCRIPTION": "Moeten gearchiveerde brouwsels op de startpagina worden weergegeven?", + "COPY": "Kopiëren", + "TOAST_PREPARATION_METHOD_REPEATED_SUCCESSFULLY": "Bereidingsmethode succesvol gekopieerd", + "PREPARATION_TYPE_CAFEC_FLOWER": "Cafec Flower", + "PREPARATION_TYPE_DECEMBER_DRIPPER": "December Dripper", + "PREPARATION_TYPE_DECENT_ESPRESSO": "Decent Espresso", + "PREPARATION_TYPE_HARIO_SWITCH": "Hario Switch", + "PREPARATION_TYPE_HARIO_WOODNECK": "Hario Woodneck", + "PREPARATION_TYPE_RATIO_SIX_COFFEE_BREWER": "Ratio Six-koffiezetapparaat", + "PREPARATION_TYPE_ROK": "ROK", + "PREPARATION_TYPE_TORNADO_DUO": "Tornado-duo", + "PREPARATION_TYPE_TRICOLATE": "Tricolate", + "QR_CODE_REFRESH_DATA_MESSAGE": "Alle informatie voor deze boon wordt overschreven, doorgaan?", + "POPOVER_QR_CODE_REFRESH": "Gegevens opnieuw laden", + "BREW_FLOW_WEIGHT_REALTIME": "Stroom (realtime)", + "SMART_SCALE_STOP_TIMER_ON_BREW": "Stop de timer van de weegschaal bij een nieuw brouwsel", + "SMART_SCALE_RESET_TIMER_ON_BREW": "Reset de weegschaaltimer bij een nieuw brouwsel", + "BREW_PRESSURE_FLOW": "Druk", + "BREW_TEMPERATURE_REALTIME": "Temperatuur", + "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_SHOW_GRAPHS_FILTER": "Grafieken weergeven voor filter", + "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_SHOW_GRAPHS_ESPRESSO": "Toon grafieken voor espresso", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE": "Drukapparaat", + "PAGE_SETTINGS_TAB_BLUETOOTH_TEMPERATURE": "Temperatuur apparaat", + "PRESSURE_LOG": "Activeer logbestanden voor drukapparaat", + "PRESSURE_THRESHOLD_ACTIVE": "Starttimer met vooraf gedefinieerde druk", + "PRESSURE_THRESHOLD_BAR": "Drempelwaarde druk", + "TIMER_MILLISECONDS": "MS", + "PAGE_SETTINGS_BREW_ENABLE_MILLISECONDS": "Milliseconden?", + "PAGE_SETTINGS_BREW_ENABLE_MILLISECONDS_DESCRIPTION": "Gebruik milliseconden om uw brouwsels nog nauwkeuriger te analyseren", + "PAGE_SETTINGS_BEAN_RATING": "Bonen beoordeling", + "PAGE_SETTINGS_BEAN_RATING_TOOLTIP": "Is de standaard '0 tot 5' niet de juiste beoordeling voor u? U kunt in plaats daarvan een '0 tot 100'-schaal gebruiken", + "PAGE_SETTINGS_BEAN_RATING_STEPS": "Stappen voor het beoordelen van bonen", + "COFFEE_GRAMS_BREWED": "grammen gebrouwen", + "SMART_SCALE_MAXIMIZE_ON_START_TIMER": "Maximaliseer realtime-grafiek bij het starten van de timer", + "PRESSURE": { + "CONNECTION_NOT_ESTABLISHED": "Drukapparaat niet gevonden, of verbinding kon niet tot stand worden gebracht", + "CONNECTED_SUCCESSFULLY": "Drukapparaat aangesloten", + "DISCONNECTED_SUCCESSFULLY": "Drukapparaat losgekoppeld", + "DISCONNECTED_UNPLANNED": "Drukapparaat onverwachts losgekoppeld", + "STAY_CONNECTED_ON_APP_MINIMIZE": "Houd het drukapparaat aangesloten, zelfs als de app op de achtergrond draait", + "INFORMATION_DESCRIPTION": "Ondersteunde apparaten zijn: Popsicle, Pressensor, Smart Espresso Profiler, Bookoo Espresso Monitor", + "BLUETOOTH_SCAN_RUNNING": "Zoeken naar drukapparaat gedurende maximaal 60 seconden", + "BLUETOOTH_NOT_ENABLED": "Bluetooth niet geactiveerd, activeer het om het goed te laten werken", + "REQUEST_PERMISSION": { + "LOCATION": "Om drukapparaten te vinden, heeft de app toegang nodig tot de locatie.", + "BLUETOOTH": "Om drukapparaten te vinden, heeft de app toegang tot Bluetooth nodig" + } + }, + "POPOVER_BLUETOOTH_ACTION_RECONNECT_SCALE": "Weegschaal opnieuw verbinden", + "POPOVER_BLUETOOTH_ACTION_RECONNECT_PRESSURE_DEVICE": "Drukapparaat opnieuw aansluiten", + "POPOVER_SHOW_BREWS": "Toon brouwsels", + "LAST_USED_GRIND_SIZE_SETTING": "Laatste maalstand", + "LAST_USED_BEAN": "Laatste boon", + "PAGE_SETTINGS_BREW_MILLISECONDS_DECIMAL_PLACES_DESCRIPTION": "Hoeveel decimalen moeten er worden weergegeven?", + "SMART_SCALE_COMMAND_DELAY": "Opdracht vertraging", + "SMART_SCALE_COMMAND_DELAY_TOOLTIP": "Hoeveel tijd moet er verstrijken tussen elk bluetooth-commando?", + "SUPPORT_ME": "Donatie", + "PAGE_SETTINGS_BREW_DISPLAY_BEAN_IMAGE": "Bonenafbeeldingen op brouwsels weergeven?", + "PAGE_SETTINGS_BREW_DISPLAY_BEAN_IMAGE_DESCRIPTION": "Als er een bonenafbeelding bestaat, wordt deze gebruikt in plaats van de afbeelding van de bereidingsmethode", + "DOWNLOAD_XLSX": "Excel downloaden", + "DOWNLOAD_JSON": "JSON downloaden", + "NAV_BEAN_PARAMS": "Boon Parameters", + "BEAN_DATA_ROAST_NAME_TYPE": "Brand graad", + "ENABLE_BEAN_SORT_INFORMATION": "Variëteit-informatie inschakelen", + "BEAN_SORT_MIX": "Bonenmix", + "PAGE_MANAGE_BEAN_PARAMETERS_DESCRIPTION": "Markeer welke parameters moeten worden weergegeven bij het bewerken van bean-informatie.", + "PAGE_BEAN_LIST_VIEW_CUSTOM_PARAMETERS_DESCRIPTION": "Bepaal de parameters die moeten worden weergegeven in lijstweergaven voor bonen", + "BEAN_DATA_NAME_TOOLTIP": "De naam van de bonen", + "BEAN_DATA_ROASTER_TOOLTIP": "Wie is de brander", + "BEAN_DATA_BUY_DATE_TOOLTIP": "Wanneer heb je de bonen gekocht?", + "BEAN_DATA_ROASTING_DATE_TOOLTIP": "Wat is de branddatum van de bonen?", + "BEAN_DATA_ROASTING_TYPE_TOOLTIP": "Voor welke bereidingswijze is deze boon geroosterd, bijvoorbeeld Filter", + "BEAN_DATA_ROAST_NAME_TOOLTIP": "Wat is de brandingsgraad van de bonen?", + "BEAN_DATA_ROAST_NAME_TYPE_TOOLTIP": "Welk type branding (Cinnamon, New England, etc.)", + "BREW_DATA_RATING_TOOLTIP": "Je waardering", + "BEAN_SORT_MIX_TOOLTIP": "Zijn de bonen een melange?", + "BEAN_DATA_WEIGHT_TOOLTIP": "Hoeveel wegen de bonen?", + "BEAN_DATA_COST_TOOLTIP": "Hoeveel hebben de bonen gekost?", + "BEAN_DATA_AROMATICS_TOOLTIP": "Welke aroma's\/smaken hebben de bonen?", + "BEAN_DATA_CUPPING_POINTS_TOOLTIP": "Hoeveel cupping-punten heeft de boon?", + "BEAN_DATA_DECAFFEINATED_TOOLTIP": "Is het cafeïnevrij?", + "BEAN_DATA_URL_TOOLTIP": "URL naar de winkel", + "BEAN_DATA_EAN_TOOLTIP": "Het EAN\/artikelnummer", + "NOTES_TOOLTIP": "Aantekeningen gemaakt", + "BREW_DATA_ATTACHMENTS_TOOLTIP": "Foto's toevoegen?", + "ENABLE_BEAN_SORT_INFORMATION_TOOLTIP": "Meer informatie over de bonen: waar komen ze vandaan, wanneer zijn ze geoogst, etc.", + "BEAN_DATA_COUNTRY_TOOLTIP": "Uit welk land komen de bonen?", + "BEAN_DATA_REGION_TOOLTIP": "Uit welke regio komen de bonen?", + "BEAN_DATA_FARM_TOOLTIP": "Van welke boerderij komen de bonen?", + "BEAN_DATA_FARMER_TOOLTIP": "Wie verbouwde deze bonen", + "BEAN_DATA_ELEVATION_TOOLTIP": "Op welke hoogte groeiden de bonen?", + "BEAN_DATA_PROCESSING_TOOLTIP": "Hoe zijn de bonen verwerkt (natuurlijk, gewassen, enz.)", + "BEAN_DATA_VARIETY_TOOLTIP": "De Koffieboon Variëteit (SL28 bv.)", + "BEAN_DATA_HARVEST_TIME_TOOLTIP": "In welk jaar\/maand werden deze bonen geoogst?", + "BEAN_DATA_PERCENTAGE_TOOLTIP": "Hoeveel procent van deze bonen zit er in de zak?", + "BEAN_DATA_CERTIFICATION_TOOLTIP": "Welke certificeringen hebben de bonen (bijvoorbeeld fairtrade)", + "BEAN_DATA_PURCHASING_PRICE_TOOLTIP": "Wat was de aankoopprijs van de koffiebrander", + "BEAN_DATA_FOB_PRICE_TOOLTIP": "Wat was de Free-On-Board (FOB) prijs toen de brander het kocht?", + "BEAN_PARAMETER_CUSTOMIZE_TITLE": "Pas de informatie aan die u voor bonen wilt gebruiken", + "BEAN_PARAMETER_CUSTOMIZE_DESCRIPTION": "Er kan veel informatie over de bonen worden ingevoerd of gebruikt. Kies zelf de parameters die u wilt invullen en welke u wilt weergeven", + "BREW_CANT_START_BECAUSE_TIMER_NOT_RESETTED_TITLE": "Timer resetten!", + "BREW_CANT_START_BECAUSE_TIMER_NOT_RESETTED_DESCRIPTION": "Omdat je een Bluetooth-apparaat hebt aangesloten, moet je eerst je timer resetten voordat je kunt beginnen.", + "BREW_FILTER_JUST_CHART_DATA": "Alleen grafieken", + "SCALE_RESET_TRIGGERED_DESCRIPTION": "De bluetooth weegschaal wil zowel de app-timer als de brouwgrafiek resetten, wil je doorgaan?", + "SCALE_RESET_TRIGGERED_TITLE": "Opnieuw instellen?", + "NAV_REPEAT_PARAMETERS": "Herhalingsparameters definiëren", + "PAGE_REPEAT_PARAMETERS_DESCRIPTION": "Markeer welke parameters vooraf moeten worden ingesteld wanneer u een specifieke brouwbeurt herhaalt", + "CUSTOM_REPEAT_PARAMETERS": "Herhalen", + "CUSTOM_REPEAT_PARAMETERS_DESCRIPTION": "Aangepaste herhaling activeren?", + "PAGE_SETTINGS_USE_NUERMIC_KEYBOARD_FOR_GRIND_SIZE": "Numeriek toetsenbord voor maalinstelling?", + "PAGE_SETTINGS_USE_NUERMIC_KEYBOARD_FOR_GRIND_SIZE_DESCRIPTION": "Wilt u een numeriek toetsenbord gebruiken voor de maalinstelling, in plaats van het hele toetsenbord?", + "PREPARATION_DEVICE": { + "TYPE": { + "NONE": "Geen", + "XENIA": "Xenia", + "METICULOUS": "Meticulous" + }, + "URL": "URL-adres", + "CHOOSE_DEVICE": "Kies apparaat", + "CONNECTION": { + "UNSUCCESFULLY": "Er kon geen verbinding met de machine worden gemaakt", + "SUCCESFULLY": "Verbinding met machine was succesvol" + }, + "TYPE_XENIA": { + "TITLE": "Xenia machine", + "PRESS_START_SCRIPT": "Start script bij start", + "FIRST_DRIP_SCRIPT": "Start script bij eerste druppel", + "SCRIPT_AT_WEIGHT": "Gewicht voor scriptuitvoering", + "SCRIPT_LIST_GENERAL_0": "Niets", + "SCRIPT_LIST_GENERAL_1": "Espresso, 25 seconden", + "SCRIPT_LIST_GENERAL_2": "Espresso, eindeloos", + "SCRIPT_LIST_GENERAL_STOP": "Schot stoppen", + "CHOOSE_SCRIPT_AT_WEIGHT": "Kies script voor gewicht bereikt", + "ERROR_NOT_ALL_SCRIPTS_FOUND": "Een of meer scripts konden niet worden gevonden, ze zijn gereset", + "ERROR_CONNECTION_COULD_NOT_BE_ESTABLISHED": "De verbinding met de xenia espressomachine kon niet tot stand worden gebracht. Controleer of u zich op het juiste netwerk (LAN) bevindt, of u het juiste IP-adres hebt ingevoerd, etc.", + "CHECKING_CONNECTION_TO_PORTAFILTER": "Verbinding met Xenia wordt gecontroleerd", + "GRABING_SCRIPTS": "Scripts laden vanuit Xenia", + "BREW_BY_WEIGHT_ACTIVE": "Brouwen op gewicht actief" + }, + "API_VERSION": "Api-versie", + "RESIDUAL_LAG_TIME": "Resterende vertragingstijd", + "RESIDUAL_LAG_TIME_DESCRIPTION": "Stel de tijd in die overeenkomt met het resterende water. Met een naakte portafilter heb je een lagere tijd nodig, met een tuit een hogere. Hoe langzamer uw weegschaal het gewicht rapporteert, hoe meer tijd u nodig heeft", + "TYPE_METICULOUS": { + "TITLE": "Meticulous machine", + "NO_PROFILE": "Geen profiel", + "CHOOSE_PROFILE": "Profiel kiezen", + "SHOT_STARTED": "Brouwen gestart", + "SHOT_ENDED": "Brouwen gestopt" + } + }, + "DEVICE_CONNECTION": "Apparaatverbinding", + "PREPARATION_DEVICE_CONNECTION": "Apparaatverbinding", + "MANUAL_EXPORT_TO_VISUALIZER": "Exporteren naar Visualizer", + "ONLY_FAVOURITES": "Alleen favorieten", + "TEMPERATURE": { + "CONNECTION_NOT_ESTABLISHED": "Temperatuurapparaat niet gevonden, of verbinding kon niet tot stand worden gebracht", + "CONNECTED_SUCCESSFULLY": "Temperatuurapparaat aangesloten", + "DISCONNECTED_SUCCESSFULLY": "Temperatuurapparaat losgekoppeld", + "DISCONNECTED_UNPLANNED": "Temperatuurapparaat onverwachts losgekoppeld", + "STAY_CONNECTED_ON_APP_MINIMIZE": "Houd de thermometer verbonden, zelfs als de app op de achtergrond staat", + "INFORMATION_DESCRIPTION": "Ondersteunde apparaten zijn: ETI Ltd BLE-thermometers (ThermaQ Blue, BlueTherm, enz.), Combustion Inc., Meater (niet Meater+ of Meater 2)", + "BLUETOOTH_SCAN_RUNNING": "Zoeken naar temperatuurapparaat gedurende maximaal 60 seconden", + "BLUETOOTH_NOT_ENABLED": "Bluetooth niet geactiveerd, activeer het om het goed te laten werken", + "REQUEST_PERMISSION": { + "LOCATION": "Om temperatuurmeters te vinden, heeft de app toegang nodig tot de locatie.", + "BLUETOOTH": "Om temperatuurapparaten te vinden, heeft de app toegang tot Bluetooth nodig" + }, + "LOG": "Logbestanden voor temperatuurapparaat activeren", + "THRESHOLD_ACTIVE": "Start timer met vooraf ingestelde temperatuur", + "THRESHOLD_TEMP": "Drempel temperatuur" + }, + "POPOVER_BLUETOOTH_ACTION_RECONNECT_TEMPERATURE_DEVICE": "Temperatuurapparaat opnieuw aansluiten", + "PRESSURE_DEVICE_JUST_VISIBLE_ON_ESPRESSO": "Drukapparaat is alleen bruikbaar bij de bereidingswijze 'Espresso'", + "PRESSURE_MESSAGE_AFTER_CONNECTION": "Bekend gedrag (in analyse): Houd er rekening mee dat het langer kan duren om verbinding te maken nadat u uw telefoon opnieuw hebt opgestart of wanneer u de app langere tijd niet hebt gebruikt. Na deze tijd zou het verbindingsprobleem opgelost moeten zijn.", + "SMART_SCALE_ACAIA_HEARTBEAT_TIMER": "Hartslagtimer - Speciaal voor Acaia Scales", + "SMART_SCALE_ACAIA_HEARTBEAT_TIMER_TOOLTIP": "Alleen gebruikt voor Acaia-weegschalen! - Oudere Acaia-weegschalen hebben een hartslagsignaal nodig. Als u problemen ondervindt, probeer dan de hartslag op een hogere frequentie in te stellen.", + "SHARE_BEAN_URL": "Delen als URL", + "SHARE_BEAN_IMAGE": "Delen als afbeelding", + "SMART_SCALE_ESPRESSO_STOP_ON_NO_WEIGHT_CHANGE": "Espresso - Automatisch stoppen met brouwen", + "SMART_SCALE_ESPRESSO_STOP_ON_NO_WEIGHT_CHANGE_DESCRIPTION": "Deze instelling wordt alleen gebruikt voor brouwsels van het type 'espresso'. Het brouwsel wordt automatisch gestopt wanneer er geen flow wordt gedetecteerd.", + "BREW_DATA_BREW_QUANTITY_TOOLTIP_BREW_RATIO": "De zetverhouding wordt berekend met deze waarde voor alle brouwsels, maar niet voor type 'espresso'.", + "BREW_DATA_BREW_BEVERAGE_QUANTITY_TOOLTIP_BREW_RATIO": "De zetverhouding wordt alleen met deze waarde berekend voor brouwsels van het type 'espresso'", + "SMART_SCALE_DID_NOT_SEND_ANY_WEIGHT_DESCRIPTION": "Het lijkt erop dat de bluetooth weegschaal niet goed is aangesloten. Dit gebeurt meestal op iOS-apparaten met Acaia weegschalen, sluit de weegschaal opnieuw aan en zorg ervoor dat de gewichtstegel wordt bijgewerkt.", + "SMART_SCALE_DID_NOT_SEND_ANY_WEIGHT_TITLE": "Geen gewichtswaarden?", + "SMART_SCALE_ESPRESSO_STOP_ON_NO_WEIGHT_CHANGE_MIN_FLOW_DESCRIPTION": "De stroomsnelheid waarbij het zetten moet worden gestopt. Het zetten wordt pas gestopt als er minimaal 5 seconden zijn verstreken, er 5 gram in de kop zit of als u het gewicht van de gemalen koffie hebt ingevoerd - er is minimaal een zetverhouding van 1:1 bereikt (bijv.: 18 g erin, 18 g eruit). Nadat aan deze voorwaarden is voldaan, wordt de hier ingestelde minimale stroomsnelheid gecontroleerd", + "ONLY_BEST_BREWS": "Alleen de beste brouwsels", + "POPOVER_BEST_BREW": "Beste brouwsel", + "PAGE_SETTINGS_BEST_BREW": "Beste brouwsels activeren", + "PAGE_SETTINGS_BEST_BREW_DESCRIPTION": "Markeer je beste brouwsel voor een specifieke boon. Elke boon kan één beste brouwsel hebben in plaats van meerdere favorieten.", + "PAGE_SETTINGS_TAB_BLUETOOTH_REFRACTOMETER": "Refractometer apparaat", + "REFRACTOMETER": { + "CONNECTION_NOT_ESTABLISHED": "Refractometerapparaat niet gevonden, of verbinding kon niet tot stand worden gebracht", + "CONNECTED_SUCCESSFULLY": "Refractometerapparaat aangesloten", + "DISCONNECTED_SUCCESSFULLY": "Refractometerapparaat losgekoppeld", + "DISCONNECTED_UNPLANNED": "Refractometerapparaat onverwachts losgekoppeld", + "STAY_CONNECTED_ON_APP_MINIMIZE": "Houd de refractometer verbonden, zelfs als de app op de achtergrond draait", + "INFORMATION_DESCRIPTION": "Ondersteunde apparaten zijn: DiFluid R2", + "BLUETOOTH_SCAN_RUNNING": "Zoeken naar refractometer-apparaat gedurende maximaal 60 seconden", + "BLUETOOTH_NOT_ENABLED": "Bluetooth niet geactiveerd, activeer het om het goed te laten werken", + "REQUEST_PERMISSION": { + "LOCATION": "Om refractometerapparaten te vinden, heeft de app toegang nodig tot de locatie.", + "BLUETOOTH": "Om refractometerapparaten te vinden, heeft de app toegang tot Bluetooth nodig" + }, + "LOG": "Activeer logbestanden voor refractometerapparaat", + "READ_END": "Test voltooid - resultaat ontvangen" + }, + "COPIED_TO_CLIPBOARD_SUCCESSFULLY": "Toegevoegd aan klembord", + "COPIED_TO_CLIPBOARD_UNSUCCESSFULLY": "Kan niet worden toegevoegd aan het klembord", + "PAGE_SETTINGS_LANGUAGE_FRENCH": "Frans", + "ANDROID_EXTERNAL_FILE_ACCESS_NOT_POSSIBLE_TITLE": "Gegevens kunnen niet worden opgeslagen vanwege Android-beperkingen", + "ANDROID_EXTERNAL_FILE_ACCESS_NEEDED_DESCRIPTION": "Je Android-telefoon ondersteunt geen externe bestandssystemen, dus je moet het ZIP-bestand downloaden zonder mediabestanden. Ga voor meer informatie naar https:\/\/beanconqueror.com\/faq.", + "PAGE_SETTINGS_SECURITY_CHECK_WHEN_GOING_BACK": "Beveiligingsbericht voor afsluiten?", + "PAGE_SETTINGS_SECURITY_CHECK_WHEN_GOING_BACK_DESCRIPTION": "Controleer of de gegevens zijn gewijzigd bij het toevoegen\/bewerken van bonen of brouwsels; als dat het geval is, wordt er een veiligheidswaarschuwing weergegeven bij het teruggaan.", + "PAGE_BEANS_DISCARD_CONFIRM": "Boon informatie is gewijzigd. Weet je zeker dat je de pagina wilt verlaten zonder op te slaan?", + "PAGE_BREW_DISCARD_CONFIRM": "De brouw informatie is gewijzigd. Weet je zeker dat je de pagina wilt verlaten zonder op te slaan?", + "NO_ENTRIES_FOUND": "Geen vermeldingen gevonden", + "POPOVER_BEANS_OPTION_REPEAT": "Herhaal laatste brouwsel", + "REPEAT_LAST_BREW": "Herhaal laatste brouwsel", + "REPEAT_BEST_BREW": "Herhaal beste brouwsel", + "PAGE_SETTINGS_VISUALIZER_SECTION": "Visualizer", + "VISUALIZER": { + "ACTIVATE": "Visualizer activeren", + "CHOOSE_SERVER": "Server kiezen", + "CONNECTION": { + "UNSUCCESSFULLY": "Er kon geen verbinding tot stand worden gebracht.", + "SUCCESSFULLY": "Verbinding kon tot stand worden gebracht." + }, + "SERVER": { + "VISUALIZER": "Visualizer-server", + "CUSTOM": "Aangepaste server" + }, + "SHOT": { + "UPLOAD_SUCCESSFULLY": "Brouwen geüpload naar visualizer.", + "UPLOAD_UNSUCCESSFULLY": "Brouwen kon niet worden geüpload naar de Visualizer." + }, + "URL": "Server-URL", + "USERNAME": "Gebruikersnaam", + "PASSWORD": "Wachtwoord", + "UPLOAD_AUTOMATIC": "Elke brouwsessie automatisch uploaden?", + "UPLOAD_AUTOMATIC_TOOLTIP": "Zorg ervoor dat u een actieve internetverbinding hebt wanneer u uw brouwsel opslaat.", + "UPLOAD_ALL": "Alle brouwsels uploaden", + "NOT_ALL_SHOTS_UPLOADED": "Niet alle brouwsels konden worden geüpload", + "ALL_SHOTS_UPLOADED": "Alle brouwsels zijn geüpload" + }, + "SMART_SCALE_AUTO_START_LISTENING": "Timer automatisch starten?", + "SMART_SCALE_AUTO_START_LISTENING_DESCRIPTION": "Start de timer automatisch bij het bereiken van de volgende gewichtswaarde", + "CHOOSE_REFERENCE_GRAPH": "Referentiegrafiek selecteren", + "RESET": "Opnieuw instellen", + "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_GRAPHS_AXIS": "Definieer de beginassen van de grafiek", + "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_GRAPHS_AXIS_DESCRIPTION": "Stel de begingrootte van de assen in voor zowel filter- als espressokoffie.", + "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_GRAPHS_FILTER_WEIGHT": "Filter - Gewicht", + "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_GRAPHS_FILTER_FLOW": "Filter - Doorstroming", + "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_GRAPHS_ESPRESSO_WEIGHT": "Espresso - Gewicht", + "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_GRAPHS_ESPRESSO_FLOW": "Espresso - Doorstroming", + "SMART_SCALE_IGNORE_INCOMING_WEIGHT": "Negeer huidige gewichtsoverdracht ", + "SMART_SCALE_IGNORE_INCOMING_WEIGHT_TOOLTIP": "Er verschijnt een nieuwe knop in het brouwgedeelte, die nieuwe gewichtswaarden van de bluetooth weegschaal zal negeren.", + "BREWS_ACTIVE": "Actieve brouwsels", + "BREWS_ARCHIVED": "Gearchiveerde brouwsels", + "GRAPHS": "Grafieken", + "GRAPH_SECTION": { + "NAV_GRAPH": "Grafieken", + "NO_ARCHIVED_ENTRIES": "Geen gearchiveerde vermeldingen", + "NO_ENTRIES": "Geen vermeldingen", + "SECTION_HAS_BEEN_ACTIVATED": "Grafieksectie is geactiveerd" + }, + "TOAST_GRAPH_ARCHIVED_SUCCESSFULLY": "Grafiek gearchiveerd", + "TOAST_GRAPH_DELETED_SUCCESSFULLY": "Grafiek verwijderd", + "TOAST_GRAPH_EDITED_SUCCESSFULLY": "Grafiek bewerkt", + "TOAST_GRAPH_ADD_SUCCESSFULLY": "Grafiek toegevoegd", + "NAV_GRAPH_SECTION": "Grafieken", + "DELETE_GRAPH_QUESTION": "Wilt u deze grafiek verwijderen?", + "PAGE_SETTINGS_SHOW_ARCHIVED_GRAPHS": "Gearchiveerde grafieken weergeven", + "PAGE_SETTINGS_SHOW_GRAPH_SECTION": "Grafieksectie activeren", + "EDIT_GRAPH": "Grafiek bewerken", + "ADD_GRAPH": "Grafiek toevoegen", + "GRAPH": { + "PLACE_HOLDER": { + "NAME": "Grafiek naam", + "NOTES": "Notities" + }, + "UPLOAD": "Grafiek uploaden", + "DELETE": "Grafiek verwijderen", + "UPLOAD_DESCRIPTION": "Importeer een .JSON-bestand dat u kunt downloaden in de brew-detailweergave. U kunt ook gedeelde grafieken van de community importeren die van het type .JSON zijn." + }, + "SHOW_VISUALIZER": "Visualiseerder weergeven", + "NO_BREWS_FOUND": "Geen brouwsels gevonden", + "NO_GRAPHS_FOUND": "Geen grafieken gevonden", + "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_GRAPHS_TIME_AXIS": "Definieer de maximale tijdsas van de grafiek", + "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_GRAPHS_TIME_DESCRIPTION": "Stel het maximum aantal seconden in op de tijd-as voor zowel filterkoffie als espresso.", + "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_GRAPHS_TIME_FILTER_AXIS_NORMAL_SCREEN": "Filter - Normaal scherm", + "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_GRAPHS_TIME_FILTER_AXIS_FULL_SCREEN_SCREEN": "Filter - Volledig scherm", + "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_GRAPHS_TIME_ESPRESSO_AXIS_NORMAL_SCREEN": "Espresso - Normaal scherm", + "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_GRAPHS_TIME_ESPRESSO_AXIS_FULL_SCREEN_SCREEN": "Espresso - Volledig scherm", + "PAGE_SETTINGS_DATE_FORMAT": "Datum notatie", + "PAGE_SETTINGS_LANGUAGE_FRANCE": "Frans", + "PAGE_SETTINGS_LANGUAGE_INDONESIA": "Indonesisch", + "EXTRACTION_CHART_TITLE": "Extractie grafiek", + "PAGE_SETTINGS_SHOW_BACKUP_ISSUES": "Back-up problemen weergeven", + "PAGE_SETTINGS_SHOW_BACKUP_ISSUES_DESCRIPTION": "Geef een pop-up weer als er geen back-ups naar het bestandssysteem kunnen worden geschreven", + "AUTOMATIC_BACKUP_DID_FAIL": "Automatische back-up werkte niet, zorg ervoor dat u werkende back-ups hebt! Deze informatie kan worden uitgeschakeld in de instellingen", + "INTERNAL_BACKUP_DID_FAIL": "Interne back-up werkte niet, zorg ervoor dat u werkende back-ups hebt! Deze informatie kan worden uitgeschakeld in de instellingen", + "ZIP_BACKUP_FILE_COULD_NOT_BE_BUILD": "ZIP-bestand kon niet worden opgeslagen! Deze informatie kan worden uitgeschakeld in de instellingen", + "SEND_LOGS": "Logboeken verzenden", + "POPOVER_BLUETOOTH_ACTION_RECONNECT_REFRACTOMETER": "Sluit refractometer apparaat opnieuw aan", + "PAGE_SETTINGS_LANGUAGE_ITALIAN": "Italiaans", + "PAGE_SETTINGS_LANGUAGE_POLISH": "Pools", + "BREW_CANT_START_BECAUSE_TIMER_NOT_RESETTED_GENERAL_DESCRIPTION": "Je moet eerst je timer resetten voordat je kunt beginnen.", + "SMART_SCALE_FIRST_DRIP_THRESHOLD": "Eerste druppel drempel", + "SMART_SCALE_FIRST_DRIP_THRESHOLD_TOOLTIP": "Bij welk weegschaalgewicht moet de eerste druppel worden geteld? Standaard: >= 0,1g", + "PAGE_SETTINGS_BREW_TIMER_START_DELAY_ACTIVE": "Zetvertraging starten", + "PAGE_SETTINGS_BREW_TIMER_START_DELAY_ACTIVE_DESCRIPTION": "Stel een vertragingstijd in die een laadspinner weergeeft totdat het brouwen begint", + "STARTING_IN": "Beginnend in ... {{time}}", + "IOS_DATABASE_ISSUE_TITLE": "ATTENTIE !!!!! - DATABASEVERBINDING VERLOREN", + "IOS_DATABASE_ISSUE_DESCRIPTION": "Het spijt ons u te moeten meedelen dat de verbinding met de database is verbroken. Dit probleem is het gevolg van een onopgeloste bug in het systeem van Apple, waarover Beanconqueror geen controle heeft. Om mogelijk gegevensverlies te voorkomen, verzoeken wij u vriendelijk de Beanconqueror-applicatie onmiddellijk geforceerd af te sluiten en opnieuw te openen.", + "RELOAD_APP": "Herstart de app", + "WATER_TYPE_ADD_CUSTOM": "Water op maat", + "WATER_TYPE_THIRD_WAVE_WATER_CLASSIC_LIGHT_ROAST_PROFILE": "Third Wave Water - Klassiek licht geroosterd profiel", + "WATER_TYPE_THIRD_WAVE_WATER_MEDIUM_ROAST_PROFILE": "Third Wave Water - Middel geroosterd profiel", + "WATER_TYPE_THIRD_WAVE_WATER_DARK_ROAST_PROFILE": "Third Wave Water - Donker geroosterd profiel", + "WATER_TYPE_THIRD_WAVE_WATER_ESPRESSO_MACHINE_PROFILE": "Third Wave Water - Espressomachine Profiel", + "WATER_TYPE_THIRD_WAVE_WATER_COLD_BREW_PROFILE": "Third Wave Water - koud brouwprofiel", + "WATER_TYPE_THIRD_WAVE_WATER_LOW_ACID_PROFILE": "Third Wave Water - Laag Zuurprofiel", + "ADD_WATER": "Voeg water toe", + "EXTRACTION_CHART_LABEL_STRONG_UNDEREXTRACTED": "STERK
onderextractie", + "EXTRACTION_CHART_LABEL_STRONG": "Strong
", + "EXTRACTION_CHART_LABEL_STRONG_HARSH": "STERK
ruw", + "EXTRACTION_CHART_LABEL_UNDEREXTRACTED": "ondergeëxtraheerd", + "EXTRACTION_CHART_LABEL_IDEAL": "IDEAAL", + "EXTRACTION_CHART_LABEL_HARSH": "ruw", + "EXTRACTION_CHART_LABEL_WEAK_UNDEREXTRACTED": "ZWAK
onderextractie", + "EXTRACTION_CHART_LABEL_WEAK": "ZWAK
", + "EXTRACTION_CHART_LABEL_WEAK_HARSH": "ZWAK
ruw", + "PAGE_SETTINGS_TEXT_TO_SPEECH_SECTION": "Tekst naar spraak", + "TEXT_TO_SPEECH": { + "ACTIVATE": "Activeer tekst naar spraak", + "BREW_STARTED": "Brouwen gestart", + "BREW_ENDED": "Einde brouwen", + "TIME": "Tijd", + "SPEAK_EVERY_MS": "Spreek elke geselecteerde milliseconde", + "FOLLOWING_NUMBERS_WILL_BE_TEST_SPOKEN": "De volgende nummers worden als test uitgesproken", + "TEST_SPEAK": "Start test spraak", + "PITCH": "Toonhoogte", + "RATE": "beoordeling" + }, + "PAGE_SETTINGS_HAPTIC_FEEDBACK_SECTION": "Haptische feedback", + "HAPTIC_FEEDBACK": { + "ACTIVATE": "Activeer haptische feedback", + "BREW_STARTED": "Trillen bij het starten van het brouwen", + "BREW_STOPPED": "Trillen bij het stoppen van het brouwen", + "TARE": "Trillen bij tarra van de weegschaal" + }, + "CONNECTED": "Verbonden", + "DISCONNECTED": "Losgekoppeld", + "EXPERIMENTAL_FEATURE_DISCLAIMER": "Dit is een experimentele functie", + "SHOW_HOURS": "Toon uren", + "SHOW_MINUTES": "Toon Minuten", + "BEANS_UNARCHIVE": "uit het archief halen", + "TOAST_BEAN_UNARCHIVED_SUCCESSFULLY": "Boon is uit het archief gehaald", + "WATER_TYPE_PURE_COFFEE_WATER": "Zuiver Koffiewater", + "WATER_TYPE_EMPIRICAL_WATER_GLACIAL": "empirical water GLACIAL", + "WATER_TYPE_EMPIRICAL_WATER_SPRING": "empirical water SPRING", + "SORT_PREPARATION_TOOLS": "Sorteer bereidingsmiddelen", + "PREPARATION_TYPE_METICULOUS": "Meticulous", + "EXPORT_CAUTION": "Exporteren exporteert alleen de database, geen afbeeldingen, geen stromingsprofielen . Als deze nodig zijn, ga dan naar Gitbook voor meer informatie", + "POPOVER_FREEZE_COFFEE_BEAN": "Bevries koffieboon", + "POPOVER_UNFREEZE_COFFEE_BEAN": "Koffieboon ontdooien", + "BEAN_POPOVER_EDIT_FREEZE_DATE": "invries datum bewerken", + "BEAN_POPOVER_EDIT_UNFREEZE_DATE": "ontdooi datum bewerken", + "BEAN_POPOVER_LEFT_UNFROZEN": "Niet ingevroren", + "BEAN_POPOVER_FREEZE_PARTIAL_BAG": "Gedeelte zak invriezen (g)", + "FROZEN_BEANS": "Bevroren", + "BEAN_TAB_FROZEN_INFORMATION": "Bevroren notities", + "BEAN_POPOVER_FROZEN_BAGS": "Bevroren zakken", + "BEAN_POPOVER_FROZEN_DELETE_BEAN_MESSAGE": "Je gaat de hele zak invriezen en we hebben geen brouwsels gevonden die ermee gemaakt zijn, wil je de originele zak verwijderen?", + "CREATE_FROZEN_BEANS": "creëer bonen", + "BEAN_POPOVER_COPY_ATTACHMENTS": "Kopieer bijlagen", + "BEAN_POPOVER_COPY_ATTACHMENTS_DESCRIPTION": "Het kopiëren van bijlagen is aan het begin van deze functie gedeactiveerd", + "BEAN_DATA_FROZEN_DATE": "Invries datum", + "BEAN_DATA_UNFROZEN_DATE": "ontdooi datum", + "PAGE_BEANS_LIST_YOU_GOT_NO_FROZEN_BEANS": "Je hebt geen ingevroren bonen", + "BEAN_DATA_FROZEN_ID": "Ingevroren id", + "PAGE_SETTINGS_MANAGE_FEATURES": "Functies beheren", + "ACTIVE_BEAN_FREEZING_FEATURE": "Activeer het invriezen van bonen", + "NAV_FROZEN_BEANS_LIST": "Lijst met ingevroren bonen", + "BEAN_BREW_LIST_VIEW_PARAMETERS": "Boon informatie voor brouwen", + "BEAN_AGE_BY_BREW_DATE": "Leeftijd van de bonen op brouwdatum", + "BEAN_POPOVER_FROZEN_BEAN_WILL_BE_ARCHIVED_NOW_MESSAGE": "Je diepvrieszakken hebben als resultaat dat je originele zak geen gewicht meer heeft, maar je hebt er wel mee gebrouwen, dus beoordeel en stuur naar het archief.", + "BEAN_POPOVER_YOU_CANT_FREEZE_WITH_ZERO_WEIGHT_LEFT": "Je kunt niet bevriezen, want het restgewicht van je zak is nul", + "BEAN_DATA_BEST_DATE": "Beste Bonen Datum", + "BEAN_DATA_BEST_DATE_TOOLTIP": "Wanneer is de beste datum om de bonen te gebruiken?", + "BEAN_DATA_OPEN_DATE": "Openingsdatum van de zak", + "BEAN_DATA_OPEN_DATE_TOOLTIP": "Wanneer heb je de zak bonen geopend?", + "BEAN_FREEZING_STORAGE_TYPE_UNKNOWN": "Onbekend", + "BEAN_FREEZING_STORAGE_TYPE_COFFEE_BAG": "Koffiezak", + "BEAN_FREEZING_STORAGE_TYPE_COFFEE_JAR": "Koffie pot", + "BEAN_FREEZING_STORAGE_TYPE_ZIP_LOCK": "Ritssluiting", + "BEAN_FREEZING_STORAGE_TYPE_VACUUM_SEALED": "Vacuüm verzegeld", + "BEAN_FREEZING_STORAGE_TYPE_TUBE": "Buis", + "BEAN_DATA_FROZEN_STORAGE_TYPE": "Vriezer opslagtype", + "PAGE_SETTINGS_BREW_RATING_CHANGED_BREWS_NOT_VISIBLE": "U heeft de maximale beoordeling van uw brouwsels gewijzigd, maar u heeft uw brouwsels al hoger beoordeeld dan uw werkelijke maximum. Deze worden dus niet weergegeven bij de normale filterinstellingen.", + "PAGE_SETTINGS_BEAN_RATING_CHANGED_BEANS_NOT_VISIBLE": "U heeft de maximale beoordeling voor bonen gewijzigd, maar u heeft bonen al hoger beoordeeld dan uw werkelijke maximum. Ze worden dus niet weergegeven met de normale filterinstellingen.", + "BEAN_DATA_FROZEN_NOTE": "Bevroren notities", + "IGNORE_NEGATIVE_VALUES_DESCRIPTION": "Als je dit activeert, loopt de grafiek één seconde achter", + "IGNORE_ANOMALY_VALUES_DESCRIPTION": "Als je dit activeert, loopt de grafiek één seconde achter", + "SAVE_LOGFILES_TO_NOTES_FROM_MACHINE": "Bewaar de machine logs bij het opslaan van een brouwsel" +} \ No newline at end of file diff --git a/src/assets/i18n/pl.json b/src/assets/i18n/pl.json index ca541d8e..23e94e5e 100644 --- a/src/assets/i18n/pl.json +++ b/src/assets/i18n/pl.json @@ -1273,39 +1273,6 @@ "SHOW_MINUTES": "Pokaż minuty", "BEANS_UNARCHIVE": "Cofnij archiwizację", "TOAST_BEAN_UNARCHIVED_SUCCESSFULLY": "Ziarno nie zostało zarchiwizowane", - "UPDATE_TEXT_TITLE_TITLE": { - "7.4.0": { - "TITLE": "Wersja 7.4.0: Co nowego", - "DESCRIPTION": [ - "Ziarna<\/b>", - "Teraz możliwe jest zamrożenie ziaren kawy, aktywując tę funkcję w ustawieniach.", - "Dodano najlepszą i otwartą datę dla fasoli, należy aktywować ten parametr", - "Ziarna można teraz dodawać bezpośrednio z przeglądu wyboru.", - "", - "Termometr<\/b>", - "Wsparcie Combustion Inc. Termometr - Dziękujemy za urządzenie!", - "Wsparcie dla termometru Meater (nie Meater 2 lub Meater +) - dzięki Yannick!", - "", - "Sekcja wodna<\/b>", - "Dodano czystą wodę kawową", - "Dodana woda empiryczna", - "", - "Narzędzia przygotowawcze<\/b>", - "Posortuj teraz swoje narzędzia przygotowawcze", - "", - "Młynek<\/b>", - "Obrazy młynka są teraz wyświetlane w przeglądzie wyboru", - "", - "Ustawienia<\/b>", - "Komunikat bezpieczeństwa, jeśli zmieniono ocenę dla naparów lub ziaren, a maksymalna ocena jest niższa niż już oceniony wpis.", - "", - "Inne<\/b>", - "Przywrócono bezpośrednie ustawianie ostrości w sekundach podczas otwierania timera.", - "Kilka zmian technicznych w kodzie", - "Drobne poprawki" - ] - } - }, "WATER_TYPE_PURE_COFFEE_WATER": "Czysta woda kawowa", "WATER_TYPE_EMPIRICAL_WATER_GLACIAL": "woda empiryczna GLACIAL", "WATER_TYPE_EMPIRICAL_WATER_SPRING": "woda empiryczna SPRING", diff --git a/src/assets/i18n/tr.json b/src/assets/i18n/tr.json index cfb0c409..f2e49174 100644 --- a/src/assets/i18n/tr.json +++ b/src/assets/i18n/tr.json @@ -1273,39 +1273,6 @@ "SHOW_MINUTES": "Dakikayı göster", "BEANS_UNARCHIVE": "Arşivden çıkar", "TOAST_BEAN_UNARCHIVED_SUCCESSFULLY": "Çekirdek arşivden çıkarıldı", - "UPDATE_TEXT_TITLE_TITLE": { - "7.4.0": { - "TITLE": "Sürüm 7.4.0: Yenilikler", - "DESCRIPTION": [ - " Fasulye <\/b>", - "Artık kahve çekirdeklerinizi dondurmak mümkün, bu özelliği ayarlardan etkinleştirin", - "Çekirdek için en iyi ve açık tarih eklendi, bu parametreyi etkinleştirmeniz gerekir", - "Çekirdekler artık doğrudan seçime genel bakıştan eklenebilir", - "", - "Termometre<\/b>", - "Combustion Inc. Termometre Desteği - Cihaz için teşekkürler!", - "Meater Termometre desteği (Meater 2 veya Meater + değil) - Yannick'e teşekkürler!", - "", - "Su Bölümü<\/b>", - "Saf Kahve Suyu Eklendi", - "Ampirik su eklendi", - "", - "Hazırlık Araçları<\/b>", - "Hazırlık araçlarınızı şimdi sıralayın", - "", - "Öğütücü<\/b>", - "Öğütücü görüntüleri artık seçim genel görünümünde gösteriliyor", - "", - "Ayarlar<\/b>", - "Demleme veya çekirdek derecelendirmesini değiştirdiyseniz ve maksimum değer, halihazırda derecelendirilmiş bir girişten düşükse güvenlik mesajı", - "", - "Diğer<\/b>", - "Zamanlayıcıyı açarken doğrudan saniye odağı geri döndürüldü", - "Koddaki bazı teknik değişiklikler", - "Küçük değişiklikler" - ] - } - }, "WATER_TYPE_PURE_COFFEE_WATER": "Saf Kahve Suyu", "WATER_TYPE_EMPIRICAL_WATER_GLACIAL": "ampirik su GLACIAL", "WATER_TYPE_EMPIRICAL_WATER_SPRING": "ampirik su SPRING", diff --git a/src/assets/i18n/zh.json b/src/assets/i18n/zh.json index b7c5bd63..bca45dc7 100644 --- a/src/assets/i18n/zh.json +++ b/src/assets/i18n/zh.json @@ -1273,39 +1273,6 @@ "SHOW_MINUTES": "显示分钟", "BEANS_UNARCHIVE": "取消归档", "TOAST_BEAN_UNARCHIVED_SUCCESSFULLY": "咖啡豆已经取消归档", - "UPDATE_TEXT_TITLE_TITLE": { - "7.4.0": { - "TITLE": "版本7.4.0:有什么新功能?", - "DESCRIPTION": [ - "豆子<\/b>", - "现在可以将您的咖啡豆冷冻保存,您可以在设置中激活这个功能", - "为咖啡豆添加了最佳和开封日期,您需要激活此参数", - "现在可以直接从选择概览中添加豆子", - "空的", - "温度计<\/b>", - "支持 Combustion Inc. 温度计 - 感谢提供设备!", - "支持 Meater 温度计(不支持 Meater 2 或 Meater +) - 感谢 Yannick!", - "空的", - "水分区<\/b>", - "添加Pure Coffee Water", - "添加Empirical水", - "空的", - "准备工具<\/b>", - "整理您的准备工具", - "空的", - "磨豆机<\/b>", - "磨豆机的图片现在在选择概览中显示", - "空的", - "设置<\/b>", - "如果您更改了冲泡或咖啡豆的评分,并且最高评分低于已评分条目的评分,则显示安全提示信息", - "空的", - "其他<\/b>", - "在打开计时器时,恢复到以秒数计时为准", - "代码中的一些技术修改", - "小调整" - ] - } - }, "WATER_TYPE_PURE_COFFEE_WATER": "Pure Coffee Water", "WATER_TYPE_EMPIRICAL_WATER_GLACIAL": "empirical water GLACIAL", "WATER_TYPE_EMPIRICAL_WATER_SPRING": "empirical water SPRING", From 0b17805d3fe2b7a335fe9e0569b30a81f3ade94d Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Wed, 28 Aug 2024 21:56:44 +0200 Subject: [PATCH 24/55] Lokalise update --- src/assets/i18n/de.json | 33 - src/assets/i18n/en.json | 33 - src/assets/i18n/es.json | 33 - src/assets/i18n/fr.json | 37 +- src/assets/i18n/id.json | 33 - src/assets/i18n/it.json | 45 +- src/assets/i18n/nl.json | 1323 +++++++++++++++++++++++++++++++++++++++ src/assets/i18n/pl.json | 33 - src/assets/i18n/tr.json | 33 - src/assets/i18n/zh.json | 33 - 10 files changed, 1331 insertions(+), 305 deletions(-) create mode 100644 src/assets/i18n/nl.json diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json index 90777414..d63be163 100644 --- a/src/assets/i18n/de.json +++ b/src/assets/i18n/de.json @@ -1273,39 +1273,6 @@ "SHOW_MINUTES": "Minuten anzeigen", "BEANS_UNARCHIVE": "Archivierung aufheben", "TOAST_BEAN_UNARCHIVED_SUCCESSFULLY": "Bean wurde dearchiviert", - "UPDATE_TEXT_TITLE_TITLE": { - "7.4.0": { - "TITLE": "Version 7.4.0: Das ist neu", - "DESCRIPTION": [ - "Bohnen<\/b>", - "Es ist jetzt möglich Kaffeebohnen einzufrieren. Aktiviere diese Funktion in den Einstellungen", - "Bestes und offenes Datum für Bohnen hinzugefügt. Diese Parameter müssen aktiviert werden.", - "Bohnen können nun direkt aus der Auswahlübersicht hinzugefügt werden", - "", - " Thermometer <\/b>", - "Unterstützung des Combustion Inc. Thermometers – Danke für das Gerät!", - "Unterstützung des Meater-Thermometers (nicht Meater 2 oder Meater +) – Danke an Yannick!", - "", - "Wasserbereich<\/b>", - "Pure Coffee Water hinzugefügt", - "Empirical Water hinzugefügt", - "", - "Brühmethoden Tools<\/b>", - "Sortiere deine Zubereitungsmethoden", - "", - "Mühlen<\/b>", - "Mühlenbilder werden in der Übersicht angezeigt", - "", - "Einstellungen<\/b>", - "Sicherheitsmeldung, wenn du die Bewertung für Brühen oder Bohnen geändert hast und das Maximum niedriger ist als ein bereits bewerteter Eintrag", - "", - "Sonstiges<\/b>", - "Der direkte Sekundenfokus beim Öffnen des Timers wurde ausgebaut", - "Einige technische Änderungen im Code", - "Kleine Optimierungen" - ] - } - }, "WATER_TYPE_PURE_COFFEE_WATER": "Pure Coffee Water", "WATER_TYPE_EMPIRICAL_WATER_GLACIAL": "empirical water GLACIAL", "WATER_TYPE_EMPIRICAL_WATER_SPRING": "empirical water SPRING", diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index b73e2994..6408bd8f 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -1273,39 +1273,6 @@ "SHOW_MINUTES": "Show minutes", "BEANS_UNARCHIVE": "Unarchive", "TOAST_BEAN_UNARCHIVED_SUCCESSFULLY": "Bean has been unarchived", - "UPDATE_TEXT_TITLE_TITLE": { - "7.4.0": { - "TITLE": "Version 7.4.0: What's new", - "DESCRIPTION": [ - "Beans<\/b>", - "Its now possible to freeze your coffee beans, activate this feature in the settings", - "Added best and open date for beans, you need to activate this parameter", - "Beans can now be directly added from the select overview", - "", - "Thermometer<\/b>", - "Support of the Combustion Inc. Thermometer - Thanks for the Device!", - "Support of the Meater Thermometer (not Meater 2 or Meater +) - Thanks to Yannick!", - "", - "Water Section<\/b>", - "Added Pure Coffee Water", - "Added Empirical Water", - "", - "Preparation Tools<\/b>", - "Sort now your preparation tools", - "", - "Grinder<\/b>", - "Grinder images are now shown in the select overview", - "", - "Settings<\/b>", - "Security message if you changed the rating for brews or beans, and the maximum is lower then an already rated entry", - "", - "Other<\/b>", - "Reverted the direct seconds focus when opening the timer", - "Some technical changes in the code", - "Small tweaks" - ] - } - }, "WATER_TYPE_PURE_COFFEE_WATER": "Pure Coffee Water", "WATER_TYPE_EMPIRICAL_WATER_GLACIAL": "empirical water GLACIAL", "WATER_TYPE_EMPIRICAL_WATER_SPRING": "empirical water SPRING", diff --git a/src/assets/i18n/es.json b/src/assets/i18n/es.json index 9736cc49..38d1a467 100644 --- a/src/assets/i18n/es.json +++ b/src/assets/i18n/es.json @@ -1273,39 +1273,6 @@ "SHOW_MINUTES": "Mostrar minutos", "BEANS_UNARCHIVE": "Desarchivar", "TOAST_BEAN_UNARCHIVED_SUCCESSFULLY": "Café desarchivado", - "UPDATE_TEXT_TITLE_TITLE": { - "7.4.0": { - "TITLE": "Versión 7.4.0: Novedades", - "DESCRIPTION": [ - "Cafés<\/b>", - "Ahora es posible congelar tus cafés, puedes activar esta opción en ajustes.", - "Añadidas fechas de apertura y de consumo óptimo para tus cafés (desactivadas por defecto).", - "Ahora puedes añadir un nuevo café desde el menú de selección de cafés de una preparación.", - "", - "Termómetro<\/b>", - "Añadido soporte para el termómetro Combustion Inc. - Gracias por el aporte!", - "Añadido soporte para el termómetro Meater (no Meater 2 o Meater+) - Gracias a Yannick!", - "", - "Sección de agua<\/b>", - "Añadida agua Pure Coffee Water", - "Añadida agua Empirical Water", - "", - "Métodos de preparación<\/b>", - "Ahora puedes ordenar tus métodos de preparación", - "", - "Molinos<\/b>", - "Ahora se muestran las imágenes de los molinos en el menú de selección.", - "", - "Ajustes<\/b>", - "Se muestra una alerta si cambias la puntuación de una preparación o un café, y el máximo es menor que otra entrada ya puntuada.", - "", - "Otros<\/b>", - "El campo \"segundos\" ya no se selecciona automáticamente al abrir el selector de tiempo.", - "Cambios técnicos en el código.", - "Pequeños ajustes y mejoras." - ] - } - }, "WATER_TYPE_PURE_COFFEE_WATER": "Pure Coffee Water", "WATER_TYPE_EMPIRICAL_WATER_GLACIAL": "empirical water GLACIAL", "WATER_TYPE_EMPIRICAL_WATER_SPRING": "empirical water SPRING", diff --git a/src/assets/i18n/fr.json b/src/assets/i18n/fr.json index 8f71307e..17a5ccbe 100644 --- a/src/assets/i18n/fr.json +++ b/src/assets/i18n/fr.json @@ -479,7 +479,7 @@ "CUSTOM_MANAGE_PARAMETERS": "Gérer", "CUSTOM_SORT_PARAMETERS": "Trier", "BREW_PARAMETER_CUSTOMIZE_TITLE": "Paramètres personnalisés pour chaque méthode de préparation", - "BREW_PARAMETER_CUSTOMIZE_DESCRIPTION": "Tu veux choisir des paramètres personnalisés pour chaque méthode de préparation ? Navigues jusqu'à \"Méthodes\", ouvre le menu de la méthode de préparation spécifique et choisissez \"Personnaliser les paramètres\". Là, tu peux choisir quels paramètres seront utilisés pour cette préparation !", + "BREW_PARAMETER_CUSTOMIZE_DESCRIPTION": "Tu veux choisir des paramètres personnalisés pour chaque méthode de préparation ? Navigue jusqu'à \"Méthodes\", ouvre le menu de la méthode de préparation spécifique et choisissez \"Personnaliser les paramètres\". Là, tu peux choisir quels paramètres seront utilisés pour cette préparation !", "BREW_DATA_BREW_QUANTITY_TOOLTIP": "Quantité d'eau (non utilisable pour l'espresso)", "BREW_DATA_COFFEE_FIRST_DRIP_TIME_TOOLTIP": "Premier temps d'égouttage du café (espresso uniquement)", "BREW_DATA_PREPARATION_METHOD_TOOLTIP": "Préparation (uniquement personnalisable lorsqu'elle est active)", @@ -1148,7 +1148,7 @@ }, "SHOT": { "UPLOAD_SUCCESSFULLY": "Infusion téléchargé sur Visualizer.", - "UPLOAD_UNSUCCESSFULLY": "L'infusion n'a pas pu être téléchargé sur Visualizer." + "UPLOAD_UNSUCCESSFULLY": "L'infusion n'a pas pu être téléchargée sur Visualizer." }, "URL": "URL du serveur", "USERNAME": "Nom d'utilisateur", @@ -1273,39 +1273,6 @@ "SHOW_MINUTES": "Afficher les minutes", "BEANS_UNARCHIVE": "Désarchiver", "TOAST_BEAN_UNARCHIVED_SUCCESSFULLY": "Le grain a été désarchivé", - "UPDATE_TEXT_TITLE_TITLE": { - "7.4.0": { - "TITLE": "Version 7.4.0: c'est nouveau", - "DESCRIPTION": [ - "Grains<\/b>", - "Il est possible de congeler des grains de café. Active cette fonction dans les réglages", - "Ajout de la meilleure date et de la date d'ouverture pour les grains. Ces paramètres doivent être activés.", - "Les grains peuvent désormais être ajoutés directement à partir de l’aperçu de la sélection", - "", - " Thermomètre <\/b>", - "Support pour les thermomètres de Combustion Inc. – Merci pour l'appareil !", - "Support du thermomètre Meater (pas Meater 2 ou Meater +) – Merci à Yannick !", - "", - " Zone d'eau <\/b>", - "Ajouté Pure Coffee Water", - "Ajouté Empirical Water", - "", - " Outils de méthode d'infusion <\/b>", - "Trier tes méthodes de préparation", - "", - "Moulins<\/b>", - "Les images des moulins sont affichées dans l'aperçu", - "", - "Paramètres<\/b>", - "Message de sécurité si tu as modifié l'évaluation des infusions ou des grains et que le maximum est inférieur à une entrée déjà évaluée.", - "", - "Autres<\/b>", - "La mise au point directe en secondes lors de l'ouverture de la minuterie a été rétablie", - "Quelques changements techniques dans le code", - "Petites optimisations" - ] - } - }, "WATER_TYPE_PURE_COFFEE_WATER": "Pure Coffee Water", "WATER_TYPE_EMPIRICAL_WATER_GLACIAL": "empirical water GLACIAL", "WATER_TYPE_EMPIRICAL_WATER_SPRING": "empirical water SPRING", diff --git a/src/assets/i18n/id.json b/src/assets/i18n/id.json index fc9969cd..50594012 100644 --- a/src/assets/i18n/id.json +++ b/src/assets/i18n/id.json @@ -1273,39 +1273,6 @@ "SHOW_MINUTES": "Tampilkan menit", "BEANS_UNARCHIVE": "Tidak diarsipkan", "TOAST_BEAN_UNARCHIVED_SUCCESSFULLY": "Bean tidak diarsipkan", - "UPDATE_TEXT_TITLE_TITLE": { - "7.4.0": { - "TITLE": "Versi 7.4.0: Apa yang baru", - "DESCRIPTION": [ - "Biji<\/b>", - "Sekarang dimungkinkan untuk mencatat pembekuan biji kopi Anda, aktifkan fitur ini di pengaturan", - "Menambahkan tanggal terbaik dan tanggal membuka kopi, Anda perlu mengaktifkan parameter ini", - "Biji sekarang dapat langsung ditambahkan dari pilihan", - "", - "Thermometer<\/b>", - "Dukungan Termometer Combustion Inc. - Terima kasih untuk Perangkatnya!", - "Dukungan Termometer Meater (bukan Meater 2 atau Meater +) - Terima kasih kepada Yannick!", - "", - "Water Section<\/b>", - "Penambahan Pure Coffee Water", - "Penambahan Empirical Water", - "", - "Preparation Tools<\/b>", - "Jenis alat-alat seduhan dapat diurutkan", - "", - "Grinder<\/b>", - "Gambar grinder sekarang ditampilkan dalam pilihan", - "", - "Pengaturan<\/b>", - "Pesan keamanan jika Anda mengubah peringkat untuk seduhan atau biji kopi, dan nilai maksimumnya lebih rendah dari entri yang sudah diberi peringkat", - "", - "Lain-lain<\/b>", - "Mengembalikan fokus detik langsung saat membuka pengatur waktu", - "Beberapa perubahan teknis pada kode", - "Perubahan kecil" - ] - } - }, "WATER_TYPE_PURE_COFFEE_WATER": "Pure Coffee Water", "WATER_TYPE_EMPIRICAL_WATER_GLACIAL": "empirical water GLACIAL", "WATER_TYPE_EMPIRICAL_WATER_SPRING": "empirical water SPRING", diff --git a/src/assets/i18n/it.json b/src/assets/i18n/it.json index 0face123..522b9323 100644 --- a/src/assets/i18n/it.json +++ b/src/assets/i18n/it.json @@ -44,13 +44,13 @@ "PAGE_HOME_DIFFERENT_PREPARATION_METHODS": "metodi di preparazione", "PAGE_HOME_DIFFERENT_MILLS": "macinacaffè", "PAGE_HOME_SUPPORTER": "App Supporter", - "PAGE_HOME_START_BREW": "", + "PAGE_HOME_START_BREW": "Inizia la preparazione", "PAGE_BEANS_LIST_OBTAINABLE": "Disponibili", "PAGE_BEANS_LIST_YOU_GOT_NO_FRESH_BEANS": "Non c'è più caffè!", "PAGE_BEANS_LIST_YOU_GOT_NO_FINISHED_BEANS": "Nessun caffè archiviato", "PAGE_MILL_LIST_NO_MILL_EXISTING": "Nessun macinacaffè", "PAGE_PREPARATION_LIST_NO_PREPARATION_EXISTING": "Nessun metodo di preparazione", - "PAGE_CONTACT_SUGGESTIONS_QUESTIONS_WISHES": "Suggerimenti, domande, bug o richieste?", + "PAGE_CONTACT_SUGGESTIONS_QUESTIONS_WISHES": "Suggerimenti, domande, bugs o richieste?", "PAGE_THANKS_THANKS_FOR_YOUR_SUPPORT": "Grazie per il sostegno!", "PAGE_SETTINGS_LANGUAGE": "Impostazioni della lingua", "PAGE_SETTINGS_LANGUAGE_GERMAN": "Deutsch", @@ -61,8 +61,8 @@ "PAGE_SETTINGS_GENERAL_SETTINGS": "Impostazioni generali", "PAGE_SETTINGS_TRANSFER": "Trasferisci dati", "PAGE_SETTINGS_PRESET_LAST_BREW": "Attiva parametri preimpostati", - "PAGE_SETTINGS_DISPLAY": "", - "PAGE_SETTINGS_DISPLAY_SINGLE_PAGE": "", + "PAGE_SETTINGS_DISPLAY": "Visualizza", + "PAGE_SETTINGS_DISPLAY_SINGLE_PAGE": "Pagina singola", "PAGE_SETTINGS_DISPLAY_PAGING": "Paginazione", "PAGE_SETTINGS_STARTUP_VIEW": "Pagina iniziale", "PAGE_SETTINGS_ANALYTICS_INFORMATION": "Analytics", @@ -76,10 +76,10 @@ "PAGE_STATISTICS_TOTAL_GROUND_BEANS": "Totale caffè macinato", "PAGE_STATISTICS_MONEY_SPENT_FOR_COFFEE": "Soldi spesi in caffè", "PAGE_STATISTICS_DRUNKEN_BREWS": "Numero totale preparazioni", - "PAGE_STATISTICS_BREW_PROCESSES": "Preparazioni", + "PAGE_STATISTICS_BREW_PROCESSES": "Preparazioni totali", "PAGE_STATISTICS_DRUNKEN_QUANTITY": "Quantità consumata", "PAGE_STATISTICS_BEAN_WEIGHT_USED": "Totale caffè macinato", - "PAGE_BREW_TEXT_INFORMATION_FROM_ROASTER": "", + "PAGE_BREW_TEXT_INFORMATION_FROM_ROASTER": "Info sulla torrefazione", "PAGE_ABOUT_NO_VERSION_AVAILABLE": "Nessuna versione disponibile", "PAGE_ABOUT_APP_VERSION": "Versione App", "PAGE_LICENCES_WEBSITE": "Sito web", @@ -1273,39 +1273,6 @@ "SHOW_MINUTES": "Mostra minuti", "BEANS_UNARCHIVE": "Annulla l'archiviazione", "TOAST_BEAN_UNARCHIVED_SUCCESSFULLY": "Il caffè è stato disarchiviato", - "UPDATE_TEXT_TITLE_TITLE": { - "7.4.0": { - "TITLE": "Versione 7.4.0: Cosa c'è di nuovo", - "DESCRIPTION": [ - "Caffè<\/b>", - "Ora è possibile tenere traccia del caffè congelato, puoi attivare questa funzione nelle impostazioni", - "Aggiunta della data suggerita e di apertura per i caffè, è necessario attivare questo parametro nelle impostazioni", - "Ora è possibile aggiungere del caffè direttamente dalla selezione", - "", - " Termometro <\/b>", - "Supporto del termometro Combustion Inc. - Grazie per il dispositivo!", - "Supporto del termometro Meater (non Meater 2 o Meater +) - Grazie a Yannick!", - "", - "Sezione Acqua<\/b>", - "Aggiunta l'acqua Pure Coffee Water", - "Aggiunta l'acqua Empirical Water", - "", - "Accessori preparazione<\/b>", - "Ordina ora i tuoi strumenti di preparazione", - "", - "Macinacaffè<\/b>", - "Le immagini del macinacaffè sono mostrate nella selezione", - "", - "Impostazioni<\/b>", - "Messaggio di sicurezza se hai modificato la valutazione e il massimo è inferiore a cioè che è già valutato", - "", - "Altro<\/b>", - "Rimossa la recente aggiunta di auto-focus sui secondi del timer della preparazione", - "Piccole migliorie nel codice", - "Piccole migliorie" - ] - } - }, "WATER_TYPE_PURE_COFFEE_WATER": "Pure Coffee Water", "WATER_TYPE_EMPIRICAL_WATER_GLACIAL": "empirical water GLACIAL", "WATER_TYPE_EMPIRICAL_WATER_SPRING": "empirical water SPRING", diff --git a/src/assets/i18n/nl.json b/src/assets/i18n/nl.json new file mode 100644 index 00000000..31e2cd3e --- /dev/null +++ b/src/assets/i18n/nl.json @@ -0,0 +1,1323 @@ +{ + "NAV_MENU": "Menu", + "NAV_HOME": "Thuis", + "NAV_SETTINGS": "Instellingen", + "NAV_BREWS": "Brouwsels", + "NAV_BEANS": "Bonen", + "NAV_PREPARATION": "Methoden", + "NAV_MILL": "Malers", + "NAV_ABOUT_US": "Over ons", + "NAV_CONTACT": "Contact", + "NAV_PRIVACY": "Privacy", + "NAV_CREDITS": "Credits", + "NAV_TERMS": "Algemene voorwaarden", + "NAV_THANKS": "Bedankt!", + "NAV_LICENCES": "OSS-licenties", + "NAV_STATISTICS": "Statistieken", + "NAV_IMPRESSUM": "Impressum", + "NAV_COOKIE": "Cookies", + "NAV_LOGS": "Logboeken", + "NAV_BREW_PARAMS": "Brouwparameters", + "NAV_INFORMATION_TO_APP": "Over Beanconqueror", + "NAV_WATER_SECTION": "Water", + "NAV_HELPER": "Calculaties", + "POPOVER_BREWS_OPTION_REPEAT": "Herhalen", + "POPOVER_BREWS_OPTION_DETAIL": "Details", + "DETAIL": "Details", + "POPOVER_BREWS_OPTION_EDIT": "Bewerking", + "POPOVER_BREWS_OPTION_DELETE": "Verwijder", + "POPOVER_BREWS_OPTION_PHOTO_GALLERY": "Fotogalerij", + "POPOVER_BREWS_OPTION_CUPPING": "Cupping", + "POPOVER_BREWS_OPTION_MAP_COORDINATES": "Toon op kaart", + "POPOVER_BREWS_OPTION_FAST_REPEAT": "Snel brouwen herhalen", + "PAGE_BREWS_NO_ENTRIES": "Nog geen brouwsels toegevoegd", + "PAGE_BREWS_NO_ARCHIVED_ENTRIES": "Je hebt nog geen brouwsels gearchiveerd", + "CANT_START_NEW_BREW_TITLE": "Er ontbreekt hier iets...", + "CANT_START_NEW_BREW_DESCRIPTION": "Om te beginnen, maak een beschrijving voor één type bonen, één zetmethode en één grinder. Gebruik het menu om naar de verschillende categorieën te navigeren om deze informatie toe te voegen.", + "PAGE_HOME_WELCOME_GREETINGS": "Leuk dat je er bent!", + "PAGE_HOME_TOTAL_BREW": "Brouwen", + "PAGE_HOME_TOTAL_BREWS": "Brouwsels", + "PAGE_HOME_BEAN_EXPLORED": "Bonen onderzocht", + "PAGE_HOME_BEANS_EXPLORED": "Bonen onderzocht", + "PAGE_HOME_LAST_BREWS": "Laatste brouwsels", + "PAGE_HOME_LAST_BREW": "Laatste brouwsel", + "PAGE_HOME_DIFFERENT_PREPARATION_METHODS": "Verschillende bereidingsmethoden", + "PAGE_HOME_DIFFERENT_MILLS": "Verschillende malers", + "PAGE_HOME_SUPPORTER": "App Supporter", + "PAGE_HOME_START_BREW": "Begin met brouwen", + "PAGE_BEANS_LIST_OBTAINABLE": "Beschikbaar", + "PAGE_BEANS_LIST_YOU_GOT_NO_FRESH_BEANS": "Je hebt geen verse bonen meer!", + "PAGE_BEANS_LIST_YOU_GOT_NO_FINISHED_BEANS": "Je hebt geen gearchiveerde bonen.", + "PAGE_MILL_LIST_NO_MILL_EXISTING": "Je hebt nog geen grinders toegevoegd.", + "PAGE_PREPARATION_LIST_NO_PREPARATION_EXISTING": "Je hebt nog geen zetmethodes toegevoegd.", + "PAGE_CONTACT_SUGGESTIONS_QUESTIONS_WISHES": "Suggesties, vragen, bugs of verzoeken?", + "PAGE_THANKS_THANKS_FOR_YOUR_SUPPORT": "Bedankt voor je steun!", + "PAGE_SETTINGS_LANGUAGE": "Taalinstellingen", + "PAGE_SETTINGS_LANGUAGE_GERMAN": "Duits", + "PAGE_SETTINGS_LANGUAGE_ENGLISH": "Engels", + "PAGE_SETTINGS_LANGUAGE_SPANISH": "Spaans", + "PAGE_SETTINGS_LANGUAGE_TURKISH": "Turks", + "PAGE_SETTINGS_LANGUAGE_CHINESE": "Chinees", + "PAGE_SETTINGS_GENERAL_SETTINGS": "Algemene instellingen", + "PAGE_SETTINGS_TRANSFER": "Gegevens overdragen", + "PAGE_SETTINGS_PRESET_LAST_BREW": "Vooraf ingestelde waarden?", + "PAGE_SETTINGS_DISPLAY": "Weergave", + "PAGE_SETTINGS_DISPLAY_SINGLE_PAGE": "Eén pagina", + "PAGE_SETTINGS_DISPLAY_PAGING": "Paging", + "PAGE_SETTINGS_STARTUP_VIEW": "Startpagina", + "PAGE_SETTINGS_ANALYTICS_INFORMATION": "Analytics", + "PAGE_SETTINGS_ANALYTICS_INFORMATION_TOOLTIP": "Druk op i voor meer informatie", + "PAGE_SETTINGS_TRACK_BREW_COORDINATES": "Geolocatie van brouwsel opslaan", + "PAGE_SETTINGS_FAST_REPEAT": "Snel herhalen", + "PAGE_SETTINGS_TRACK_CAFFEINE_CONSUMPTION": "bewaar cafeïneconsumptie", + "PAGE_SETTINGS_WAKE_LOCK": "Houd het display actief tijdens het brouwen", + "PAGE_SETTINGS_CURRENCY": "Valuta", + "PAGE_STATISTICS_DIFFERENT_PREPARATION_METHOD": "Bereidingsmethoden", + "PAGE_STATISTICS_TOTAL_GROUND_BEANS": "Totaal gemalen bonen", + "PAGE_STATISTICS_MONEY_SPENT_FOR_COFFEE": "Geld uitgegeven aan bonen", + "PAGE_STATISTICS_DRUNKEN_BREWS": "Totaal aantal brouwsels", + "PAGE_STATISTICS_BREW_PROCESSES": "Totaal brouwsels", + "PAGE_STATISTICS_DRUNKEN_QUANTITY": "Verbruikte hoeveelheid", + "PAGE_STATISTICS_BEAN_WEIGHT_USED": "Totaal gemalen bonen", + "PAGE_BREW_TEXT_INFORMATION_FROM_ROASTER": "Informatie over de brander", + "PAGE_ABOUT_NO_VERSION_AVAILABLE": "Geen Versie beschikbaar", + "PAGE_ABOUT_APP_VERSION": "App Versie", + "PAGE_LICENCES_WEBSITE": "Website", + "BEAN_DATA_ROAST_NAME": "Mate van branding", + "BEAN_DATA_CUSTOM_ROAST_NAME": "Aangepaste mate van branding", + "BEAN_DATA_ROASTING_DATE": "Brand datum", + "BEAN_DATA_ROASTER": "Brander", + "BEAN_DATA_VARIETY": "Variëteit", + "BEAN_DATA_PROCESSING": "Verwerking", + "BEAN_DATA_COUNTRY": "Land", + "BEAN_DATA_MIX": "Melange", + "BEAN_DATA_AROMATICS": "Smaakprofiel", + "BEAN_DATA_WEIGHT": "Gewicht", + "BEAN_DATA_COST": "kosten", + "BEAN_DATA_NAME": "Naam", + "BEAN_DATA_REGION": "Regio", + "BEAN_DATA_FARM": "Boerderij", + "BEAN_DATA_FARMER": "Boer", + "BEAN_DATA_ELEVATION": "Elevatie", + "BEAN_DATA_HARVEST_TIME": "Geoogst", + "BEAN_DATA_PERCENTAGE": "Percentage", + "BEAN_DATA_CERTIFICATION": "Bonencertificering", + "BEAN_DATA_ROASTING_TYPE": "type branding", + "BEAN_DATA_DECAFFEINATED": "Cafeïnevrij", + "BEAN_DATA_URL": "Website", + "BEAN_DATA_EAN": "EAN \/ Artikelnummer", + "BEAN_DATA_PURCHASING_PRICE": "Aankoopprijs", + "BEAN_DATA_FOB_PRICE": "FOB-prijs", + "BEAN_DATA_CUPPING_POINTS": "Cupping-punten", + "BREW_DATA_CUSTOM_BREW_TIME": "Aangepaste brouwtijd", + "BREW_CREATION_DATE": "Aanmaakdatum", + "REPEAT": "Herhalen", + "EDIT": "Bewerken", + "DELETE": "Verwijderen", + "FINISHED": "Archief", + "NOTES": "Notities", + "ADD_PHOTO": "Foto toevoegen", + "CANCEL": "Annuleren", + "GENERATE": "Genereren", + "SAVE": "Opslaan", + "ADD_SOMETHING": "Toevoegen", + "CONTACT": "Contact", + "NAME": "Naam", + "IMPORT": "import", + "EXPORT": "Export", + "VIEW": "Weergave", + "ARCHIVE": "archief", + "CURRENT": "huidig", + "BACK": "Terug", + "CLOSE": "sluiten", + "DAY": "Dag", + "BREW_DATA_TEMPERATURE_TIME": "Temperatuur Tijd", + "BREW_DATA_SURF_TIME": "Surftijd", + "BREW_DATA_TIME": "Tijd", + "BREW_DATA_GRIND_SIZE": "Maalinstelling", + "BREW_DATA_GRIND_WEIGHT": "Gemalen koffie (gr)", + "BREW_DATA_IN_OUT_BR": "In\/Uit (BR)", + "BREW_DATA_NOTES": "Notities", + "BREW_DATA_PREPARATION_METHOD": "Bereidingswijze", + "BREW_DATA_MILL": "Maler", + "BREW_DATA_MILL_SPEED": "Maalsnelheid (rpm)", + "BREW_DATA_MILL_TIMER": "Maaltijd", + "BREW_DATA_BREW_QUANTITY": "Hoeveelheid water", + "BREW_DATA_BEAN_TYPE": "Boon type", + "BREW_DATA_BREW_TEMPERATURE": "Brouwtemperatuur", + "BREW_DATA_PRESSURE_PROFILE": "Profiel", + "BREW_DATA_COFFEE_TYPE": "Type koffie", + "BREW_DATA_COFFEE_CONCENTRATION": "Koffie Concentratie", + "BREW_DATA_COFFEE_FIRST_DRIP_TIME": "Eerste druppeltijd", + "BREW_DATA_COFFEE_BLOOMING_TIME": "Bloeitijd \/ Pre-infusie", + "BREW_DATA_ATTACHMENTS": "Bijlagen \/ Foto's", + "BREW_DATA_RATING": "Beoordeling", + "BREW_DATA_CALCULATED_COFFEE_BREW_TIME": "Koffiezettijd", + "BREW_DATA_TDS": "Totaal opgeloste vaste stoffen %", + "BREW_DATA_CALCULATED_EXTRACTION_YIELD": "Extractie Opbrengst %", + "BREW_INFORMATION_BREW_RATIO": "Brouw Verhouding", + "BREW_INFORMATION_BEAN_AGE": "Boon leeftijd", + "BREW_INFORMATION_BREW_QUANTITY_TYPE_NAME": "Hoeveelheidstype", + "BREW_DATA_TDS_EY": "TDS \/ %EY", + "BREW_DATA_BREW_BEVERAGE_QUANTITY": "Hoeveelheid drank", + "BREW_DATA_PREPARATION_METHOD_TOOL": "bereidings gereedschappen", + "BREW_DATA_WATER": "Water", + "BREW_DATA_BEAN_WEIGHT_IN": "Bonengewicht (g)", + "BREW_DATA_VESSEL": "Serveerkan", + "BREW_DATA_VESSEL_WEIGHT": "Serveerkan gewicht", + "BREW_DATA_VESSEL_NAME": "Serveerkan naam", + "BREW_DATA_FLAVOR": "Smaak", + "BREW_DATA_FLOW_PROFILE": "Stroom", + "ONE_DAY": "Dag", + "DAYS": "Dagen", + "ONE_HOUR": "uur", + "HOURS": "uren", + "ONE_MINUTE": "minuut", + "MINUTES": "minuten", + "WITHOUT_COFFEE": "zonder koffie", + "NOT_FOUND": "Niet gevonden", + "INVALID_FILE_FORMAT": "Ongeldig bestandsformaat", + "FILE_NOT_FOUND_INFORMATION": "Bestand niet gevonden", + "ERROR_ON_FILE_READING": "Fout bij het lezen van bestandsgegevens", + "IMPORT_SUCCESSFULLY": "Importeren succesvol", + "IMPORT_UNSUCCESSFULLY_DATA_NOT_CHANGED": "Import mislukt, er zijn geen gegevens gewijzigd", + "INVALID_FILE_DATA": "Ongeldige bestandsinhoud", + "DOWNLOADED": "Gedownload", + "FILE_DOWNLOADED_SUCCESSFULLY": "Bestand ' {{fileName}} ' is succesvol gedownload naar uw downloadmap!", + "NO": "Nee", + "YES": "Ja", + "SURE_QUESTION": "Weet je het zeker?", + "DELETE_BREW_QUESTION": "Brouwsel verwijderen?", + "DELETE_BEAN_QUESTION": "Boon verwijderen? Alle bijbehorende brouwsels worden ook verwijderd!", + "DELETE_GREEN_BEAN_QUESTION": "Groene bonen verwijderen? Alle bijbehorende geroosterde bonen, evenals brouwsels worden verwijderd!", + "DELETE_MILL_QUESTION": "Grinder verwijderen? Alle bijbehorende brouwsels worden ook verwijderd!", + "DELETE_PREPARATION_METHOD_QUESTION": "Bereidingswijze verwijderen? Alle bijbehorende brouwsels worden ook verwijderd!", + "DELETE_PREPARATION_TOOL_QUESTION": "Bereidingstool verwijderen? Alle brouwsels die aan deze tool zijn gekoppeld, worden bijgewerkt.", + "APP_COULD_NOT_STARTED_CORRECTLY_BECAUSE_MISSING_FILESYSTEM": "App kon niet correct worden gestart vanwege ontbrekend bestandssysteem", + "CARE": "Zorg", + "ERROR_OCCURED": "Er is een fout opgetreden", + "CSV_FILE_NOT_DOWNLOADED": "CSV-bestand kon niet worden gedownload!", + "CSV_FILE_DOWNLOADED_SUCCESSFULLY": "CSV-bestand ' {{fileName}} ' is succesvol gedownload naar uw downloadmap!", + "ADD_BREW": "Voeg brouwsel toe", + "CHOOSE": "Kies", + "CHOOSE_PHOTO_OR_LIBRARY": "Maak een foto of kies uit de fotogalerij.", + "RECORD": "Maak een foto", + "PAGE_BEAN_INFORMATION": "Boon informatie", + "PAGE_BEAN_INFORMATION_GOOD_BREWS": "Goed", + "PAGE_BEAN_INFORMATION_BAD_BREWS": "Slecht", + "PAGE_BEAN_INFORMATION_COUNT_BREWS": "Totaal aantal brouwsels", + "INFORMATION": "Informatie", + "PAGE_BEAN_BREW_CHART_TITLE": "Brouwoverzicht voor deze boon", + "PAGE_BEAN_INFORMATION_AWESOME_BREWS": "Geweldig", + "PAGE_BEAN_INFORMATION_NORMAL_BREWS": "Normaal", + "PAGE_BEAN_INFORMATION_NOT_RATED_BREWS": "Niet beoordeeld", + "PAGE_PREPARATION_INFORMATION_BREWS_DONE": "Brouwsels met deze bereidingswijze", + "PAGE_PREPARATION_INFORMATION_BREWED_QUANTITY": "Gebrouwen hoeveelheid", + "PAGE_PREPARATION_INFORMATION_GRIND_WEIGHT": "Verbruikt gewicht van bonen", + "PAGE_PREPARATION_INFORMATION_TIME_SPENT_BREWING": "Totale bereidingstijd", + "PAGE_PREPARATION_INFORMATION": "Informatie over de bereiding", + "SECONDS": "Seconden", + "PAGE_MILL_INFORMATION": "Maler informatie", + "PAGE_MILL_INFORMATION_BREWS_DONE": "Brouwsels met deze maler", + "PAGE_MILL_INFORMATION_GRIND_WEIGHT": "Verbruikt gewicht van bonen", + "PAGE_HELPER_WATER_HARDNESS": "Waterhardheid", + "PAGE_HELPER_WATER_HARDNESS_CA_CONTENTS": "Calciumgehalte mg\/l", + "PAGE_HELPER_WATER_HARDNESS_MG_CONTENTS": "Magnesiumgehalte mg\/l", + "PAGE_HELPER_WATER_HARDNESS_GERMAN_HARDNESS": "Duitse hardheidsgraad", + "PAGE_HELPER_WATER_HARDNESS_TOTAL_HARDNESS": "Totale hardheid", + "PAGE_HELPER_BREW_RATIO": "Brouw Verhouding", + "PAGE_HELPER_BREW_RATIO_GROUND_COFFEE": "Gemalen koffie (gr)", + "PAGE_HELPER_BREW_RATIO_WATER": "Vloeistof (gr\/ml)", + "PAGE_HELPER_BREW_RATIO_CALCULATED": "Berekende brouwverhouding", + "PAGE_SETTINGS_SHOW_ARCHIVED_BREWS": "Gearchiveerde brouwsels weergeven", + "PAGE_SETTINGS_SHOW_ARCHIVED_BEANS": "Gearchiveerde bonen weergeven", + "PAGE_SETTINGS_SHOW_ARCHIVED_GREEN_BEANS": "Toon gearchiveerde groene bonen", + "CUPPING_SCORE": "Score", + "CUPPING_SCORE_DRY_FRAGRANCE": "Droge geur", + "CUPPING_SCORE_WET_AROMA": "Nat aroma", + "CUPPING_SCORE_BRIGHTNESS": "Helderheid", + "CUPPING_SCORE_FLAVOR": "Smaak", + "CUPPING_SCORE_BODY": "Body", + "CUPPING_SCORE_FINISH": "Afwerking", + "CUPPING_SCORE_SWEETNESS": "Zoetheid", + "CUPPING_SCORE_CLEAN_CUP": "Clean Cup", + "CUPPING_SCORE_COMPLEXITY": "Complexiteit", + "CUPPING_SCORE_UNIFORMITY": "Uniformiteit", + "CUPPING_SCORE_CUPPERS_CORRECTION": "Cuppers-correctie", + "CUPPING_SCORE_DRY_FRAGRANCE_TOOLTIP": "Verwijst naar het aroma van de droge gemalen koffie voordat er heet water aan wordt toegevoegd.", + "CUPPING_SCORE_WET_AROMA_TOOLTIP": "De geur van nat koffiedik, nadat er heet water aan is toegevoegd.", + "CUPPING_SCORE_BRIGHTNESS_TOOLTIP": "Zuurgraad is de smaak van scherpe hoge tonen in de koffie, veroorzaakt door een set van chlorogeen, citroenzuur, kininezuur, azijnzuur en andere, voornamelijk waargenomen in de voorkant van de mond en op de tong. (Het is een goede kwaliteit; niet gerelateerd aan bitterheid in koffie, en niet direct verantwoordelijk voor maagklachten). Zuurgraad wordt gewaardeerd door veel kopers, en is direct gerelateerd aan de kwaliteit van de kop, aangezien zuurgraad het product is van aanplantingen op grote hoogte.", + "CUPPING_SCORE_FLAVOR_TOOLTIP": "Dit is de algehele indruk in de mond, inclusief alle andere beoordelingen. Er zijn 4 \"primaire smaak\"-groeperingen (zuur, zoet, zout, bitter) en veel \"secundaire smaken\".", + "CUPPING_SCORE_BODY_TOOLTIP": "Vaak \"mondgevoel\" genoemd, is body het gevoel van gewicht en dikte van de gezette koffie, veroorzaakt door het percentage oplosbare vaste stoffen in de kop, inclusief alle organische verbindingen die worden geëxtraheerd (de zetmethode en de hoeveelheid gemalen koffie die wordt gebruikt, hebben hier grote invloed op). We beoordelen Body op een lagere schaal omdat licht gebodyde koffiesoorten zeker niet slecht zijn en in sommige origines past de lichtere body het beste bij het algehele karakter van de kop.", + "CUPPING_SCORE_FINISH_TOOLTIP": "De aanhoudende of opkomende smaken die komen nadat de mond is schoongemaakt. Dit omvat de tijd dat de koffie je mond verlaat tot minuten daarna...een reden dat je veel cuppers zult vinden die hun nasmaakscores herzien terwijl ze een minuut of twee later nog steeds een positieve smaak ervaren.", + "CUPPING_SCORE_SWEETNESS_TOOLTIP": "Zoetheid is bijna altijd een gewenste kwaliteit in koffie, zelfs als het op eufemistische wijze wordt beschreven, zoals \"rustieke zoetheid\" of \"bitterzoetheid\". U zult merken dat verfijnde zoetheid (denk aan Europees gebak, fijn snoepgoed, witte suiker, pure zoetheid) hoog scoort, evenals complexe zoetheid van fruitsuikers (fructose). Moutige zoetheid (maltose) is minder traditioneel, maar wel wenselijk en honing kan variëren van heel puur en schoon tot complex, rustiek, bijna gistachtig. Kortom, als zoetheid een sleutel is tot de kop, zal het goed worden beoordeeld.", + "CUPPING_SCORE_CLEAN_CUP_TOOLTIP": "Let op dat \"clean cup\" niet letterlijk betekent dat er geen vuil op de koffie zit. Het gaat alleen om smaak en rauwe, funky koffies die \"onrein\" zijn en de smaak kan ook heel wenselijk zijn, zoals nat-gepelde Indonesische koffies uit Sumatra, of droog verwerkte Ethiopische en Jemenitische types.", + "CUPPING_SCORE_COMPLEXITY_TOOLTIP": "Complexiteit complimenteert de scores voor \"smaak\" en \"afwerking\", om een veelheid of gelaagdheid van veel smaken te communiceren. Het betekent dat er veel te ontdekken valt in de kop. Aan de andere kant kunnen simpele koffies een opluchting zijn na overmatige blootstelling aan veel krachtige, intense, complexe koffies.", + "CUPPING_SCORE_UNIFORMITY_TOOLTIP": "Uniformiteit verwijst naar verschillen tussen kopjes. Koffiesoorten die met een droog proces zijn bereid, kunnen van nature minder uniform zijn dan koffiesoorten die met een nat proces zijn bereid. We zouden nooit een partij met fantastische smaken vermijden als deze af en toe afwijkt. Dit wordt gescoord tijdens het cuppingprotocol, waarbij meerdere kopjes worden gemaakt van elke partij die wordt beoordeeld.", + "CUPPING_SCORE_CUPPERS_CORRECTION_TOOLTIP": "Dit is aangepast van het SCAA-systeem en de Cup of Excellence-score (soms noemen ze het \"Overall Points\"). Het stelt een cupper in staat om ervoor te zorgen dat de totale score de algehele indruk van de cup correct weergeeft. U zou deze aanpak kunnen bekritiseren en het \"vervalsen\" van het totaal kunnen beschouwen. In zekere zin zou u gelijk hebben ... maar het zou veel erger zijn om de categoriescores te veranderen om het gewenste totaal te bereiken (om een koffie een 9 te geven voor zuurgraad terwijl u weet dat het een 7 is), of omgekeerd om een koffie die absoluut een 90 verdient, op 84 te laten eindigen. Het specifieke Cupper's Correction-nummer doet er niet toe, of het nu een 5 of een 8 is ... het idee is dat de totale score een correcte indruk geeft van de kwaliteit van de koffie.", + "CUPPING_SCORE_TOOLTIP": "100-95 = Verbazingwekkend, 90-94 = Uitstekend, 85-89 = Zeer goed, 80-84 = Goed, 75-79 = Redelijk, 70-74 = Slecht", + "DETAIL_BREW": "Brouw details", + "DETAIL_BEAN": "Boon details", + "DETAIL_MILL": "Maler details", + "DETAIL_PREPARATION": "Bereidings details", + "EDIT_BREW": "Bewerk brouwsel", + "ADD_BEAN": "Boon toevoegen", + "EDIT_BEAN": "Bewerken boon", + "ADD_PREPARATION": "Bereidingsmethode toevoegen", + "EDIT_PREPARATION": "Bewerk bereidingsmethode", + "ADD_MILL": "Voeg maler toe", + "EDIT_MILL": "Maler bewerken", + "USE_FILTER": "Filter toepassen", + "RESET_FILTER": "Filter resetten", + "COFFEE_GRAMS_GRINDED": "Gram gemalen", + "BEANS_USED": "Bonen geconsumeerd", + "BREW_HEADER_BEFORE_BREW": "Voor het brouwen", + "BREW_HEADER_WHILE_BREW": "Tijdens het brouwen", + "BREW_HEADER_AFTER_BREW": "Na het brouwen", + "BREW_HEADER_CUPPING": "Proeven", + "BEANS_CONSUMED": "Archieveer", + "NAV_MANAGE_PARAMETERS": "Parameters beheren", + "NAV_SORT_PARAMETERS": "Sorteer parameters", + "NAV_DEFAULT_PARAMETERS": "Standaardparameters definiëren", + "PAGE_SORT_PARAMETERS_DESCRIPTION": "Versleep parameters om te bepalen in welke volgorde ze worden weergegeven.", + "PAGE_MANAGE_PARAMETERS_DESCRIPTION": "Geef aan welke parameters moeten worden weergegeven bij het bewerken van brouwinformatie.", + "PAGE_DEFAULT_PARAMETERS_DESCRIPTION": "Markeer welke parameters standaard moeten worden ingesteld op de laatst gebruikte waarde.", + "SORT_PARAMETERS_BEFORE": "Voor het brouwen", + "SORT_PARAMETERS_MEANWHILE": "Tijdens het brouwen", + "SORT_PARAMETERS_AFTER": "Na het brouwen", + "MORE_INFORMATION": "Meer informatie", + "UNDERSTOOD": "Begrepen", + "WELCOME_PAGE_ACTIVATE_ANALYTICS_TITLE": "Analyse en tracking", + "WELCOME_PAGE_ACTIVATE_ANALYTICS_DESCRIPTION": "We willen de app, de website en onze toekomstige diensten voor u voortdurend verbeteren. Om dit te doen, moeten we wat gegevens bijhouden over hoe u de app en de functies ervan gebruikt. Maar we beloven dat we nooit persoonlijke gegevens zullen bijhouden. Om deze beloften waar te maken, gebruiken we Matomo, een open source service gericht op gegevensbeveiliging en privacy die op onze eigen server wordt gehost - dit zorgt ervoor dat alleen wij eigenaar zijn van de gegevens. Onze website biedt alle informatie over de parameters die we bijhouden en bovendien kunt u de broncode bekijken die 100% open source is. Heeft u vragen? Neem dan gerust contact met ons op.", + "ANALYTICS_INFORMATION_TITLE": "Analyse en tracking", + "ANALYTICS_INFORMATION_DESCRIPTION": "Zoals u weet, is de veiligheid van uw gegevens en uw privacy onze topprioriteit. Daarom zijn we overgestapt van Google Analytics naar de open source service Matomo, die zich richt op gegevensbeveiliging en privacy. Deze service wordt gehost op onze eigen server. Dit betekent dat wij volledig eigenaar zijn van de gegevens. De bijgehouden parameters zijn niet gewijzigd en we beloven nog steeds dat we nooit persoonlijke gegevens zullen bijhouden. Op onze website vindt u alle informatie over de parameters die we bijhouden. Bovendien kunt u de broncode bekijken, die 100% open source is. Heeft u vragen? Neem dan gerust contact met ons op.", + "ACTIVATE": "Activeren", + "DO_NOT_ACTIVE": "Niet activeren", + "WELCOME_PAGE_BEAN_TITLE": "Boon", + "WELCOME_PAGE_BEAN_DESCRIPTION": "Koffie zetten zonder bonen is een beetje lastig. Voeg je eerste boon toe om te beginnen!", + "WELCOME_PAGE_BEAN_ADD": "Boon toevoegen", + "SKIP": "Overslaan", + "WELCOME_PAGE_PREPARATION_TITLE": "Bereidingswijze", + "WELCOME_PAGE_PREPARATION_DESCRIPTION": "V60, Aeropress, Espresso - er zijn veel manieren om koffie te zetten. Voeg er minstens één toe.", + "WELCOME_PAGE_PREPARATION_ADD": "Bereidingswijze toevoegen", + "WELCOME_PAGE_MILL_TITLE": "Maler", + "WELCOME_PAGE_MILL_DESCRIPTION": "Bijna klaar, maar je hebt iets nodig om je bonen te malen! Voeg minstens één maler toe.", + "WELCOME_PAGE_MILL_ADD": "Voeg Maler toe", + "WELCOME_PAGE_TITLE": "Welkom!", + "WELCOME_PAGE_BEAN_HEADLINE": "Eerste boon", + "WELCOME_PAGE_PREPARATION_HEADLINE": "Bereidingswijze toevoegen", + "WELCOME_PAGE_MILL_HEADLINE": "Eerste Maler", + "WELCOME_PAGE_LETS_START_HEADLINE": "Nu kunnen we beginnen met brouwen!", + "WELCOME_PAGE_LETS_START_TITLE": "Nu kunnen we beginnen met brouwen!", + "WELCOME_PAGE_LETS_START_DESCRIPTION": "Gefeliciteerd, je bent klaar om de beste koffie van je leven te zetten. Veel plezier en verspreid de liefde voor goede koffie!", + "PREPARATION_TYPE": "Bereidingsmethode", + "PREPARATION_TYPE_NAME": "Naam", + "ARCHIVED": "Gearchiveerd", + "PAGE_SETTINGS_SHOW_ARCHIVED_PREPARATIONS": "Toon gearchiveerde bereidingsmethoden", + "PAGE_SETTINGS_SHOW_ARCHIVED_MILLS": "Toon gearchiveerde malers", + "PAGE_MILL_LIST_NO_ARCHIVED_MILL_EXISTING": "Er zijn nog geen malers gearchiveerd.", + "PAGE_PREPARATION_LIST_NO_ARCHIVED_PREPARATION_EXISTING": "Je hebt geen gearchiveerde bereidingswijzen.", + "TOAST_BREW_ADDED_SUCCESSFULLY": "Brouwsel succesvol toegevoegd", + "TOAST_BREW_REPEATED_SUCCESSFULLY": "Brouwsel succesvol herhaald", + "TOAST_BEAN_ADDED_SUCCESSFULLY": "Boon succesvol toegevoegd", + "TOAST_MILL_ADDED_SUCCESSFULLY": "Maler succesvol toegevoegd", + "TOAST_PREPARATION_ADDED_SUCCESSFULLY": "Bereidingswijze succesvol toegevoegd", + "TOAST_WATER_ADDED_SUCCESSFULLY": "Water succesvol toegevoegd", + "TOAST_BREW_DELETED_SUCCESSFULLY": "Brouwsel is verwijderd", + "TOAST_BEAN_DELETED_SUCCESSFULLY": "Boon is verwijderd", + "TOAST_GREEN_BEAN_DELETED_SUCCESSFULLY": "Groene boon is verwijderd", + "TOAST_MILL_DELETED_SUCCESSFULLY": "Maler is verwijderd", + "TOAST_WATER_DELETED_SUCCESSFULLY": "Water is verwijderd", + "TOAST_PREPARATION_DELETED_SUCCESSFULLY": "Bereidingswijze is verwijderd", + "TOAST_BREW_EDITED_SUCCESSFULLY": "Brouwsel is bewerkt", + "TOAST_BEAN_EDITED_SUCCESSFULLY": "Boon is bewerkt", + "TOAST_MILL_EDITED_SUCCESSFULLY": "Maler is bewerkt", + "TOAST_PREPARATION_EDITED_SUCCESSFULLY": "Bereiding is bewerkt", + "TOAST_WATER_EDITED_SUCCESSFULLY": "Water is bewerkt", + "TOAST_BEAN_ARCHIVED_SUCCESSFULLY": "Boon is gearchiveerd", + "TOAST_MILL_ARCHIVED_SUCCESSFULLY": "Maler is gearchiveerd", + "TOAST_PREPARATION_ARCHIVED_SUCCESSFULLY": "Bereidingsmethode is gearchiveerd", + "TOAST_WATER_ARCHIVED_SUCCESSFULLY": "Water is gearchiveerd", + "BEAN_WEIGHT_ALREADY_USED": "{{gramUsed}} g van {{gramTotal}} g ( {{leftOver}} g)", + "PREPARATION_TYPE_CUSTOM_PREPARATION": "Aangepaste bereidingsmethode", + "PREPARATION_TYPE_AEROPRESS": "Aeropress", + "PREPARATION_TYPE_V60": "V60", + "PREPARATION_TYPE_CHEMEX": "Chemex", + "PREPARATION_TYPE_BIALETTI": "Bialetti", + "PREPARATION_TYPE_PORTAFILTER": "Espresso machine", + "PREPARATION_TYPE_KALITA_WAVE": "Kalita Wave", + "PREPARATION_TYPE_FRENCH_PRESS": "French Press", + "PREPARATION_TYPE_SWANNECK": "Swan Neck", + "PREPARATION_TYPE_DRIPPER": "Dripper", + "PREPARATION_TYPE_DELTER_PRESS": "Delter Press", + "PREPARATION_TYPE_COLD_BREW": "Koud brouwsel", + "PREPARATION_TYPE_AEROPRESS_INVERTED": "Aeropress Omgekeerd", + "PREPARATION_TYPE_TURKISH": "Turks", + "PREPARATION_TYPE_BLUE_DRIPPER": "Blue Dripper", + "PREPARATION_TYPE_ADD_CUSTOM": "Aangepaste methode toevoegen", + "PREPARATION_TYPE_GINA": "Gina", + "PREPARATION_TYPE_KONO": "Kono", + "PREPARATION_TYPE_ORIGAMI": "Origami", + "PREPARATION_TYPE_CAFELAT": "Cafelat", + "PREPARATION_TYPE_OREA": "Orea", + "PREPARATION_TYPE_COLD_DRIP": "Cold Drip", + "PREPARATION_TYPE_HAND_LEVER": "Handmatige hendel", + "PREPARATION_TYPE_FLAIR": "Flair", + "PREPARATION_TYPE_APRIL_BREWER": "April Brewer", + "PREPARATION_TYPE_ESPRO_BLOOM": "Espreo Bloom", + "PREPARATION_TYPE_FELLOW_STAGG": "Fellow Stagg", + "PREPARATION_TYPE_HSIAO_50": "Hsiao 50", + "PREPARATION_TYPE_KARLSBADER_KANNE": "Karlsbader", + "PREPARATION_TYPE_MOCCA_MASTER": "Mocca Master", + "PREPARATION_TYPE_SIPHON": "Sifon", + "CHOOSE_BEANS": "Selecteer bonen", + "CHOOSE_BEAN": "Selecteer boon", + "CHOOSE_WATERS": "Selecteer wateren", + "CHOOSE_WATER": "Selecteer water", + "CHOOSE_PREPARATIONS": "Selecteer bereidingsmethoden", + "CHOOSE_PREPARATION": "Selecteer bereidingsmethode", + "CHOOSE_MILLS": "Selecteer molens", + "CHOOSE_MILL": "Selecteer molen", + "BEAN": { + "PLACE_HOLDER": { + "BEAN_DATA_NAME": "Naam toevoegen", + "BEAN_DATA_ROAST_NAME": "Voeg de mate van branding toe", + "BEAN_DATA_ROASTING_DATE": "Wanneer werden de bonen geroosterd?", + "BEAN_DATA_ROASTER": "Wie heeft de bonen geroosterd?", + "BEAN_DATA_VARIETY": "Voeg de koffievariant toe", + "BEAN_DATA_PROCESSING": "Koffieverwerking, bijvoorbeeld gewassen", + "BEAN_DATA_COUNTRY": "Waar komt de boon vandaan?", + "BEAN_DATA_MIX": "Wat is de melange ratio?", + "BEAN_DATA_AROMATICS": "Voeg smaken toe", + "BEAN_DATA_WEIGHT": "Gewicht van de bonen", + "BEAN_DATA_COST": "Hoeveel hebben de bonen gekost?", + "BEAN_DATA_REGION": "Regio toevoegen", + "BEAN_DATA_FARM": "Voeg de boerderij toe", + "BEAN_DATA_FARMER": "Voeg de boer toe", + "BEAN_DATA_ELEVATION": "Op welke hoogte werden de bonen geteeld", + "BEAN_DATA_HARVEST_TIME": "wanneer zijn de bonen geoogst?", + "BEAN_DATA_BUY_DATE": "Wanneer zijn de bonen gekocht?", + "BEAN_DATA_PERCENTAGE": "Voeg het percentage van deze boon in de melange toe", + "BEAN_DATA_CERTIFICATION": "Voeg de bonencertificering toe (bijv. fairtrade, bio)", + "BEAN_DATA_ROASTING_TYPE": "Voeg brand type toe", + "BEAN_DATA_DECAFFEINATED": "Is deze koffie cafeïnevrij", + "BEAN_DATA_URL": "Voeg de website-url toe", + "BEAN_DATA_EAN": "Voeg het EAN- of artikelnummer toe", + "BEAN_DATA_CUPPING_POINTS": "Voeg de cuppingpunten toe", + "BEAN_DATA_PURCHASING_PRICE": "Voeg de aankoopprijs toe", + "BEAN_DATA_FOB_PRICE": "Voeg de FOB-prijs toe", + "NOTES": "Voeg notities toe over deze bonen", + "CHOOSE_DATA_ROASTER": "Kies een brander", + "CHOOSE_DATA_ROASTING_TYPE": "Kies type branding", + "BEAN_DATA_BEST_DATE": "Wanneer is de beste datum om de bonen te gebruiken?", + "BEAN_DATA_OPEN_DATE": "Wanneer is de zak bonen geopend?", + "FROZEN_NOTES": "Zijn er opmerkingen over de bevroren koffie? Bijvoorbeeld in welke vriezer je het hebt bewaard." + } + }, + "PREPARATION": { + "PLACE_HOLDER": { + "PREPARATION_TYPE_NAME": "Voeg een naam toe", + "NOTES": "Voeg notities toe over deze bereidingsmethode" + } + }, + "MILL": { + "PLACE_HOLDER": { + "NAME": "Voeg een naam toe", + "NOTES": "Voeg notities toe over deze grinder" + } + }, + "BREW": { + "PLACE_HOLDER": { + "BREW_DATA_GRIND_SIZE": "Voer de maalinstelling in", + "BREW_DATA_GRIND_WEIGHT": "Vul de hoeveelheid koffie in (gr)", + "BREW_DATA_BREW_TEMPERATURE": "Voer de brouwtemperatuur in", + "BREW_DATA_PREPARATION_METHOD": "Selecteer bereidingsmethode", + "BREW_DATA_BEAN_TYPE": "Selecteer bonen", + "BREW_DATA_MILL": "Selecteer een molen", + "BREW_DATA_MILL_SPEED": "Voer de maalsnelheid in", + "BREW_DATA_MILL_TIMER": "Voer de maal tijd in", + "BREW_DATA_PRESSURE_PROFILE": "Druk\/stroomprofiel, brouwmethodologie", + "BREW_DATA_TEMPERATURE_TIME": "Voer de tijd in die de machine heeft mogen opwarmen", + "BREW_DATA_COFFEE_BLOOMING_TIME": "Hoe lang is de bloom?", + "BREW_DATA_COFFEE_FIRST_DRIP_TIME": "Wanneer verscheen de eerste druppel?", + "BREW_DATA_BREW_QUANTITY": "Hoeveel water heb je gebruikt om te brouwen?", + "BREW_DATA_COFFEE_TYPE": "Voer de koffiestijl in (bijv. ristretto)", + "BREW_DATA_COFFEE_CONCENTRATION": "Voer de koffie concentratie in", + "BREW_DATA_TDS": "Wat was de gemeten hoeveelheid opgeloste vaste stoffen (TDS)?", + "BREW_DATA_NOTES": "Voeg notities toe over dit brouwsel", + "BREW_DATA_BREW_BEVERAGE_QUANTITY": "Voeg totale drank hoeveelheid toe", + "BREW_DATA_PREPARATION_METHOD_TOOL": "Kies je bereiding tools", + "BREW_DATA_WATER": "Kies het gebruikte water", + "BREW_DATA_BEAN_WEIGHT_IN": "Welk gewicht aan bonen heb je gebruikt?" + } + }, + "ROASTED_BEFORE": "Geroosterd voor", + "DAY_OLD": "dag oud", + "DAYS_OLD": "dagen oud", + "BEANS_AMOUNT_USED": "Verbruikt", + "CUPPING_BREW": "Proeven", + "COFFEE_DRUNKEN_QUANTITY": "Koffie dronken", + "IMAGE_DELETED": "Afbeelding is verwijderd", + "IMAGE_NOT_DELETED": "Afbeelding kon niet worden verwijderd", + "EXTERNAL_STORAGE_NOT_SUPPORTED": "Sorry, externe opslag wordt niet ondersteund", + "BEANS_ARCHIVED": "Gearchiveerd", + "TAB_ARCHIVE": "Archief", + "TODAY": "Vandaag", + "PLEASE_WAIT": "Even geduld aub...", + "PREPARATION_STYLE_POUR_OVER": "Pourover", + "PREPARATION_STYLE_ESPRESSO": "Espresso", + "PREPARATION_STYLE_FULL_IMMERSION": "Immersie", + "PREPARATION_STYLE_PERCOLATION": "Percolatie", + "PREPARATION_TYPE_STYLE": "Bereidingsstijl", + "PAGE_SETTINGS_FAST_REPEAT_DESCRIPTION": "Activeert een nieuw menu-item - hiermee kun je een brouwsel kopieren.", + "PAGE_SETTINGS_TRACK_BREW_COORDINATES_DESCRIPTION": "Sla geolocatie gegevens op voor elk brouwsel.", + "PAGE_SETTINGS_TRACK_CAFFEINE_CONSUMPTION_DESCRIPTION": "Bewaar de hoeveelheid geconsumeerde cafeïne", + "UPDATE_TITLE": "Wat is er nieuw?", + "NEXT": "Volgende", + "CUSTOM_PARAMETERS": "Parameters aanpassen", + "CUSTOM_DEFAULT_PARAMETERS": "Standaard", + "CUSTOM_MANAGE_PARAMETERS": "Beheer", + "CUSTOM_SORT_PARAMETERS": "Sorteer", + "BREW_PARAMETER_CUSTOMIZE_TITLE": "Aangepaste parameters voor elke bereidingsmethode", + "BREW_PARAMETER_CUSTOMIZE_DESCRIPTION": "Wilt u voor elke bereidingsmethode aangepaste parameters kiezen? Navigeer naar \"Methoden\", open het menu van de specifieke bereidingsmethode en kies \"Parameters aanpassen\". Daar kunt u kiezen welke parameters voor deze bereiding worden gebruikt!", + "BREW_DATA_BREW_QUANTITY_TOOLTIP": "Hoeveelheid water (niet bruikbaar voor espresso)", + "BREW_DATA_COFFEE_FIRST_DRIP_TIME_TOOLTIP": "Eerste druppel tijd van de koffie (alleen espresso)", + "BREW_DATA_PREPARATION_METHOD_TOOLTIP": "Voorbereiding (alleen aanpasbaar indien actief)", + "PAGE_SETTINGS_GENERAL": "Algemene instellingen", + "EDIT_PREPARATION_CUSTOM_PARAMETERS": "Parameters aanpassen", + "ENABLE_PREPARATION_CUSTOM_PARAMETERS": "Aangepaste parameters gebruiken", + "BEAN_ADD_ANOTHER_SORT": "Voeg een andere boon type toe", + "BEAN_SORT": "Boon Type", + "BEAN_SORT_INFORMATION": "Informatie over de variëteit", + "BEAN_SORT_MORE_INFORMATION": "Meer informatie", + "NAVIGATE_TO_PREPARATION_METHODS": "Navigeer naar bereidingsmethoden", + "PREPARATION_TOOLS": "bereidingsmiddelen", + "PREPARATION_TOOLS_INFORMATION": "Voeg verschillende mandjes toe, WDT-gereedschappen voor uw espressomachine; stoffen, papieren of gaasfilters voor uw filterkoffie, enz.", + "PREPARATION_TOOLS_PLACEHOLDER": "Papieren of stoffen filter, VST 20g, 14g mandje, enz.", + "PREPARATION_PARAMETERS_CUSTOMIZED": "Aangepaste parameters", + "BEANS_WEIGHT_AVAILABLE": "Beschikbare bonen", + "SORT_ORDER": "Sorteervolgorde wijzigen", + "ASCENDING": "Oplopend", + "DESCENDING": "Aflopend", + "SORT_AFTER": "Sorteren na", + "BEAN_SORT_NAME_OF_BEAN": "Boon naam", + "BEAN_SORT_ROASTER": "Brander", + "BEAN_SORT_ROASTING_DATE": "Datum van roosteren", + "BEAN_TAB_ROAST_INFORMATION": "Geroosterde informatie", + "BEAN_TAB_GENERAL_INFORMATION": "Algemeen", + "BEAN_TAB_SORT_INFORMATION": "Informatie over de variëteit", + "PAGE_SETTINGS_MANAGE_ARCHIVE": "Archief beheren", + "LAST_USE": "Laatst gebruikt", + "SEARCH": "Zoeken", + "OVERVIEW": "Overzicht", + "BEAN_HEADER_ADDITIONALE_INFORMATION": "Aanvullende informatie", + "THREE_DEE_TOUCH_ACTION_BREW": "Brouw", + "THREE_DEE_TOUCH_ACTION_BEAN": "Boon", + "THREE_DEE_TOUCH_ACTION_PREPARATION": "Bereidingsmethode", + "THREE_DEE_TOUCH_ACTION_MILL": "Koffiemolen", + "PAGE_CREDITS_NOT_EXISTING": "Geen inhoud", + "TIMER_HOUR": "Uren", + "TIMER_MINUTES": "Minuten", + "TIMER_SECONDS": "Seconden", + "EXCEL": { + "BEAN": { + "CREATION_DATE": "Aanmaakdatum", + "ID": "Bonen-ID" + }, + "PREPARATION": { + "CREATION_DATE": "Aanmaakdatum", + "ID": "bereidingsmiddelen" + }, + "GRINDER": { + "CREATION_DATE": "Aanmaakdatum", + "ID": "Molen-ID" + } + }, + "EXCEL_EXPORT": "Excel-export", + "HEALTH_KIT_QUESTION_TITLE": "cafeïneconsumptie opslaan", + "HEALTH_KIT_QUESTION_MESSAGE": "Na activering wordt de geschatte cafeïne van elk brouwsel automatisch opgeslagen in Apple Health.", + "NAV_ROASTING_SECTION": "Bonen roosteren", + "ROASTING_SECTION": { + "NAV_GREEN_BEANS": "Groene bonen", + "NAV_ROASTING_MACHINE": "Roostermachine", + "ROASTING_MACHINE": { + "TOTAL_ROAST_QUANTITY": "Totaalgewicht geroosterd", + "TOTAL_ROAST_COUNT": "Aantal brandingen" + }, + "GREEN_BEAN": { + "ADD": "Toevoegen", + "EDIT": "Bewerking", + "DETAIL": "Groene bonen details", + "ROASTABLE": "Roosterbaar", + "NO_ROASTS_YET": "Nog geen gebrande bonen" + }, + "BEAN": { + "DROP_TEMPERATURE": "Eindtemperatuur van de gebrande boon", + "ROAST_LENGTH": "brand lengte", + "ROASTER_MACHINE": "Roostermachine", + "GREEN_BEAN_WEIGHT": "Gewicht van groene bonen", + "OUTSIDE_TEMPERATURE": "Omgevingstemperatuur", + "HUMIDITY": "Vochtigheid", + "FIRST_CRACK_MINUTE": "eerst kraak minuut", + "FIRST_CRACK_TEMPERATURE": "eerste kraak temperatuur", + "SECOND_CRACK_MINUTE": "Tweede kraak minuut", + "SECOND_CRACK_TEMPERATURE": "tweede kraak temperatuur", + "PLACE_HOLDER": { + "DROP_TEMPERATURE": "Eindtemperatuur van de gebrande boon", + "ROAST_LENGTH": "brand lengte", + "ROASTER_MACHINE": "Roostermachine", + "GREEN_BEAN_WEIGHT": "Gewicht van groene bonen", + "OUTSIDE_TEMPERATURE": "Omgevingstemperatuur", + "HUMIDITY": "Vochtigheid", + "FIRST_CRACK_MINUTE": "Eerste kraak minuut", + "FIRST_CRACK_TEMPERATURE": "Eerste kraak temperatuur", + "SECOND_CRACK_TEMPERATURE": "Tweede kraak temperatuur", + "SECOND_CRACK_MINUTE": "Tweede kraak minuut" + } + } + }, + "PAGE_SETTINGS_MANAGE_SECTIONS": "Meer secties", + "PAGE_SETTINGS_SHOW_ROASTING_SECTION": "Activeer brandersectie", + "PAGE_SETTINGS_SHOW_WATER_SECTION": "Activeer water sectie", + "PAGE_SETTINGS_SHOW_CUPPING_SECTION": "Activeer cupping-sectie", + "BEAN_DATA_BUY_DATE": "Aankoopdatum", + "BEAN_SORT_CREATION_DATE": "Aanmaakdatum", + "BEAN_SORT_PURCHASE_DATE": "Aankoopdatum", + "BEAN_ROAST_COUNT": "Roost aantal", + "TRANSFER_ROAST": "Geroosterd", + "BEAN_TAB_LINKED_ROASTS": "Geroosterd", + "BEAN_DATA_WEIGHT_AFTER_ROASTING": "Gewicht na het roosteren", + "TOAST_GREEN_BEAN_ADDED_SUCCESSFULLY": "Groene bonen toegevoegd", + "TOAST_GREEN_BEAN_EDITED_SUCCESSFULLY": "Groene boon bewerkt", + "TOAST_GREEN_BEAN_ARCHIVED_SUCCESSFULLY": "Groene bonen gearchiveerd", + "TOAST_ROASTING_MACHINE_ADDED_SUCCESSFULLY": "Roostermachine toegevoegd", + "TOAST_ROASTING_MACHINE_EDITED_SUCCESSFULLY": "Brandmachine bewerkt", + "TOAST_ROASTING_MACHINE_ARCHIVED_SUCCESSFULLY": "Brander gearchiveerd", + "DELETE_ROASTING_MACHINE_QUESTION": "Brander verwijderen? Alle gebrande bonen waarnaar verwezen wordt, worden bijgewerkt en niet verwijderd.", + "TOAST_ROASTING_MACHINE_DELETED_SUCCESSFULLY": "Brander verwijderd", + "EDIT_ROASTING_MACHINE": "Bewerk", + "DETAIL_ROASTING_MACHINE": "Details van de brander", + "DELETE_WATER_QUESTION": "Water verwijderen? Alle gerefereerde brouwsels worden bijgewerkt en niet verwijderd", + "ROASTING_MACHINE": { + "PLACE_HOLDER": { + "NAME": "Voeg een naam toe", + "NOTES": "Opmerkingen toevoegen voor deze brander" + } + }, + "NAV_ROASTING_MACHINE": "koffierooster machines", + "PAGE_ROASTING_MACHINE_LIST_NO_MACHINES_EXISTING": "U hebt geen koffiebrander toegevoegd", + "PAGE_ROASTING_MACHINE_LIST_NO_ARCHIVED_MACHINES_EXISTING": "je hebt geen koffiebrander gearchiveerd", + "CHOOSE_ROASTING_MACHINES": "koffiebrander", + "CHOOSE_ROASTING_MACHINE": "koffiebrander", + "POPOVER_BREWS_OPTION_TOGGLE_FAVOURITE": "Favoriet", + "TOAST_BREW_FAVOURITE_ADDED": "Favoriet toegevoegd", + "TOAST_BREW_FAVOURITE_REMOVED": "Favoriet verwijderd", + "BREW_FILTER_JUST_FAVOURITE": "Favorieten", + "STATISTICS_PREPARATION_USAGES": "Bereidingsmethode gebruik", + "STATISTICS_PREPARATION_TIMELINE_USAGES": "Gebruiksgeschiedenis bereidingsmethode", + "STATISTICS_GRINDER_TIMELINE_USAGES": "Gebruiksgeschiedenis van de Maler", + "ACCEPT": "Accepteer", + "STATISTIC_TAB_GENERAL": "Algemeen", + "STATISTIC_TAB_BREWS": "Brouwsels", + "STATISTIC_TAB_BEANS": "Bonen", + "STATISTIC_TAB_PREPARATIONS": "Voorbereidingen", + "STATISTIC_TAB_GRINDERS": "Malers", + "PAGE_STATISTICS_BREW_PER_DAYPROCESSES": "Brouwsels per dag", + "PAGE_STATISTICS_BREW_TIME": "Brouwtijd", + "PAGE_STATISTICS_PHOTOS_TAKEN": "Foto's gemaakt", + "PAGE_SETTINGS_IMAGE_QUALITY": "afbeelding kwaliteit", + "PAGE_SETTINGS_IMAGE_QUALITY_TOOLTIP": "Bepaal in welke kwaliteit uw afbeeldingen moeten worden opgeslagen. Dit kan uw dataverbruik verlagen.", + "PAGE_SETTINGS_BREW_RATING": "Brouw beoordeling", + "PAGE_SETTINGS_BREW_RATING_TOOLTIP": "Is de standaard '-1 tot 5' niet de juiste beoordeling voor u? U kunt in plaats daarvan een '-1 tot 100' schaal gebruiken", + "UPDATE_ENTRY_OF": "Update item {{index}} van {{count}}", + "WEBSITE": "Website", + "SHARE": "Deel", + "ANDROID_FILE_ACCESS_NEEDED_TITLE": "Toegang tot gedeeld bestand vereist", + "ANDROID_FILE_ACCESS_NEEDED_DESCRIPTION": "Om de app volledig te laten werken, vragen we u om bestandstoegang te autoriseren. Anders ontstaan er problemen bij het gebruik van de app. Dit is specifiek nodig voor het automatische back-upsysteem.", + "COULD_NOT_ACCESS_FILE": "We konden het gekozen bestand niet openen", + "WRONG_FILE_FORMAT": "U hebt een niet-ondersteund bestandsformaat gekozen", + "SCAN_BEAN": "Scan pakket", + "CLEAR": "leeg maken", + "BEAN_LOOKS_LIKE_CONSUMED": "Het lijkt erop dat deze bonen op zijn. Wil je ze archiveren?", + "CUPPING_1": "Fruit", + "CUPPING_2": "Citrus", + "CUPPING_3": "Citroen & limonade", + "CUPPING_4": "Limoen", + "CUPPING_5": "Grapefruit", + "CUPPING_6": "Clementine", + "CUPPING_7": "Mandarijn", + "CUPPING_8": "mandarijn sinaasappel", + "CUPPING_9": "sinasappel", + "CUPPING_10": "Appel\/peer", + "CUPPING_11": "Groene appel", + "CUPPING_12": "Rode appel", + "CUPPING_13": "Meloen", + "CUPPING_14": "Watermeloen", + "CUPPING_15": "Honingdauw meloen", + "CUPPING_16": "Cantaloupe meloen", + "CUPPING_17": "Druif", + "CUPPING_18": "Witte druif", + "CUPPING_19": "Groene druif", + "CUPPING_20": "Rode druif", + "CUPPING_21": "Concord-druif", + "CUPPING_22": "Tropisch fruit", + "CUPPING_23": "Litchi", + "CUPPING_24": "Stervrucht", + "CUPPING_25": "Tamarinde", + "CUPPING_26": "Passievrucht", + "CUPPING_27": "Ananas", + "CUPPING_28": "Mango", + "CUPPING_29": "Papaja", + "CUPPING_30": "Kiwi", + "CUPPING_31": "Banaan", + "CUPPING_32": "Kokosnoot", + "CUPPING_33": "Steenvrucht", + "CUPPING_34": "Perzik", + "CUPPING_35": "Nectarine", + "CUPPING_36": "Abrikoos", + "CUPPING_37": "Pruim", + "CUPPING_38": "Kers", + "CUPPING_39": "Zwarte kers", + "CUPPING_40": "Bes", + "CUPPING_41": "Cranberry", + "CUPPING_42": "Framboos", + "CUPPING_43": "Aardbei", + "CUPPING_44": "Bosbes", + "CUPPING_45": "Rode bes", + "CUPPING_46": "Zwarte bes", + "CUPPING_47": "Gedroogd fruit", + "CUPPING_48": "Gouden rozijn", + "CUPPING_49": "Rozijn", + "CUPPING_50": "Gedroogde vijg", + "CUPPING_51": "Gedroogde dadels", + "CUPPING_52": "Gedroogde Pruim", + "CUPPING_53": "Zoet & gebrand", + "CUPPING_54": "Chocolade", + "CUPPING_55": "Cacaonibs", + "CUPPING_56": "Donkere chocolade", + "CUPPING_57": "Bakkers chocolade", + "CUPPING_58": "Bitterzoete chocolade", + "CUPPING_59": "Cacaopoeder", + "CUPPING_60": "Melkchocolade", + "CUPPING_61": "Noot", + "CUPPING_62": "Walnoot", + "CUPPING_63": "Pinda", + "CUPPING_64": "Cashew", + "CUPPING_65": "Pecannoot", + "CUPPING_66": "Hazelnoot", + "CUPPING_67": "Amandel", + "CUPPING_68": "Graan & Graanproducten", + "CUPPING_69": "Zoet broodgebak", + "CUPPING_70": "Granola", + "CUPPING_71": "Graham-cracker", + "CUPPING_72": "Rogge", + "CUPPING_73": "Tarwe", + "CUPPING_74": "Gerst", + "CUPPING_75": "Vers brood", + "CUPPING_76": "Zoet & Suikerachtig", + "CUPPING_77": "Vanille", + "CUPPING_78": "Noga", + "CUPPING_79": "Honing", + "CUPPING_80": "Boter", + "CUPPING_81": "Room", + "CUPPING_82": "Marshmallow", + "CUPPING_83": "Rietsuiker", + "CUPPING_84": "Bruine suiker", + "CUPPING_85": "Karamel", + "CUPPING_86": "Ahornsiroop", + "CUPPING_87": "Melasse", + "CUPPING_88": "Cola", + "CUPPING_89": "Geroosterd", + "CUPPING_90": "Toast", + "CUPPING_91": "Verbrande suiker", + "CUPPING_92": "Rokerig", + "CUPPING_93": "Koolstof", + "CUPPING_94": "Plantaardig, hartig en kruidig", + "CUPPING_95": "Kruiden", + "CUPPING_96": "Zwarte peper", + "CUPPING_97": "Witte peper", + "CUPPING_98": "Kaneel", + "CUPPING_99": "Koriander", + "CUPPING_100": "Gember", + "CUPPING_101": "Nootmuskaat", + "CUPPING_102": "Kerrie", + "CUPPING_103": "Drop-anijs", + "CUPPING_104": "Kruidnagel", + "CUPPING_105": "Hartig", + "CUPPING_106": "Leerachtig", + "CUPPING_107": "Vleesachtig", + "CUPPING_108": "Sojasaus", + "CUPPING_109": "Zongedroogde tomaat", + "CUPPING_110": "Tomaat", + "CUPPING_111": "Plantaardig aards kruid", + "CUPPING_112": "Grond", + "CUPPING_113": "Vers hout", + "CUPPING_114": "Ceder", + "CUPPING_115": "Tabak", + "CUPPING_116": "Hooi \/ stro", + "CUPPING_117": "Bladgroenten", + "CUPPING_118": "Olijf", + "CUPPING_119": "Groene peper", + "CUPPING_120": "Pompoen", + "CUPPING_121": "Paddestoel", + "CUPPING_122": "Zoete erwt", + "CUPPING_123": "Sneeuwerwt", + "CUPPING_124": "Grassig", + "CUPPING_125": "Dille", + "CUPPING_126": "Salie", + "CUPPING_127": "Munt", + "CUPPING_128": "Groene thee", + "CUPPING_129": "Zwarte thee", + "CUPPING_130": "Hop", + "CUPPING_131": "Bergamot", + "CUPPING_132": "Bloemrijk", + "CUPPING_133": "Bloemen", + "CUPPING_134": "Hibiscus", + "CUPPING_135": "Rozenbottels", + "CUPPING_136": "Lavendel", + "CUPPING_137": "Magnolia", + "CUPPING_138": "Jasmijn kamperfoelie", + "CUPPING_139": "Oranjebloesem", + "CUPPING_140": "Citroengras", + "WATER_SECTION": { + "NAV_WATER": "Water", + "YOU_GOT_NO_ARCHIVED_WATER": "Je hebt nog geen water gearchiveerd", + "YOU_GOT_NO_WATER": "Je hebt nog geen water toegevoegd", + "CATEGORY_INFORMATION": "Water informatie", + "CATEGORY_GENERAL": "Algemeen", + "WATER_BOTTLE_EXPLANATION": "Waterflessen vermelden de concentratie meestal in eenheden van ppm = mg\/L", + "USED_TIMES": "Aantal keren gebruikt", + "AMOUNT": "Gebruikte hoeveelheid", + "WATER": { + "GENERAL_HARDNESS": "Algemene hardheid (GH)", + "TOTAL_ALKALINITY": "Totale alkaliteit (KH)", + "CALCIUM": "Kalium (Ca)", + "MAGNESIUM": "Magnesium (Mg)", + "SODIUM": "Natrium (Na)", + "TDS": "Totaal opgeloste vaste stoffen (TDS)", + "UNITS": "Eenheden", + "PLACE_HOLDER": { + "GENERAL_HARDNESS": "Algemene hardheid", + "TOTAL_ALKALINITY": "Totale alkaliteit", + "CALCIUM": "Kalium (Ca)", + "MAGNESIUM": "Magnesium (Mg)", + "SODIUM": "Natrium (Na)", + "TDS": "Totaal opgeloste vaste stoffen (TDS)", + "NAME": "Voeg waternaam toe", + "NOTES": "Voeg wat notities toe voor je water", + "POTASSIUM": "Kalium (K)", + "CHLORIDE": "Chloride (Cl)", + "SULFATE": "Sulfaat (SO4)" + }, + "WATER_UNIT": { + "UNKNOWN": "Onbekend", + "PPM": "ppm als CaCO3", + "MG_L": "mg\/L", + "MMOL_L": "mmol\/L", + "DH": "°dH" + }, + "POTASSIUM": "Kalium (K)", + "CHLORIDE": "Chloride (Cl)", + "SULFATE": "Sulfaat (SO4)" + } + }, + "BREW_BRIX_CALCULATION": "Graden Brix", + "SET_TDS": "TDS instellen", + "TOTAL_WEIGHT": "Totaal gewicht", + "CALCULATED_WEIGHT": "Berekend gewicht", + "SET_WEIGHT": "Gewicht instellen", + "ADD_FLAVORS_AROMAS_TITLE": "Aroma's \/ Smaken", + "CUSTOM_FLAVORS_AROMAS": "Individueel aroma", + "CUSTOM_FLAVORS_AROMAS_PLACEHOLDER": "Voeg je individuele aroma toe", + "PREDEFINED_FLAVORS_AROMAS": "Veel voorkomende aroma's", + "ADD_AROMA_FLAVOR": "Aroma's\/smaken toevoegen", + "BEAN_WEIGHT_IN_PLACEHOLDER": "Bonen uit de verpakking gehaald", + "VESSEL_PLACEHOLDER": "Naam van het Serveerkan en het leeggewicht ervan", + "GRIND_WEIGHT_PLACEHOLDER": "Gewicht van de gemalen bonen die voor het brouwen worden gebruikt", + "PRESET_BREW_TITLE": "Gebruik laatste brouwsel als voorinstelling", + "CUPPING_BREW_TAB_AROMA": "Aroma", + "CUPPING_BREW_TAB_TASTING": "Systematische cupping", + "WATER_PLACEHOLDER": "Activeer het watergedeelte in het instellingenmenu voor volledige functionaliteit", + "PAGE_SETTINGS_SCALES": "Weegschalen", + "CONNECT": "Verbinden", + "DISCONNECT": "Loskoppelen", + "SCALE": { + "BLUETOOTH_SCAN_RUNNING": "Zoeken naar weegschaal tot 60s", + "BLUETOOTH_NOT_ENABLED": "Bluetooth niet geactiveerd, activeer het om het goed te laten werken", + "CONNECTION_NOT_ESTABLISHED": "Weegschaal niet gevonden, of verbinding kon niet tot stand worden gebracht", + "CONNECTED_SUCCESSFULLY": "Weegschaal verbonden", + "DISCONNECTED_SUCCESSFULLY": "Weegschaal losgekoppeld", + "DISCONNECTED_UNPLANNED": "Weegschaal onverwachts losgekoppeld", + "REQUEST_PERMISSION": { + "LOCATION": "Om Bluetooth-weegschalen te vinden, heeft de app toegang nodig tot de locatie.", + "BLUETOOTH": "Om bluetooth-weegschalen te vinden, heeft de app toegang tot bluetooth nodig" + }, + "INFORMATION_DESCRIPTION": "Ondersteunde weegschalen zijn: Decent Scale, Acaia, Felicita, Hiroia Jimmy, Skale 2, DiFluid Microbalance, Smartchef Scale, Blackcoffee.io, Bookoo Mini Scale en Eureka Precisa. Let op: Als de Eureka Precisa een negatieve waarde ontvangt, stopt de timer" + }, + "QR": { + "WRONG_QRCODE_DESCRIPTION": "Ongeldige QR-code of onbekende inhoud", + "WRONG_QRCODE_TITLE": "Fout", + "WRONG_LINK_DESCRIPTION": "Ongeldige QR-link", + "WRONG_LINK_TITLE": "Fout", + "SERVER": { + "ERROR_OCCURED": "Er is een fout opgetreden. De QR-code kon niet worden gelezen. Probeer het opnieuw.", + "BEAN_NOT_APPROVED": "Boon is nog niet goedgekeurd, probeer het later nog eens" + }, + "BEAN_SUCCESSFULLY_SCANNED": "Bean succesvol gescand", + "BEAN_SUCCESSFULLY_REFRESHED": "Boon succesvol bijgewerkt", + "IMAGES_GETTING_DOWNLOADED": "Afbeeldingen downloaden" + }, + "RETRY_CONNECT": "Verbinding opnieuw proberen", + "SMART_SCALE_STAY_CONNECTED_ON_APP_MINIMIZE": "Houd de weegschaal verbonden, zelfs als de app op de achtergrond draait", + "BREW_FLOW_WEIGHT": "Gewicht", + "BREW_FLOW_WEIGHT_PER_SECOND": "Stroom (afgevlakt)", + "ROAST_TYPE_UNKNOWN": "onbekend", + "ROAST_TYPE_CINNAMON_ROAST": "Cinnamon Branding", + "ROAST_TYPE_AMERICAN_ROAST": "Amerikaans Branding", + "ROAST_TYPE_NEW_ENGLAND_ROAST": "Nieuw-Engeland Branding", + "ROAST_TYPE_HALF_CITY_ROAST": "Half City Branding", + "ROAST_TYPE_MODERATE_LIGHT_ROAST": "Matig-lichte branding", + "ROAST_TYPE_CITY_ROAST": "City Branding", + "ROAST_TYPE_CITY_PLUS_ROAST": "City+ Branding", + "ROAST_TYPE_FULL_CITY_ROAST": "Full City Branding", + "ROAST_TYPE_FULL_CITY_PLUS_ROAST": "Full City + Branding", + "ROAST_TYPE_ITALIAN_ROAST": "Ialiaanse Branding", + "ROAST_TYPE_VIEANNA_ROAST": "Weense Branding", + "ROAST_TYPE_FRENCH_ROAST": "Franse Branding", + "ROAST_TYPE_CUSTOM_ROAST": "Aangepast", + "BEAN_MIX_UNKNOWN": "onbekend", + "BEAN_MIX_SINGLE_ORIGIN": "Single Origin", + "BEAN_MIX_BLEND": "Melange", + "BEAN_ROASTING_TYPE_FILTER": "Filter", + "BEAN_ROASTING_TYPE_ESPRESSO": "Espresso", + "BEAN_ROASTING_TYPE_OMNI": "Omni", + "BEAN_ROASTING_TYPE_UNKNOWN": "Onbekend", + "SMART_SCALE_LOG": "Activeer logbestanden voor slimme weegschaal (alleen voor foutopsporing)", + "TOAST_PREPARATION_TOOL_EDITED_SUCCESSFULLY": "Voorbereidingstool bewerkt", + "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE": "Bluetooth-weegschaal", + "PAGE_SETTINGS_TAB_GENERAL": "Algemeen", + "SMART_SCALE_TARE_ON_BREW": "Tarraweegschaal op nieuw brouwsel", + "SMART_SCALE_TARE_ON_START_TIMER": "Tarra weegschaal bij het starten van de timer", + "PAGE_SETTINGS_BREW_RATING_STEPS": "Beoordelingsstappen", + "BREW_AVG_FLOW_WEIGHT_PER_SECOND": "Ø Stroom", + "CUSTOM_LIST_VIEW_PARAMETERS": "Parameters voor lijstweergave", + "NAV_LIST_VIEW_CUSTOM_PARAMETERS": "Parameters voor lijstweergave", + "PAGE_LIST_VIEW_CUSTOM_PARAMETERS_DESCRIPTION": "Bepaal welke parameters moeten worden weergegeven op de tegels van de lijstweergave", + "BREW_DATA_VESSEL_NAME_WEIGHT": "Serveerkan Naam\/Gewicht", + "IGNORE_NEGATIVE_VALUES": "Negeer negatieve gewichtswaarden", + "IGNORE_ANOMALY_VALUES": "Negeer afwijkende waarden", + "IGNORE_ANOMALY_VALUES_TOOLTIP": "Bijvoorbeeld: Een kopje rond bewegen op de weegschaal", + "TOAST_BEAN_FAVOURITE_ADDED": "Favoriet toegevoegd", + "TOAST_BEAN_FAVOURITE_REMOVED": "Favoriet verwijderd", + "QR_CODE_SCANNER_INFORMATION_TITLE": "QR-code", + "QR_CODE_SCANNER_INFORMATION_DESCRIPTION": "Alle gescande boneninformatie komt rechtstreeks van de branderij. Mocht u onjuiste of misleidende informatie aantreffen, laat het mij dan weten via e-mail: info@beanconqueror.com.", + "DONT_SHOW_AGAIN": "Niet meer weergeven", + "ARCHIVED_TOOLS": "Gearchiveerd gereedschap", + "UNARCHIVE": "Herstel", + "PAGE_SETTINGS_HIDE_ARCHIVED_BREWS_DASHBOARD": "Toon gearchiveerde brouwsels op dashboard", + "PAGE_SETTINGS_HIDE_ARCHIVED_BREWS_DASHBOARD_DESCRIPTION": "Moeten gearchiveerde brouwsels op de startpagina worden weergegeven?", + "COPY": "Kopiëren", + "TOAST_PREPARATION_METHOD_REPEATED_SUCCESSFULLY": "Bereidingsmethode succesvol gekopieerd", + "PREPARATION_TYPE_CAFEC_FLOWER": "Cafec Flower", + "PREPARATION_TYPE_DECEMBER_DRIPPER": "December Dripper", + "PREPARATION_TYPE_DECENT_ESPRESSO": "Decent Espresso", + "PREPARATION_TYPE_HARIO_SWITCH": "Hario Switch", + "PREPARATION_TYPE_HARIO_WOODNECK": "Hario Woodneck", + "PREPARATION_TYPE_RATIO_SIX_COFFEE_BREWER": "Ratio Six-koffiezetapparaat", + "PREPARATION_TYPE_ROK": "ROK", + "PREPARATION_TYPE_TORNADO_DUO": "Tornado-duo", + "PREPARATION_TYPE_TRICOLATE": "Tricolate", + "QR_CODE_REFRESH_DATA_MESSAGE": "Alle informatie voor deze boon wordt overschreven, doorgaan?", + "POPOVER_QR_CODE_REFRESH": "Gegevens opnieuw laden", + "BREW_FLOW_WEIGHT_REALTIME": "Stroom (realtime)", + "SMART_SCALE_STOP_TIMER_ON_BREW": "Stop de timer van de weegschaal bij een nieuw brouwsel", + "SMART_SCALE_RESET_TIMER_ON_BREW": "Reset de weegschaaltimer bij een nieuw brouwsel", + "BREW_PRESSURE_FLOW": "Druk", + "BREW_TEMPERATURE_REALTIME": "Temperatuur", + "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_SHOW_GRAPHS_FILTER": "Grafieken weergeven voor filter", + "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_SHOW_GRAPHS_ESPRESSO": "Toon grafieken voor espresso", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE": "Drukapparaat", + "PAGE_SETTINGS_TAB_BLUETOOTH_TEMPERATURE": "Temperatuur apparaat", + "PRESSURE_LOG": "Activeer logbestanden voor drukapparaat", + "PRESSURE_THRESHOLD_ACTIVE": "Starttimer met vooraf gedefinieerde druk", + "PRESSURE_THRESHOLD_BAR": "Drempelwaarde druk", + "TIMER_MILLISECONDS": "MS", + "PAGE_SETTINGS_BREW_ENABLE_MILLISECONDS": "Milliseconden?", + "PAGE_SETTINGS_BREW_ENABLE_MILLISECONDS_DESCRIPTION": "Gebruik milliseconden om uw brouwsels nog nauwkeuriger te analyseren", + "PAGE_SETTINGS_BEAN_RATING": "Bonen beoordeling", + "PAGE_SETTINGS_BEAN_RATING_TOOLTIP": "Is de standaard '0 tot 5' niet de juiste beoordeling voor u? U kunt in plaats daarvan een '0 tot 100'-schaal gebruiken", + "PAGE_SETTINGS_BEAN_RATING_STEPS": "Stappen voor het beoordelen van bonen", + "COFFEE_GRAMS_BREWED": "grammen gebrouwen", + "SMART_SCALE_MAXIMIZE_ON_START_TIMER": "Maximaliseer realtime-grafiek bij het starten van de timer", + "PRESSURE": { + "CONNECTION_NOT_ESTABLISHED": "Drukapparaat niet gevonden, of verbinding kon niet tot stand worden gebracht", + "CONNECTED_SUCCESSFULLY": "Drukapparaat aangesloten", + "DISCONNECTED_SUCCESSFULLY": "Drukapparaat losgekoppeld", + "DISCONNECTED_UNPLANNED": "Drukapparaat onverwachts losgekoppeld", + "STAY_CONNECTED_ON_APP_MINIMIZE": "Houd het drukapparaat aangesloten, zelfs als de app op de achtergrond draait", + "INFORMATION_DESCRIPTION": "Ondersteunde apparaten zijn: Popsicle, Pressensor, Smart Espresso Profiler, Bookoo Espresso Monitor", + "BLUETOOTH_SCAN_RUNNING": "Zoeken naar drukapparaat gedurende maximaal 60 seconden", + "BLUETOOTH_NOT_ENABLED": "Bluetooth niet geactiveerd, activeer het om het goed te laten werken", + "REQUEST_PERMISSION": { + "LOCATION": "Om drukapparaten te vinden, heeft de app toegang nodig tot de locatie.", + "BLUETOOTH": "Om drukapparaten te vinden, heeft de app toegang tot Bluetooth nodig" + } + }, + "POPOVER_BLUETOOTH_ACTION_RECONNECT_SCALE": "Weegschaal opnieuw verbinden", + "POPOVER_BLUETOOTH_ACTION_RECONNECT_PRESSURE_DEVICE": "Drukapparaat opnieuw aansluiten", + "POPOVER_SHOW_BREWS": "Toon brouwsels", + "LAST_USED_GRIND_SIZE_SETTING": "Laatste maalstand", + "LAST_USED_BEAN": "Laatste boon", + "PAGE_SETTINGS_BREW_MILLISECONDS_DECIMAL_PLACES_DESCRIPTION": "Hoeveel decimalen moeten er worden weergegeven?", + "SMART_SCALE_COMMAND_DELAY": "Opdracht vertraging", + "SMART_SCALE_COMMAND_DELAY_TOOLTIP": "Hoeveel tijd moet er verstrijken tussen elk bluetooth-commando?", + "SUPPORT_ME": "Donatie", + "PAGE_SETTINGS_BREW_DISPLAY_BEAN_IMAGE": "Bonenafbeeldingen op brouwsels weergeven?", + "PAGE_SETTINGS_BREW_DISPLAY_BEAN_IMAGE_DESCRIPTION": "Als er een bonenafbeelding bestaat, wordt deze gebruikt in plaats van de afbeelding van de bereidingsmethode", + "DOWNLOAD_XLSX": "Excel downloaden", + "DOWNLOAD_JSON": "JSON downloaden", + "NAV_BEAN_PARAMS": "Boon Parameters", + "BEAN_DATA_ROAST_NAME_TYPE": "Brand graad", + "ENABLE_BEAN_SORT_INFORMATION": "Variëteit-informatie inschakelen", + "BEAN_SORT_MIX": "Bonenmix", + "PAGE_MANAGE_BEAN_PARAMETERS_DESCRIPTION": "Markeer welke parameters moeten worden weergegeven bij het bewerken van bean-informatie.", + "PAGE_BEAN_LIST_VIEW_CUSTOM_PARAMETERS_DESCRIPTION": "Bepaal de parameters die moeten worden weergegeven in lijstweergaven voor bonen", + "BEAN_DATA_NAME_TOOLTIP": "De naam van de bonen", + "BEAN_DATA_ROASTER_TOOLTIP": "Wie is de brander", + "BEAN_DATA_BUY_DATE_TOOLTIP": "Wanneer heb je de bonen gekocht?", + "BEAN_DATA_ROASTING_DATE_TOOLTIP": "Wat is de branddatum van de bonen?", + "BEAN_DATA_ROASTING_TYPE_TOOLTIP": "Voor welke bereidingswijze is deze boon geroosterd, bijvoorbeeld Filter", + "BEAN_DATA_ROAST_NAME_TOOLTIP": "Wat is de brandingsgraad van de bonen?", + "BEAN_DATA_ROAST_NAME_TYPE_TOOLTIP": "Welk type branding (Cinnamon, New England, etc.)", + "BREW_DATA_RATING_TOOLTIP": "Je waardering", + "BEAN_SORT_MIX_TOOLTIP": "Zijn de bonen een melange?", + "BEAN_DATA_WEIGHT_TOOLTIP": "Hoeveel wegen de bonen?", + "BEAN_DATA_COST_TOOLTIP": "Hoeveel hebben de bonen gekost?", + "BEAN_DATA_AROMATICS_TOOLTIP": "Welke aroma's\/smaken hebben de bonen?", + "BEAN_DATA_CUPPING_POINTS_TOOLTIP": "Hoeveel cupping-punten heeft de boon?", + "BEAN_DATA_DECAFFEINATED_TOOLTIP": "Is het cafeïnevrij?", + "BEAN_DATA_URL_TOOLTIP": "URL naar de winkel", + "BEAN_DATA_EAN_TOOLTIP": "Het EAN\/artikelnummer", + "NOTES_TOOLTIP": "Aantekeningen gemaakt", + "BREW_DATA_ATTACHMENTS_TOOLTIP": "Foto's toevoegen?", + "ENABLE_BEAN_SORT_INFORMATION_TOOLTIP": "Meer informatie over de bonen: waar komen ze vandaan, wanneer zijn ze geoogst, etc.", + "BEAN_DATA_COUNTRY_TOOLTIP": "Uit welk land komen de bonen?", + "BEAN_DATA_REGION_TOOLTIP": "Uit welke regio komen de bonen?", + "BEAN_DATA_FARM_TOOLTIP": "Van welke boerderij komen de bonen?", + "BEAN_DATA_FARMER_TOOLTIP": "Wie verbouwde deze bonen", + "BEAN_DATA_ELEVATION_TOOLTIP": "Op welke hoogte groeiden de bonen?", + "BEAN_DATA_PROCESSING_TOOLTIP": "Hoe zijn de bonen verwerkt (natuurlijk, gewassen, enz.)", + "BEAN_DATA_VARIETY_TOOLTIP": "De Koffieboon Variëteit (SL28 bv.)", + "BEAN_DATA_HARVEST_TIME_TOOLTIP": "In welk jaar\/maand werden deze bonen geoogst?", + "BEAN_DATA_PERCENTAGE_TOOLTIP": "Hoeveel procent van deze bonen zit er in de zak?", + "BEAN_DATA_CERTIFICATION_TOOLTIP": "Welke certificeringen hebben de bonen (bijvoorbeeld fairtrade)", + "BEAN_DATA_PURCHASING_PRICE_TOOLTIP": "Wat was de aankoopprijs van de koffiebrander", + "BEAN_DATA_FOB_PRICE_TOOLTIP": "Wat was de Free-On-Board (FOB) prijs toen de brander het kocht?", + "BEAN_PARAMETER_CUSTOMIZE_TITLE": "Pas de informatie aan die u voor bonen wilt gebruiken", + "BEAN_PARAMETER_CUSTOMIZE_DESCRIPTION": "Er kan veel informatie over de bonen worden ingevoerd of gebruikt. Kies zelf de parameters die u wilt invullen en welke u wilt weergeven", + "BREW_CANT_START_BECAUSE_TIMER_NOT_RESETTED_TITLE": "Timer resetten!", + "BREW_CANT_START_BECAUSE_TIMER_NOT_RESETTED_DESCRIPTION": "Omdat je een Bluetooth-apparaat hebt aangesloten, moet je eerst je timer resetten voordat je kunt beginnen.", + "BREW_FILTER_JUST_CHART_DATA": "Alleen grafieken", + "SCALE_RESET_TRIGGERED_DESCRIPTION": "De bluetooth weegschaal wil zowel de app-timer als de brouwgrafiek resetten, wil je doorgaan?", + "SCALE_RESET_TRIGGERED_TITLE": "Opnieuw instellen?", + "NAV_REPEAT_PARAMETERS": "Herhalingsparameters definiëren", + "PAGE_REPEAT_PARAMETERS_DESCRIPTION": "Markeer welke parameters vooraf moeten worden ingesteld wanneer u een specifieke brouwbeurt herhaalt", + "CUSTOM_REPEAT_PARAMETERS": "Herhalen", + "CUSTOM_REPEAT_PARAMETERS_DESCRIPTION": "Aangepaste herhaling activeren?", + "PAGE_SETTINGS_USE_NUERMIC_KEYBOARD_FOR_GRIND_SIZE": "Numeriek toetsenbord voor maalinstelling?", + "PAGE_SETTINGS_USE_NUERMIC_KEYBOARD_FOR_GRIND_SIZE_DESCRIPTION": "Wilt u een numeriek toetsenbord gebruiken voor de maalinstelling, in plaats van het hele toetsenbord?", + "PREPARATION_DEVICE": { + "TYPE": { + "NONE": "Geen", + "XENIA": "Xenia", + "METICULOUS": "Meticulous" + }, + "URL": "URL-adres", + "CHOOSE_DEVICE": "Kies apparaat", + "CONNECTION": { + "UNSUCCESFULLY": "Er kon geen verbinding met de machine worden gemaakt", + "SUCCESFULLY": "Verbinding met machine was succesvol" + }, + "TYPE_XENIA": { + "TITLE": "Xenia machine", + "PRESS_START_SCRIPT": "Start script bij start", + "FIRST_DRIP_SCRIPT": "Start script bij eerste druppel", + "SCRIPT_AT_WEIGHT": "Gewicht voor scriptuitvoering", + "SCRIPT_LIST_GENERAL_0": "Niets", + "SCRIPT_LIST_GENERAL_1": "Espresso, 25 seconden", + "SCRIPT_LIST_GENERAL_2": "Espresso, eindeloos", + "SCRIPT_LIST_GENERAL_STOP": "Schot stoppen", + "CHOOSE_SCRIPT_AT_WEIGHT": "Kies script voor gewicht bereikt", + "ERROR_NOT_ALL_SCRIPTS_FOUND": "Een of meer scripts konden niet worden gevonden, ze zijn gereset", + "ERROR_CONNECTION_COULD_NOT_BE_ESTABLISHED": "De verbinding met de xenia espressomachine kon niet tot stand worden gebracht. Controleer of u zich op het juiste netwerk (LAN) bevindt, of u het juiste IP-adres hebt ingevoerd, etc.", + "CHECKING_CONNECTION_TO_PORTAFILTER": "Verbinding met Xenia wordt gecontroleerd", + "GRABING_SCRIPTS": "Scripts laden vanuit Xenia", + "BREW_BY_WEIGHT_ACTIVE": "Brouwen op gewicht actief" + }, + "API_VERSION": "Api-versie", + "RESIDUAL_LAG_TIME": "Resterende vertragingstijd", + "RESIDUAL_LAG_TIME_DESCRIPTION": "Stel de tijd in die overeenkomt met het resterende water. Met een naakte portafilter heb je een lagere tijd nodig, met een tuit een hogere. Hoe langzamer uw weegschaal het gewicht rapporteert, hoe meer tijd u nodig heeft", + "TYPE_METICULOUS": { + "TITLE": "Meticulous machine", + "NO_PROFILE": "Geen profiel", + "CHOOSE_PROFILE": "Profiel kiezen", + "SHOT_STARTED": "Brouwen gestart", + "SHOT_ENDED": "Brouwen gestopt" + } + }, + "DEVICE_CONNECTION": "Apparaatverbinding", + "PREPARATION_DEVICE_CONNECTION": "Apparaatverbinding", + "MANUAL_EXPORT_TO_VISUALIZER": "Exporteren naar Visualizer", + "ONLY_FAVOURITES": "Alleen favorieten", + "TEMPERATURE": { + "CONNECTION_NOT_ESTABLISHED": "Temperatuurapparaat niet gevonden, of verbinding kon niet tot stand worden gebracht", + "CONNECTED_SUCCESSFULLY": "Temperatuurapparaat aangesloten", + "DISCONNECTED_SUCCESSFULLY": "Temperatuurapparaat losgekoppeld", + "DISCONNECTED_UNPLANNED": "Temperatuurapparaat onverwachts losgekoppeld", + "STAY_CONNECTED_ON_APP_MINIMIZE": "Houd de thermometer verbonden, zelfs als de app op de achtergrond staat", + "INFORMATION_DESCRIPTION": "Ondersteunde apparaten zijn: ETI Ltd BLE-thermometers (ThermaQ Blue, BlueTherm, enz.), Combustion Inc., Meater (niet Meater+ of Meater 2)", + "BLUETOOTH_SCAN_RUNNING": "Zoeken naar temperatuurapparaat gedurende maximaal 60 seconden", + "BLUETOOTH_NOT_ENABLED": "Bluetooth niet geactiveerd, activeer het om het goed te laten werken", + "REQUEST_PERMISSION": { + "LOCATION": "Om temperatuurmeters te vinden, heeft de app toegang nodig tot de locatie.", + "BLUETOOTH": "Om temperatuurapparaten te vinden, heeft de app toegang tot Bluetooth nodig" + }, + "LOG": "Logbestanden voor temperatuurapparaat activeren", + "THRESHOLD_ACTIVE": "Start timer met vooraf ingestelde temperatuur", + "THRESHOLD_TEMP": "Drempel temperatuur" + }, + "POPOVER_BLUETOOTH_ACTION_RECONNECT_TEMPERATURE_DEVICE": "Temperatuurapparaat opnieuw aansluiten", + "PRESSURE_DEVICE_JUST_VISIBLE_ON_ESPRESSO": "Drukapparaat is alleen bruikbaar bij de bereidingswijze 'Espresso'", + "PRESSURE_MESSAGE_AFTER_CONNECTION": "Bekend gedrag (in analyse): Houd er rekening mee dat het langer kan duren om verbinding te maken nadat u uw telefoon opnieuw hebt opgestart of wanneer u de app langere tijd niet hebt gebruikt. Na deze tijd zou het verbindingsprobleem opgelost moeten zijn.", + "SMART_SCALE_ACAIA_HEARTBEAT_TIMER": "Hartslagtimer - Speciaal voor Acaia Scales", + "SMART_SCALE_ACAIA_HEARTBEAT_TIMER_TOOLTIP": "Alleen gebruikt voor Acaia-weegschalen! - Oudere Acaia-weegschalen hebben een hartslagsignaal nodig. Als u problemen ondervindt, probeer dan de hartslag op een hogere frequentie in te stellen.", + "SHARE_BEAN_URL": "Delen als URL", + "SHARE_BEAN_IMAGE": "Delen als afbeelding", + "SMART_SCALE_ESPRESSO_STOP_ON_NO_WEIGHT_CHANGE": "Espresso - Automatisch stoppen met brouwen", + "SMART_SCALE_ESPRESSO_STOP_ON_NO_WEIGHT_CHANGE_DESCRIPTION": "Deze instelling wordt alleen gebruikt voor brouwsels van het type 'espresso'. Het brouwsel wordt automatisch gestopt wanneer er geen flow wordt gedetecteerd.", + "BREW_DATA_BREW_QUANTITY_TOOLTIP_BREW_RATIO": "De zetverhouding wordt berekend met deze waarde voor alle brouwsels, maar niet voor type 'espresso'.", + "BREW_DATA_BREW_BEVERAGE_QUANTITY_TOOLTIP_BREW_RATIO": "De zetverhouding wordt alleen met deze waarde berekend voor brouwsels van het type 'espresso'", + "SMART_SCALE_DID_NOT_SEND_ANY_WEIGHT_DESCRIPTION": "Het lijkt erop dat de bluetooth weegschaal niet goed is aangesloten. Dit gebeurt meestal op iOS-apparaten met Acaia weegschalen, sluit de weegschaal opnieuw aan en zorg ervoor dat de gewichtstegel wordt bijgewerkt.", + "SMART_SCALE_DID_NOT_SEND_ANY_WEIGHT_TITLE": "Geen gewichtswaarden?", + "SMART_SCALE_ESPRESSO_STOP_ON_NO_WEIGHT_CHANGE_MIN_FLOW_DESCRIPTION": "De stroomsnelheid waarbij het zetten moet worden gestopt. Het zetten wordt pas gestopt als er minimaal 5 seconden zijn verstreken, er 5 gram in de kop zit of als u het gewicht van de gemalen koffie hebt ingevoerd - er is minimaal een zetverhouding van 1:1 bereikt (bijv.: 18 g erin, 18 g eruit). Nadat aan deze voorwaarden is voldaan, wordt de hier ingestelde minimale stroomsnelheid gecontroleerd", + "ONLY_BEST_BREWS": "Alleen de beste brouwsels", + "POPOVER_BEST_BREW": "Beste brouwsel", + "PAGE_SETTINGS_BEST_BREW": "Beste brouwsels activeren", + "PAGE_SETTINGS_BEST_BREW_DESCRIPTION": "Markeer je beste brouwsel voor een specifieke boon. Elke boon kan één beste brouwsel hebben in plaats van meerdere favorieten.", + "PAGE_SETTINGS_TAB_BLUETOOTH_REFRACTOMETER": "Refractometer apparaat", + "REFRACTOMETER": { + "CONNECTION_NOT_ESTABLISHED": "Refractometerapparaat niet gevonden, of verbinding kon niet tot stand worden gebracht", + "CONNECTED_SUCCESSFULLY": "Refractometerapparaat aangesloten", + "DISCONNECTED_SUCCESSFULLY": "Refractometerapparaat losgekoppeld", + "DISCONNECTED_UNPLANNED": "Refractometerapparaat onverwachts losgekoppeld", + "STAY_CONNECTED_ON_APP_MINIMIZE": "Houd de refractometer verbonden, zelfs als de app op de achtergrond draait", + "INFORMATION_DESCRIPTION": "Ondersteunde apparaten zijn: DiFluid R2", + "BLUETOOTH_SCAN_RUNNING": "Zoeken naar refractometer-apparaat gedurende maximaal 60 seconden", + "BLUETOOTH_NOT_ENABLED": "Bluetooth niet geactiveerd, activeer het om het goed te laten werken", + "REQUEST_PERMISSION": { + "LOCATION": "Om refractometerapparaten te vinden, heeft de app toegang nodig tot de locatie.", + "BLUETOOTH": "Om refractometerapparaten te vinden, heeft de app toegang tot Bluetooth nodig" + }, + "LOG": "Activeer logbestanden voor refractometerapparaat", + "READ_END": "Test voltooid - resultaat ontvangen" + }, + "COPIED_TO_CLIPBOARD_SUCCESSFULLY": "Toegevoegd aan klembord", + "COPIED_TO_CLIPBOARD_UNSUCCESSFULLY": "Kan niet worden toegevoegd aan het klembord", + "PAGE_SETTINGS_LANGUAGE_FRENCH": "Frans", + "ANDROID_EXTERNAL_FILE_ACCESS_NOT_POSSIBLE_TITLE": "Gegevens kunnen niet worden opgeslagen vanwege Android-beperkingen", + "ANDROID_EXTERNAL_FILE_ACCESS_NEEDED_DESCRIPTION": "Je Android-telefoon ondersteunt geen externe bestandssystemen, dus je moet het ZIP-bestand downloaden zonder mediabestanden. Ga voor meer informatie naar https:\/\/beanconqueror.com\/faq.", + "PAGE_SETTINGS_SECURITY_CHECK_WHEN_GOING_BACK": "Beveiligingsbericht voor afsluiten?", + "PAGE_SETTINGS_SECURITY_CHECK_WHEN_GOING_BACK_DESCRIPTION": "Controleer of de gegevens zijn gewijzigd bij het toevoegen\/bewerken van bonen of brouwsels; als dat het geval is, wordt er een veiligheidswaarschuwing weergegeven bij het teruggaan.", + "PAGE_BEANS_DISCARD_CONFIRM": "Boon informatie is gewijzigd. Weet je zeker dat je de pagina wilt verlaten zonder op te slaan?", + "PAGE_BREW_DISCARD_CONFIRM": "De brouw informatie is gewijzigd. Weet je zeker dat je de pagina wilt verlaten zonder op te slaan?", + "NO_ENTRIES_FOUND": "Geen vermeldingen gevonden", + "POPOVER_BEANS_OPTION_REPEAT": "Herhaal laatste brouwsel", + "REPEAT_LAST_BREW": "Herhaal laatste brouwsel", + "REPEAT_BEST_BREW": "Herhaal beste brouwsel", + "PAGE_SETTINGS_VISUALIZER_SECTION": "Visualizer", + "VISUALIZER": { + "ACTIVATE": "Visualizer activeren", + "CHOOSE_SERVER": "Server kiezen", + "CONNECTION": { + "UNSUCCESSFULLY": "Er kon geen verbinding tot stand worden gebracht.", + "SUCCESSFULLY": "Verbinding kon tot stand worden gebracht." + }, + "SERVER": { + "VISUALIZER": "Visualizer-server", + "CUSTOM": "Aangepaste server" + }, + "SHOT": { + "UPLOAD_SUCCESSFULLY": "Brouwen geüpload naar visualizer.", + "UPLOAD_UNSUCCESSFULLY": "Brouwen kon niet worden geüpload naar de Visualizer." + }, + "URL": "Server-URL", + "USERNAME": "Gebruikersnaam", + "PASSWORD": "Wachtwoord", + "UPLOAD_AUTOMATIC": "Elke brouwsessie automatisch uploaden?", + "UPLOAD_AUTOMATIC_TOOLTIP": "Zorg ervoor dat u een actieve internetverbinding hebt wanneer u uw brouwsel opslaat.", + "UPLOAD_ALL": "Alle brouwsels uploaden", + "NOT_ALL_SHOTS_UPLOADED": "Niet alle brouwsels konden worden geüpload", + "ALL_SHOTS_UPLOADED": "Alle brouwsels zijn geüpload" + }, + "SMART_SCALE_AUTO_START_LISTENING": "Timer automatisch starten?", + "SMART_SCALE_AUTO_START_LISTENING_DESCRIPTION": "Start de timer automatisch bij het bereiken van de volgende gewichtswaarde", + "CHOOSE_REFERENCE_GRAPH": "Referentiegrafiek selecteren", + "RESET": "Opnieuw instellen", + "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_GRAPHS_AXIS": "Definieer de beginassen van de grafiek", + "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_GRAPHS_AXIS_DESCRIPTION": "Stel de begingrootte van de assen in voor zowel filter- als espressokoffie.", + "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_GRAPHS_FILTER_WEIGHT": "Filter - Gewicht", + "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_GRAPHS_FILTER_FLOW": "Filter - Doorstroming", + "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_GRAPHS_ESPRESSO_WEIGHT": "Espresso - Gewicht", + "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_GRAPHS_ESPRESSO_FLOW": "Espresso - Doorstroming", + "SMART_SCALE_IGNORE_INCOMING_WEIGHT": "Negeer huidige gewichtsoverdracht ", + "SMART_SCALE_IGNORE_INCOMING_WEIGHT_TOOLTIP": "Er verschijnt een nieuwe knop in het brouwgedeelte, die nieuwe gewichtswaarden van de bluetooth weegschaal zal negeren.", + "BREWS_ACTIVE": "Actieve brouwsels", + "BREWS_ARCHIVED": "Gearchiveerde brouwsels", + "GRAPHS": "Grafieken", + "GRAPH_SECTION": { + "NAV_GRAPH": "Grafieken", + "NO_ARCHIVED_ENTRIES": "Geen gearchiveerde vermeldingen", + "NO_ENTRIES": "Geen vermeldingen", + "SECTION_HAS_BEEN_ACTIVATED": "Grafieksectie is geactiveerd" + }, + "TOAST_GRAPH_ARCHIVED_SUCCESSFULLY": "Grafiek gearchiveerd", + "TOAST_GRAPH_DELETED_SUCCESSFULLY": "Grafiek verwijderd", + "TOAST_GRAPH_EDITED_SUCCESSFULLY": "Grafiek bewerkt", + "TOAST_GRAPH_ADD_SUCCESSFULLY": "Grafiek toegevoegd", + "NAV_GRAPH_SECTION": "Grafieken", + "DELETE_GRAPH_QUESTION": "Wilt u deze grafiek verwijderen?", + "PAGE_SETTINGS_SHOW_ARCHIVED_GRAPHS": "Gearchiveerde grafieken weergeven", + "PAGE_SETTINGS_SHOW_GRAPH_SECTION": "Grafieksectie activeren", + "EDIT_GRAPH": "Grafiek bewerken", + "ADD_GRAPH": "Grafiek toevoegen", + "GRAPH": { + "PLACE_HOLDER": { + "NAME": "Grafiek naam", + "NOTES": "Notities" + }, + "UPLOAD": "Grafiek uploaden", + "DELETE": "Grafiek verwijderen", + "UPLOAD_DESCRIPTION": "Importeer een .JSON-bestand dat u kunt downloaden in de brew-detailweergave. U kunt ook gedeelde grafieken van de community importeren die van het type .JSON zijn." + }, + "SHOW_VISUALIZER": "Visualiseerder weergeven", + "NO_BREWS_FOUND": "Geen brouwsels gevonden", + "NO_GRAPHS_FOUND": "Geen grafieken gevonden", + "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_GRAPHS_TIME_AXIS": "Definieer de maximale tijdsas van de grafiek", + "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_GRAPHS_TIME_DESCRIPTION": "Stel het maximum aantal seconden in op de tijd-as voor zowel filterkoffie als espresso.", + "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_GRAPHS_TIME_FILTER_AXIS_NORMAL_SCREEN": "Filter - Normaal scherm", + "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_GRAPHS_TIME_FILTER_AXIS_FULL_SCREEN_SCREEN": "Filter - Volledig scherm", + "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_GRAPHS_TIME_ESPRESSO_AXIS_NORMAL_SCREEN": "Espresso - Normaal scherm", + "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_GRAPHS_TIME_ESPRESSO_AXIS_FULL_SCREEN_SCREEN": "Espresso - Volledig scherm", + "PAGE_SETTINGS_DATE_FORMAT": "Datum notatie", + "PAGE_SETTINGS_LANGUAGE_FRANCE": "Frans", + "PAGE_SETTINGS_LANGUAGE_INDONESIA": "Indonesisch", + "EXTRACTION_CHART_TITLE": "Extractie grafiek", + "PAGE_SETTINGS_SHOW_BACKUP_ISSUES": "Back-up problemen weergeven", + "PAGE_SETTINGS_SHOW_BACKUP_ISSUES_DESCRIPTION": "Geef een pop-up weer als er geen back-ups naar het bestandssysteem kunnen worden geschreven", + "AUTOMATIC_BACKUP_DID_FAIL": "Automatische back-up werkte niet, zorg ervoor dat u werkende back-ups hebt! Deze informatie kan worden uitgeschakeld in de instellingen", + "INTERNAL_BACKUP_DID_FAIL": "Interne back-up werkte niet, zorg ervoor dat u werkende back-ups hebt! Deze informatie kan worden uitgeschakeld in de instellingen", + "ZIP_BACKUP_FILE_COULD_NOT_BE_BUILD": "ZIP-bestand kon niet worden opgeslagen! Deze informatie kan worden uitgeschakeld in de instellingen", + "SEND_LOGS": "Logboeken verzenden", + "POPOVER_BLUETOOTH_ACTION_RECONNECT_REFRACTOMETER": "Sluit refractometer apparaat opnieuw aan", + "PAGE_SETTINGS_LANGUAGE_ITALIAN": "Italiaans", + "PAGE_SETTINGS_LANGUAGE_POLISH": "Pools", + "BREW_CANT_START_BECAUSE_TIMER_NOT_RESETTED_GENERAL_DESCRIPTION": "Je moet eerst je timer resetten voordat je kunt beginnen.", + "SMART_SCALE_FIRST_DRIP_THRESHOLD": "Eerste druppel drempel", + "SMART_SCALE_FIRST_DRIP_THRESHOLD_TOOLTIP": "Bij welk weegschaalgewicht moet de eerste druppel worden geteld? Standaard: >= 0,1g", + "PAGE_SETTINGS_BREW_TIMER_START_DELAY_ACTIVE": "Zetvertraging starten", + "PAGE_SETTINGS_BREW_TIMER_START_DELAY_ACTIVE_DESCRIPTION": "Stel een vertragingstijd in die een laadspinner weergeeft totdat het brouwen begint", + "STARTING_IN": "Beginnend in ... {{time}}", + "IOS_DATABASE_ISSUE_TITLE": "ATTENTIE !!!!! - DATABASEVERBINDING VERLOREN", + "IOS_DATABASE_ISSUE_DESCRIPTION": "Het spijt ons u te moeten meedelen dat de verbinding met de database is verbroken. Dit probleem is het gevolg van een onopgeloste bug in het systeem van Apple, waarover Beanconqueror geen controle heeft. Om mogelijk gegevensverlies te voorkomen, verzoeken wij u vriendelijk de Beanconqueror-applicatie onmiddellijk geforceerd af te sluiten en opnieuw te openen.", + "RELOAD_APP": "Herstart de app", + "WATER_TYPE_ADD_CUSTOM": "Water op maat", + "WATER_TYPE_THIRD_WAVE_WATER_CLASSIC_LIGHT_ROAST_PROFILE": "Third Wave Water - Klassiek licht geroosterd profiel", + "WATER_TYPE_THIRD_WAVE_WATER_MEDIUM_ROAST_PROFILE": "Third Wave Water - Middel geroosterd profiel", + "WATER_TYPE_THIRD_WAVE_WATER_DARK_ROAST_PROFILE": "Third Wave Water - Donker geroosterd profiel", + "WATER_TYPE_THIRD_WAVE_WATER_ESPRESSO_MACHINE_PROFILE": "Third Wave Water - Espressomachine Profiel", + "WATER_TYPE_THIRD_WAVE_WATER_COLD_BREW_PROFILE": "Third Wave Water - koud brouwprofiel", + "WATER_TYPE_THIRD_WAVE_WATER_LOW_ACID_PROFILE": "Third Wave Water - Laag Zuurprofiel", + "ADD_WATER": "Voeg water toe", + "EXTRACTION_CHART_LABEL_STRONG_UNDEREXTRACTED": "STERK
onderextractie", + "EXTRACTION_CHART_LABEL_STRONG": "Strong
", + "EXTRACTION_CHART_LABEL_STRONG_HARSH": "STERK
ruw", + "EXTRACTION_CHART_LABEL_UNDEREXTRACTED": "ondergeëxtraheerd", + "EXTRACTION_CHART_LABEL_IDEAL": "IDEAAL", + "EXTRACTION_CHART_LABEL_HARSH": "ruw", + "EXTRACTION_CHART_LABEL_WEAK_UNDEREXTRACTED": "ZWAK
onderextractie", + "EXTRACTION_CHART_LABEL_WEAK": "ZWAK
", + "EXTRACTION_CHART_LABEL_WEAK_HARSH": "ZWAK
ruw", + "PAGE_SETTINGS_TEXT_TO_SPEECH_SECTION": "Tekst naar spraak", + "TEXT_TO_SPEECH": { + "ACTIVATE": "Activeer tekst naar spraak", + "BREW_STARTED": "Brouwen gestart", + "BREW_ENDED": "Einde brouwen", + "TIME": "Tijd", + "SPEAK_EVERY_MS": "Spreek elke geselecteerde milliseconde", + "FOLLOWING_NUMBERS_WILL_BE_TEST_SPOKEN": "De volgende nummers worden als test uitgesproken", + "TEST_SPEAK": "Start test spraak", + "PITCH": "Toonhoogte", + "RATE": "beoordeling" + }, + "PAGE_SETTINGS_HAPTIC_FEEDBACK_SECTION": "Haptische feedback", + "HAPTIC_FEEDBACK": { + "ACTIVATE": "Activeer haptische feedback", + "BREW_STARTED": "Trillen bij het starten van het brouwen", + "BREW_STOPPED": "Trillen bij het stoppen van het brouwen", + "TARE": "Trillen bij tarra van de weegschaal" + }, + "CONNECTED": "Verbonden", + "DISCONNECTED": "Losgekoppeld", + "EXPERIMENTAL_FEATURE_DISCLAIMER": "Dit is een experimentele functie", + "SHOW_HOURS": "Toon uren", + "SHOW_MINUTES": "Toon Minuten", + "BEANS_UNARCHIVE": "uit het archief halen", + "TOAST_BEAN_UNARCHIVED_SUCCESSFULLY": "Boon is uit het archief gehaald", + "WATER_TYPE_PURE_COFFEE_WATER": "Zuiver Koffiewater", + "WATER_TYPE_EMPIRICAL_WATER_GLACIAL": "empirical water GLACIAL", + "WATER_TYPE_EMPIRICAL_WATER_SPRING": "empirical water SPRING", + "SORT_PREPARATION_TOOLS": "Sorteer bereidingsmiddelen", + "PREPARATION_TYPE_METICULOUS": "Meticulous", + "EXPORT_CAUTION": "Exporteren exporteert alleen de database, geen afbeeldingen, geen stromingsprofielen . Als deze nodig zijn, ga dan naar Gitbook voor meer informatie", + "POPOVER_FREEZE_COFFEE_BEAN": "Bevries koffieboon", + "POPOVER_UNFREEZE_COFFEE_BEAN": "Koffieboon ontdooien", + "BEAN_POPOVER_EDIT_FREEZE_DATE": "invries datum bewerken", + "BEAN_POPOVER_EDIT_UNFREEZE_DATE": "ontdooi datum bewerken", + "BEAN_POPOVER_LEFT_UNFROZEN": "Niet ingevroren", + "BEAN_POPOVER_FREEZE_PARTIAL_BAG": "Gedeelte zak invriezen (g)", + "FROZEN_BEANS": "Bevroren", + "BEAN_TAB_FROZEN_INFORMATION": "Bevroren notities", + "BEAN_POPOVER_FROZEN_BAGS": "Bevroren zakken", + "BEAN_POPOVER_FROZEN_DELETE_BEAN_MESSAGE": "Je gaat de hele zak invriezen en we hebben geen brouwsels gevonden die ermee gemaakt zijn, wil je de originele zak verwijderen?", + "CREATE_FROZEN_BEANS": "creëer bonen", + "BEAN_POPOVER_COPY_ATTACHMENTS": "Kopieer bijlagen", + "BEAN_POPOVER_COPY_ATTACHMENTS_DESCRIPTION": "Het kopiëren van bijlagen is aan het begin van deze functie gedeactiveerd", + "BEAN_DATA_FROZEN_DATE": "Invries datum", + "BEAN_DATA_UNFROZEN_DATE": "ontdooi datum", + "PAGE_BEANS_LIST_YOU_GOT_NO_FROZEN_BEANS": "Je hebt geen ingevroren bonen", + "BEAN_DATA_FROZEN_ID": "Ingevroren id", + "PAGE_SETTINGS_MANAGE_FEATURES": "Functies beheren", + "ACTIVE_BEAN_FREEZING_FEATURE": "Activeer het invriezen van bonen", + "NAV_FROZEN_BEANS_LIST": "Lijst met ingevroren bonen", + "BEAN_BREW_LIST_VIEW_PARAMETERS": "Boon informatie voor brouwen", + "BEAN_AGE_BY_BREW_DATE": "Leeftijd van de bonen op brouwdatum", + "BEAN_POPOVER_FROZEN_BEAN_WILL_BE_ARCHIVED_NOW_MESSAGE": "Je diepvrieszakken hebben als resultaat dat je originele zak geen gewicht meer heeft, maar je hebt er wel mee gebrouwen, dus beoordeel en stuur naar het archief.", + "BEAN_POPOVER_YOU_CANT_FREEZE_WITH_ZERO_WEIGHT_LEFT": "Je kunt niet bevriezen, want het restgewicht van je zak is nul", + "BEAN_DATA_BEST_DATE": "Beste Bonen Datum", + "BEAN_DATA_BEST_DATE_TOOLTIP": "Wanneer is de beste datum om de bonen te gebruiken?", + "BEAN_DATA_OPEN_DATE": "Openingsdatum van de zak", + "BEAN_DATA_OPEN_DATE_TOOLTIP": "Wanneer heb je de zak bonen geopend?", + "BEAN_FREEZING_STORAGE_TYPE_UNKNOWN": "Onbekend", + "BEAN_FREEZING_STORAGE_TYPE_COFFEE_BAG": "Koffiezak", + "BEAN_FREEZING_STORAGE_TYPE_COFFEE_JAR": "Koffie pot", + "BEAN_FREEZING_STORAGE_TYPE_ZIP_LOCK": "Ritssluiting", + "BEAN_FREEZING_STORAGE_TYPE_VACUUM_SEALED": "Vacuüm verzegeld", + "BEAN_FREEZING_STORAGE_TYPE_TUBE": "Buis", + "BEAN_DATA_FROZEN_STORAGE_TYPE": "Vriezer opslagtype", + "PAGE_SETTINGS_BREW_RATING_CHANGED_BREWS_NOT_VISIBLE": "U heeft de maximale beoordeling van uw brouwsels gewijzigd, maar u heeft uw brouwsels al hoger beoordeeld dan uw werkelijke maximum. Deze worden dus niet weergegeven bij de normale filterinstellingen.", + "PAGE_SETTINGS_BEAN_RATING_CHANGED_BEANS_NOT_VISIBLE": "U heeft de maximale beoordeling voor bonen gewijzigd, maar u heeft bonen al hoger beoordeeld dan uw werkelijke maximum. Ze worden dus niet weergegeven met de normale filterinstellingen.", + "BEAN_DATA_FROZEN_NOTE": "Bevroren notities", + "IGNORE_NEGATIVE_VALUES_DESCRIPTION": "Als je dit activeert, loopt de grafiek één seconde achter", + "IGNORE_ANOMALY_VALUES_DESCRIPTION": "Als je dit activeert, loopt de grafiek één seconde achter", + "SAVE_LOGFILES_TO_NOTES_FROM_MACHINE": "Bewaar de machine logs bij het opslaan van een brouwsel" +} \ No newline at end of file diff --git a/src/assets/i18n/pl.json b/src/assets/i18n/pl.json index ca541d8e..23e94e5e 100644 --- a/src/assets/i18n/pl.json +++ b/src/assets/i18n/pl.json @@ -1273,39 +1273,6 @@ "SHOW_MINUTES": "Pokaż minuty", "BEANS_UNARCHIVE": "Cofnij archiwizację", "TOAST_BEAN_UNARCHIVED_SUCCESSFULLY": "Ziarno nie zostało zarchiwizowane", - "UPDATE_TEXT_TITLE_TITLE": { - "7.4.0": { - "TITLE": "Wersja 7.4.0: Co nowego", - "DESCRIPTION": [ - "Ziarna<\/b>", - "Teraz możliwe jest zamrożenie ziaren kawy, aktywując tę funkcję w ustawieniach.", - "Dodano najlepszą i otwartą datę dla fasoli, należy aktywować ten parametr", - "Ziarna można teraz dodawać bezpośrednio z przeglądu wyboru.", - "", - "Termometr<\/b>", - "Wsparcie Combustion Inc. Termometr - Dziękujemy za urządzenie!", - "Wsparcie dla termometru Meater (nie Meater 2 lub Meater +) - dzięki Yannick!", - "", - "Sekcja wodna<\/b>", - "Dodano czystą wodę kawową", - "Dodana woda empiryczna", - "", - "Narzędzia przygotowawcze<\/b>", - "Posortuj teraz swoje narzędzia przygotowawcze", - "", - "Młynek<\/b>", - "Obrazy młynka są teraz wyświetlane w przeglądzie wyboru", - "", - "Ustawienia<\/b>", - "Komunikat bezpieczeństwa, jeśli zmieniono ocenę dla naparów lub ziaren, a maksymalna ocena jest niższa niż już oceniony wpis.", - "", - "Inne<\/b>", - "Przywrócono bezpośrednie ustawianie ostrości w sekundach podczas otwierania timera.", - "Kilka zmian technicznych w kodzie", - "Drobne poprawki" - ] - } - }, "WATER_TYPE_PURE_COFFEE_WATER": "Czysta woda kawowa", "WATER_TYPE_EMPIRICAL_WATER_GLACIAL": "woda empiryczna GLACIAL", "WATER_TYPE_EMPIRICAL_WATER_SPRING": "woda empiryczna SPRING", diff --git a/src/assets/i18n/tr.json b/src/assets/i18n/tr.json index cfb0c409..f2e49174 100644 --- a/src/assets/i18n/tr.json +++ b/src/assets/i18n/tr.json @@ -1273,39 +1273,6 @@ "SHOW_MINUTES": "Dakikayı göster", "BEANS_UNARCHIVE": "Arşivden çıkar", "TOAST_BEAN_UNARCHIVED_SUCCESSFULLY": "Çekirdek arşivden çıkarıldı", - "UPDATE_TEXT_TITLE_TITLE": { - "7.4.0": { - "TITLE": "Sürüm 7.4.0: Yenilikler", - "DESCRIPTION": [ - " Fasulye <\/b>", - "Artık kahve çekirdeklerinizi dondurmak mümkün, bu özelliği ayarlardan etkinleştirin", - "Çekirdek için en iyi ve açık tarih eklendi, bu parametreyi etkinleştirmeniz gerekir", - "Çekirdekler artık doğrudan seçime genel bakıştan eklenebilir", - "", - "Termometre<\/b>", - "Combustion Inc. Termometre Desteği - Cihaz için teşekkürler!", - "Meater Termometre desteği (Meater 2 veya Meater + değil) - Yannick'e teşekkürler!", - "", - "Su Bölümü<\/b>", - "Saf Kahve Suyu Eklendi", - "Ampirik su eklendi", - "", - "Hazırlık Araçları<\/b>", - "Hazırlık araçlarınızı şimdi sıralayın", - "", - "Öğütücü<\/b>", - "Öğütücü görüntüleri artık seçim genel görünümünde gösteriliyor", - "", - "Ayarlar<\/b>", - "Demleme veya çekirdek derecelendirmesini değiştirdiyseniz ve maksimum değer, halihazırda derecelendirilmiş bir girişten düşükse güvenlik mesajı", - "", - "Diğer<\/b>", - "Zamanlayıcıyı açarken doğrudan saniye odağı geri döndürüldü", - "Koddaki bazı teknik değişiklikler", - "Küçük değişiklikler" - ] - } - }, "WATER_TYPE_PURE_COFFEE_WATER": "Saf Kahve Suyu", "WATER_TYPE_EMPIRICAL_WATER_GLACIAL": "ampirik su GLACIAL", "WATER_TYPE_EMPIRICAL_WATER_SPRING": "ampirik su SPRING", diff --git a/src/assets/i18n/zh.json b/src/assets/i18n/zh.json index b7c5bd63..bca45dc7 100644 --- a/src/assets/i18n/zh.json +++ b/src/assets/i18n/zh.json @@ -1273,39 +1273,6 @@ "SHOW_MINUTES": "显示分钟", "BEANS_UNARCHIVE": "取消归档", "TOAST_BEAN_UNARCHIVED_SUCCESSFULLY": "咖啡豆已经取消归档", - "UPDATE_TEXT_TITLE_TITLE": { - "7.4.0": { - "TITLE": "版本7.4.0:有什么新功能?", - "DESCRIPTION": [ - "豆子<\/b>", - "现在可以将您的咖啡豆冷冻保存,您可以在设置中激活这个功能", - "为咖啡豆添加了最佳和开封日期,您需要激活此参数", - "现在可以直接从选择概览中添加豆子", - "空的", - "温度计<\/b>", - "支持 Combustion Inc. 温度计 - 感谢提供设备!", - "支持 Meater 温度计(不支持 Meater 2 或 Meater +) - 感谢 Yannick!", - "空的", - "水分区<\/b>", - "添加Pure Coffee Water", - "添加Empirical水", - "空的", - "准备工具<\/b>", - "整理您的准备工具", - "空的", - "磨豆机<\/b>", - "磨豆机的图片现在在选择概览中显示", - "空的", - "设置<\/b>", - "如果您更改了冲泡或咖啡豆的评分,并且最高评分低于已评分条目的评分,则显示安全提示信息", - "空的", - "其他<\/b>", - "在打开计时器时,恢复到以秒数计时为准", - "代码中的一些技术修改", - "小调整" - ] - } - }, "WATER_TYPE_PURE_COFFEE_WATER": "Pure Coffee Water", "WATER_TYPE_EMPIRICAL_WATER_GLACIAL": "empirical water GLACIAL", "WATER_TYPE_EMPIRICAL_WATER_SPRING": "empirical water SPRING", From d8b849d3f55999ebbbcf363c8add0e2ff3a92c3e Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Wed, 28 Aug 2024 22:00:59 +0200 Subject: [PATCH 25/55] #780 - Adding dutch as language --- src/app/app.component.ts | 2 + src/app/settings/settings.page.html | 1 + src/assets/i18n/en.json | 3 +- src/assets/i18n/nl_NL.json | 1323 ----------------- .../brew-information.component.ts | 1 - 5 files changed, 5 insertions(+), 1325 deletions(-) delete mode 100644 src/assets/i18n/nl_NL.json diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 5c9e5762..f06ed532 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -506,6 +506,8 @@ export class AppComponent implements AfterViewInit { settingLanguage = 'fr'; } else if (systemLanguage === 'id') { settingLanguage = 'id'; + } else if (systemLanguage === 'nl') { + settingLanguage = 'nl'; } else { settingLanguage = 'en'; } diff --git a/src/app/settings/settings.page.html b/src/app/settings/settings.page.html index fc663f98..dcd95cff 100644 --- a/src/app/settings/settings.page.html +++ b/src/app/settings/settings.page.html @@ -88,6 +88,7 @@

{{"EXPORT" | translate}}

{{"PAGE_SETTINGS_LANGUAGE_FRANCE" | translate}} {{"PAGE_SETTINGS_LANGUAGE_INDONESIA" | translate}} {{"PAGE_SETTINGS_LANGUAGE_POLISH" | translate}} + {{"PAGE_SETTINGS_LANGUAGE_DUTCH" | translate}} diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 682833e5..4bf29546 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -1340,5 +1340,6 @@ "BEAN_POPUP_YOU_DONT_SEE_EVERYTHING_DESCRIPTION": "You're adding a bean which has variety information in it, but those parameters are not activated. Do you want to activate them now?", "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS": "Define the axes for the pressure graph", "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_DESCRIPTION": "Set the starting and end size of the axes for pressure", - "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_RANGE": "Pressure axes" + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_RANGE": "Pressure axes", + "PAGE_SETTINGS_LANGUAGE_DUTCH": "Dutch" } diff --git a/src/assets/i18n/nl_NL.json b/src/assets/i18n/nl_NL.json deleted file mode 100644 index 31e2cd3e..00000000 --- a/src/assets/i18n/nl_NL.json +++ /dev/null @@ -1,1323 +0,0 @@ -{ - "NAV_MENU": "Menu", - "NAV_HOME": "Thuis", - "NAV_SETTINGS": "Instellingen", - "NAV_BREWS": "Brouwsels", - "NAV_BEANS": "Bonen", - "NAV_PREPARATION": "Methoden", - "NAV_MILL": "Malers", - "NAV_ABOUT_US": "Over ons", - "NAV_CONTACT": "Contact", - "NAV_PRIVACY": "Privacy", - "NAV_CREDITS": "Credits", - "NAV_TERMS": "Algemene voorwaarden", - "NAV_THANKS": "Bedankt!", - "NAV_LICENCES": "OSS-licenties", - "NAV_STATISTICS": "Statistieken", - "NAV_IMPRESSUM": "Impressum", - "NAV_COOKIE": "Cookies", - "NAV_LOGS": "Logboeken", - "NAV_BREW_PARAMS": "Brouwparameters", - "NAV_INFORMATION_TO_APP": "Over Beanconqueror", - "NAV_WATER_SECTION": "Water", - "NAV_HELPER": "Calculaties", - "POPOVER_BREWS_OPTION_REPEAT": "Herhalen", - "POPOVER_BREWS_OPTION_DETAIL": "Details", - "DETAIL": "Details", - "POPOVER_BREWS_OPTION_EDIT": "Bewerking", - "POPOVER_BREWS_OPTION_DELETE": "Verwijder", - "POPOVER_BREWS_OPTION_PHOTO_GALLERY": "Fotogalerij", - "POPOVER_BREWS_OPTION_CUPPING": "Cupping", - "POPOVER_BREWS_OPTION_MAP_COORDINATES": "Toon op kaart", - "POPOVER_BREWS_OPTION_FAST_REPEAT": "Snel brouwen herhalen", - "PAGE_BREWS_NO_ENTRIES": "Nog geen brouwsels toegevoegd", - "PAGE_BREWS_NO_ARCHIVED_ENTRIES": "Je hebt nog geen brouwsels gearchiveerd", - "CANT_START_NEW_BREW_TITLE": "Er ontbreekt hier iets...", - "CANT_START_NEW_BREW_DESCRIPTION": "Om te beginnen, maak een beschrijving voor één type bonen, één zetmethode en één grinder. Gebruik het menu om naar de verschillende categorieën te navigeren om deze informatie toe te voegen.", - "PAGE_HOME_WELCOME_GREETINGS": "Leuk dat je er bent!", - "PAGE_HOME_TOTAL_BREW": "Brouwen", - "PAGE_HOME_TOTAL_BREWS": "Brouwsels", - "PAGE_HOME_BEAN_EXPLORED": "Bonen onderzocht", - "PAGE_HOME_BEANS_EXPLORED": "Bonen onderzocht", - "PAGE_HOME_LAST_BREWS": "Laatste brouwsels", - "PAGE_HOME_LAST_BREW": "Laatste brouwsel", - "PAGE_HOME_DIFFERENT_PREPARATION_METHODS": "Verschillende bereidingsmethoden", - "PAGE_HOME_DIFFERENT_MILLS": "Verschillende malers", - "PAGE_HOME_SUPPORTER": "App Supporter", - "PAGE_HOME_START_BREW": "Begin met brouwen", - "PAGE_BEANS_LIST_OBTAINABLE": "Beschikbaar", - "PAGE_BEANS_LIST_YOU_GOT_NO_FRESH_BEANS": "Je hebt geen verse bonen meer!", - "PAGE_BEANS_LIST_YOU_GOT_NO_FINISHED_BEANS": "Je hebt geen gearchiveerde bonen.", - "PAGE_MILL_LIST_NO_MILL_EXISTING": "Je hebt nog geen grinders toegevoegd.", - "PAGE_PREPARATION_LIST_NO_PREPARATION_EXISTING": "Je hebt nog geen zetmethodes toegevoegd.", - "PAGE_CONTACT_SUGGESTIONS_QUESTIONS_WISHES": "Suggesties, vragen, bugs of verzoeken?", - "PAGE_THANKS_THANKS_FOR_YOUR_SUPPORT": "Bedankt voor je steun!", - "PAGE_SETTINGS_LANGUAGE": "Taalinstellingen", - "PAGE_SETTINGS_LANGUAGE_GERMAN": "Duits", - "PAGE_SETTINGS_LANGUAGE_ENGLISH": "Engels", - "PAGE_SETTINGS_LANGUAGE_SPANISH": "Spaans", - "PAGE_SETTINGS_LANGUAGE_TURKISH": "Turks", - "PAGE_SETTINGS_LANGUAGE_CHINESE": "Chinees", - "PAGE_SETTINGS_GENERAL_SETTINGS": "Algemene instellingen", - "PAGE_SETTINGS_TRANSFER": "Gegevens overdragen", - "PAGE_SETTINGS_PRESET_LAST_BREW": "Vooraf ingestelde waarden?", - "PAGE_SETTINGS_DISPLAY": "Weergave", - "PAGE_SETTINGS_DISPLAY_SINGLE_PAGE": "Eén pagina", - "PAGE_SETTINGS_DISPLAY_PAGING": "Paging", - "PAGE_SETTINGS_STARTUP_VIEW": "Startpagina", - "PAGE_SETTINGS_ANALYTICS_INFORMATION": "Analytics", - "PAGE_SETTINGS_ANALYTICS_INFORMATION_TOOLTIP": "Druk op i voor meer informatie", - "PAGE_SETTINGS_TRACK_BREW_COORDINATES": "Geolocatie van brouwsel opslaan", - "PAGE_SETTINGS_FAST_REPEAT": "Snel herhalen", - "PAGE_SETTINGS_TRACK_CAFFEINE_CONSUMPTION": "bewaar cafeïneconsumptie", - "PAGE_SETTINGS_WAKE_LOCK": "Houd het display actief tijdens het brouwen", - "PAGE_SETTINGS_CURRENCY": "Valuta", - "PAGE_STATISTICS_DIFFERENT_PREPARATION_METHOD": "Bereidingsmethoden", - "PAGE_STATISTICS_TOTAL_GROUND_BEANS": "Totaal gemalen bonen", - "PAGE_STATISTICS_MONEY_SPENT_FOR_COFFEE": "Geld uitgegeven aan bonen", - "PAGE_STATISTICS_DRUNKEN_BREWS": "Totaal aantal brouwsels", - "PAGE_STATISTICS_BREW_PROCESSES": "Totaal brouwsels", - "PAGE_STATISTICS_DRUNKEN_QUANTITY": "Verbruikte hoeveelheid", - "PAGE_STATISTICS_BEAN_WEIGHT_USED": "Totaal gemalen bonen", - "PAGE_BREW_TEXT_INFORMATION_FROM_ROASTER": "Informatie over de brander", - "PAGE_ABOUT_NO_VERSION_AVAILABLE": "Geen Versie beschikbaar", - "PAGE_ABOUT_APP_VERSION": "App Versie", - "PAGE_LICENCES_WEBSITE": "Website", - "BEAN_DATA_ROAST_NAME": "Mate van branding", - "BEAN_DATA_CUSTOM_ROAST_NAME": "Aangepaste mate van branding", - "BEAN_DATA_ROASTING_DATE": "Brand datum", - "BEAN_DATA_ROASTER": "Brander", - "BEAN_DATA_VARIETY": "Variëteit", - "BEAN_DATA_PROCESSING": "Verwerking", - "BEAN_DATA_COUNTRY": "Land", - "BEAN_DATA_MIX": "Melange", - "BEAN_DATA_AROMATICS": "Smaakprofiel", - "BEAN_DATA_WEIGHT": "Gewicht", - "BEAN_DATA_COST": "kosten", - "BEAN_DATA_NAME": "Naam", - "BEAN_DATA_REGION": "Regio", - "BEAN_DATA_FARM": "Boerderij", - "BEAN_DATA_FARMER": "Boer", - "BEAN_DATA_ELEVATION": "Elevatie", - "BEAN_DATA_HARVEST_TIME": "Geoogst", - "BEAN_DATA_PERCENTAGE": "Percentage", - "BEAN_DATA_CERTIFICATION": "Bonencertificering", - "BEAN_DATA_ROASTING_TYPE": "type branding", - "BEAN_DATA_DECAFFEINATED": "Cafeïnevrij", - "BEAN_DATA_URL": "Website", - "BEAN_DATA_EAN": "EAN \/ Artikelnummer", - "BEAN_DATA_PURCHASING_PRICE": "Aankoopprijs", - "BEAN_DATA_FOB_PRICE": "FOB-prijs", - "BEAN_DATA_CUPPING_POINTS": "Cupping-punten", - "BREW_DATA_CUSTOM_BREW_TIME": "Aangepaste brouwtijd", - "BREW_CREATION_DATE": "Aanmaakdatum", - "REPEAT": "Herhalen", - "EDIT": "Bewerken", - "DELETE": "Verwijderen", - "FINISHED": "Archief", - "NOTES": "Notities", - "ADD_PHOTO": "Foto toevoegen", - "CANCEL": "Annuleren", - "GENERATE": "Genereren", - "SAVE": "Opslaan", - "ADD_SOMETHING": "Toevoegen", - "CONTACT": "Contact", - "NAME": "Naam", - "IMPORT": "import", - "EXPORT": "Export", - "VIEW": "Weergave", - "ARCHIVE": "archief", - "CURRENT": "huidig", - "BACK": "Terug", - "CLOSE": "sluiten", - "DAY": "Dag", - "BREW_DATA_TEMPERATURE_TIME": "Temperatuur Tijd", - "BREW_DATA_SURF_TIME": "Surftijd", - "BREW_DATA_TIME": "Tijd", - "BREW_DATA_GRIND_SIZE": "Maalinstelling", - "BREW_DATA_GRIND_WEIGHT": "Gemalen koffie (gr)", - "BREW_DATA_IN_OUT_BR": "In\/Uit (BR)", - "BREW_DATA_NOTES": "Notities", - "BREW_DATA_PREPARATION_METHOD": "Bereidingswijze", - "BREW_DATA_MILL": "Maler", - "BREW_DATA_MILL_SPEED": "Maalsnelheid (rpm)", - "BREW_DATA_MILL_TIMER": "Maaltijd", - "BREW_DATA_BREW_QUANTITY": "Hoeveelheid water", - "BREW_DATA_BEAN_TYPE": "Boon type", - "BREW_DATA_BREW_TEMPERATURE": "Brouwtemperatuur", - "BREW_DATA_PRESSURE_PROFILE": "Profiel", - "BREW_DATA_COFFEE_TYPE": "Type koffie", - "BREW_DATA_COFFEE_CONCENTRATION": "Koffie Concentratie", - "BREW_DATA_COFFEE_FIRST_DRIP_TIME": "Eerste druppeltijd", - "BREW_DATA_COFFEE_BLOOMING_TIME": "Bloeitijd \/ Pre-infusie", - "BREW_DATA_ATTACHMENTS": "Bijlagen \/ Foto's", - "BREW_DATA_RATING": "Beoordeling", - "BREW_DATA_CALCULATED_COFFEE_BREW_TIME": "Koffiezettijd", - "BREW_DATA_TDS": "Totaal opgeloste vaste stoffen %", - "BREW_DATA_CALCULATED_EXTRACTION_YIELD": "Extractie Opbrengst %", - "BREW_INFORMATION_BREW_RATIO": "Brouw Verhouding", - "BREW_INFORMATION_BEAN_AGE": "Boon leeftijd", - "BREW_INFORMATION_BREW_QUANTITY_TYPE_NAME": "Hoeveelheidstype", - "BREW_DATA_TDS_EY": "TDS \/ %EY", - "BREW_DATA_BREW_BEVERAGE_QUANTITY": "Hoeveelheid drank", - "BREW_DATA_PREPARATION_METHOD_TOOL": "bereidings gereedschappen", - "BREW_DATA_WATER": "Water", - "BREW_DATA_BEAN_WEIGHT_IN": "Bonengewicht (g)", - "BREW_DATA_VESSEL": "Serveerkan", - "BREW_DATA_VESSEL_WEIGHT": "Serveerkan gewicht", - "BREW_DATA_VESSEL_NAME": "Serveerkan naam", - "BREW_DATA_FLAVOR": "Smaak", - "BREW_DATA_FLOW_PROFILE": "Stroom", - "ONE_DAY": "Dag", - "DAYS": "Dagen", - "ONE_HOUR": "uur", - "HOURS": "uren", - "ONE_MINUTE": "minuut", - "MINUTES": "minuten", - "WITHOUT_COFFEE": "zonder koffie", - "NOT_FOUND": "Niet gevonden", - "INVALID_FILE_FORMAT": "Ongeldig bestandsformaat", - "FILE_NOT_FOUND_INFORMATION": "Bestand niet gevonden", - "ERROR_ON_FILE_READING": "Fout bij het lezen van bestandsgegevens", - "IMPORT_SUCCESSFULLY": "Importeren succesvol", - "IMPORT_UNSUCCESSFULLY_DATA_NOT_CHANGED": "Import mislukt, er zijn geen gegevens gewijzigd", - "INVALID_FILE_DATA": "Ongeldige bestandsinhoud", - "DOWNLOADED": "Gedownload", - "FILE_DOWNLOADED_SUCCESSFULLY": "Bestand ' {{fileName}} ' is succesvol gedownload naar uw downloadmap!", - "NO": "Nee", - "YES": "Ja", - "SURE_QUESTION": "Weet je het zeker?", - "DELETE_BREW_QUESTION": "Brouwsel verwijderen?", - "DELETE_BEAN_QUESTION": "Boon verwijderen? Alle bijbehorende brouwsels worden ook verwijderd!", - "DELETE_GREEN_BEAN_QUESTION": "Groene bonen verwijderen? Alle bijbehorende geroosterde bonen, evenals brouwsels worden verwijderd!", - "DELETE_MILL_QUESTION": "Grinder verwijderen? Alle bijbehorende brouwsels worden ook verwijderd!", - "DELETE_PREPARATION_METHOD_QUESTION": "Bereidingswijze verwijderen? Alle bijbehorende brouwsels worden ook verwijderd!", - "DELETE_PREPARATION_TOOL_QUESTION": "Bereidingstool verwijderen? Alle brouwsels die aan deze tool zijn gekoppeld, worden bijgewerkt.", - "APP_COULD_NOT_STARTED_CORRECTLY_BECAUSE_MISSING_FILESYSTEM": "App kon niet correct worden gestart vanwege ontbrekend bestandssysteem", - "CARE": "Zorg", - "ERROR_OCCURED": "Er is een fout opgetreden", - "CSV_FILE_NOT_DOWNLOADED": "CSV-bestand kon niet worden gedownload!", - "CSV_FILE_DOWNLOADED_SUCCESSFULLY": "CSV-bestand ' {{fileName}} ' is succesvol gedownload naar uw downloadmap!", - "ADD_BREW": "Voeg brouwsel toe", - "CHOOSE": "Kies", - "CHOOSE_PHOTO_OR_LIBRARY": "Maak een foto of kies uit de fotogalerij.", - "RECORD": "Maak een foto", - "PAGE_BEAN_INFORMATION": "Boon informatie", - "PAGE_BEAN_INFORMATION_GOOD_BREWS": "Goed", - "PAGE_BEAN_INFORMATION_BAD_BREWS": "Slecht", - "PAGE_BEAN_INFORMATION_COUNT_BREWS": "Totaal aantal brouwsels", - "INFORMATION": "Informatie", - "PAGE_BEAN_BREW_CHART_TITLE": "Brouwoverzicht voor deze boon", - "PAGE_BEAN_INFORMATION_AWESOME_BREWS": "Geweldig", - "PAGE_BEAN_INFORMATION_NORMAL_BREWS": "Normaal", - "PAGE_BEAN_INFORMATION_NOT_RATED_BREWS": "Niet beoordeeld", - "PAGE_PREPARATION_INFORMATION_BREWS_DONE": "Brouwsels met deze bereidingswijze", - "PAGE_PREPARATION_INFORMATION_BREWED_QUANTITY": "Gebrouwen hoeveelheid", - "PAGE_PREPARATION_INFORMATION_GRIND_WEIGHT": "Verbruikt gewicht van bonen", - "PAGE_PREPARATION_INFORMATION_TIME_SPENT_BREWING": "Totale bereidingstijd", - "PAGE_PREPARATION_INFORMATION": "Informatie over de bereiding", - "SECONDS": "Seconden", - "PAGE_MILL_INFORMATION": "Maler informatie", - "PAGE_MILL_INFORMATION_BREWS_DONE": "Brouwsels met deze maler", - "PAGE_MILL_INFORMATION_GRIND_WEIGHT": "Verbruikt gewicht van bonen", - "PAGE_HELPER_WATER_HARDNESS": "Waterhardheid", - "PAGE_HELPER_WATER_HARDNESS_CA_CONTENTS": "Calciumgehalte mg\/l", - "PAGE_HELPER_WATER_HARDNESS_MG_CONTENTS": "Magnesiumgehalte mg\/l", - "PAGE_HELPER_WATER_HARDNESS_GERMAN_HARDNESS": "Duitse hardheidsgraad", - "PAGE_HELPER_WATER_HARDNESS_TOTAL_HARDNESS": "Totale hardheid", - "PAGE_HELPER_BREW_RATIO": "Brouw Verhouding", - "PAGE_HELPER_BREW_RATIO_GROUND_COFFEE": "Gemalen koffie (gr)", - "PAGE_HELPER_BREW_RATIO_WATER": "Vloeistof (gr\/ml)", - "PAGE_HELPER_BREW_RATIO_CALCULATED": "Berekende brouwverhouding", - "PAGE_SETTINGS_SHOW_ARCHIVED_BREWS": "Gearchiveerde brouwsels weergeven", - "PAGE_SETTINGS_SHOW_ARCHIVED_BEANS": "Gearchiveerde bonen weergeven", - "PAGE_SETTINGS_SHOW_ARCHIVED_GREEN_BEANS": "Toon gearchiveerde groene bonen", - "CUPPING_SCORE": "Score", - "CUPPING_SCORE_DRY_FRAGRANCE": "Droge geur", - "CUPPING_SCORE_WET_AROMA": "Nat aroma", - "CUPPING_SCORE_BRIGHTNESS": "Helderheid", - "CUPPING_SCORE_FLAVOR": "Smaak", - "CUPPING_SCORE_BODY": "Body", - "CUPPING_SCORE_FINISH": "Afwerking", - "CUPPING_SCORE_SWEETNESS": "Zoetheid", - "CUPPING_SCORE_CLEAN_CUP": "Clean Cup", - "CUPPING_SCORE_COMPLEXITY": "Complexiteit", - "CUPPING_SCORE_UNIFORMITY": "Uniformiteit", - "CUPPING_SCORE_CUPPERS_CORRECTION": "Cuppers-correctie", - "CUPPING_SCORE_DRY_FRAGRANCE_TOOLTIP": "Verwijst naar het aroma van de droge gemalen koffie voordat er heet water aan wordt toegevoegd.", - "CUPPING_SCORE_WET_AROMA_TOOLTIP": "De geur van nat koffiedik, nadat er heet water aan is toegevoegd.", - "CUPPING_SCORE_BRIGHTNESS_TOOLTIP": "Zuurgraad is de smaak van scherpe hoge tonen in de koffie, veroorzaakt door een set van chlorogeen, citroenzuur, kininezuur, azijnzuur en andere, voornamelijk waargenomen in de voorkant van de mond en op de tong. (Het is een goede kwaliteit; niet gerelateerd aan bitterheid in koffie, en niet direct verantwoordelijk voor maagklachten). Zuurgraad wordt gewaardeerd door veel kopers, en is direct gerelateerd aan de kwaliteit van de kop, aangezien zuurgraad het product is van aanplantingen op grote hoogte.", - "CUPPING_SCORE_FLAVOR_TOOLTIP": "Dit is de algehele indruk in de mond, inclusief alle andere beoordelingen. Er zijn 4 \"primaire smaak\"-groeperingen (zuur, zoet, zout, bitter) en veel \"secundaire smaken\".", - "CUPPING_SCORE_BODY_TOOLTIP": "Vaak \"mondgevoel\" genoemd, is body het gevoel van gewicht en dikte van de gezette koffie, veroorzaakt door het percentage oplosbare vaste stoffen in de kop, inclusief alle organische verbindingen die worden geëxtraheerd (de zetmethode en de hoeveelheid gemalen koffie die wordt gebruikt, hebben hier grote invloed op). We beoordelen Body op een lagere schaal omdat licht gebodyde koffiesoorten zeker niet slecht zijn en in sommige origines past de lichtere body het beste bij het algehele karakter van de kop.", - "CUPPING_SCORE_FINISH_TOOLTIP": "De aanhoudende of opkomende smaken die komen nadat de mond is schoongemaakt. Dit omvat de tijd dat de koffie je mond verlaat tot minuten daarna...een reden dat je veel cuppers zult vinden die hun nasmaakscores herzien terwijl ze een minuut of twee later nog steeds een positieve smaak ervaren.", - "CUPPING_SCORE_SWEETNESS_TOOLTIP": "Zoetheid is bijna altijd een gewenste kwaliteit in koffie, zelfs als het op eufemistische wijze wordt beschreven, zoals \"rustieke zoetheid\" of \"bitterzoetheid\". U zult merken dat verfijnde zoetheid (denk aan Europees gebak, fijn snoepgoed, witte suiker, pure zoetheid) hoog scoort, evenals complexe zoetheid van fruitsuikers (fructose). Moutige zoetheid (maltose) is minder traditioneel, maar wel wenselijk en honing kan variëren van heel puur en schoon tot complex, rustiek, bijna gistachtig. Kortom, als zoetheid een sleutel is tot de kop, zal het goed worden beoordeeld.", - "CUPPING_SCORE_CLEAN_CUP_TOOLTIP": "Let op dat \"clean cup\" niet letterlijk betekent dat er geen vuil op de koffie zit. Het gaat alleen om smaak en rauwe, funky koffies die \"onrein\" zijn en de smaak kan ook heel wenselijk zijn, zoals nat-gepelde Indonesische koffies uit Sumatra, of droog verwerkte Ethiopische en Jemenitische types.", - "CUPPING_SCORE_COMPLEXITY_TOOLTIP": "Complexiteit complimenteert de scores voor \"smaak\" en \"afwerking\", om een veelheid of gelaagdheid van veel smaken te communiceren. Het betekent dat er veel te ontdekken valt in de kop. Aan de andere kant kunnen simpele koffies een opluchting zijn na overmatige blootstelling aan veel krachtige, intense, complexe koffies.", - "CUPPING_SCORE_UNIFORMITY_TOOLTIP": "Uniformiteit verwijst naar verschillen tussen kopjes. Koffiesoorten die met een droog proces zijn bereid, kunnen van nature minder uniform zijn dan koffiesoorten die met een nat proces zijn bereid. We zouden nooit een partij met fantastische smaken vermijden als deze af en toe afwijkt. Dit wordt gescoord tijdens het cuppingprotocol, waarbij meerdere kopjes worden gemaakt van elke partij die wordt beoordeeld.", - "CUPPING_SCORE_CUPPERS_CORRECTION_TOOLTIP": "Dit is aangepast van het SCAA-systeem en de Cup of Excellence-score (soms noemen ze het \"Overall Points\"). Het stelt een cupper in staat om ervoor te zorgen dat de totale score de algehele indruk van de cup correct weergeeft. U zou deze aanpak kunnen bekritiseren en het \"vervalsen\" van het totaal kunnen beschouwen. In zekere zin zou u gelijk hebben ... maar het zou veel erger zijn om de categoriescores te veranderen om het gewenste totaal te bereiken (om een koffie een 9 te geven voor zuurgraad terwijl u weet dat het een 7 is), of omgekeerd om een koffie die absoluut een 90 verdient, op 84 te laten eindigen. Het specifieke Cupper's Correction-nummer doet er niet toe, of het nu een 5 of een 8 is ... het idee is dat de totale score een correcte indruk geeft van de kwaliteit van de koffie.", - "CUPPING_SCORE_TOOLTIP": "100-95 = Verbazingwekkend, 90-94 = Uitstekend, 85-89 = Zeer goed, 80-84 = Goed, 75-79 = Redelijk, 70-74 = Slecht", - "DETAIL_BREW": "Brouw details", - "DETAIL_BEAN": "Boon details", - "DETAIL_MILL": "Maler details", - "DETAIL_PREPARATION": "Bereidings details", - "EDIT_BREW": "Bewerk brouwsel", - "ADD_BEAN": "Boon toevoegen", - "EDIT_BEAN": "Bewerken boon", - "ADD_PREPARATION": "Bereidingsmethode toevoegen", - "EDIT_PREPARATION": "Bewerk bereidingsmethode", - "ADD_MILL": "Voeg maler toe", - "EDIT_MILL": "Maler bewerken", - "USE_FILTER": "Filter toepassen", - "RESET_FILTER": "Filter resetten", - "COFFEE_GRAMS_GRINDED": "Gram gemalen", - "BEANS_USED": "Bonen geconsumeerd", - "BREW_HEADER_BEFORE_BREW": "Voor het brouwen", - "BREW_HEADER_WHILE_BREW": "Tijdens het brouwen", - "BREW_HEADER_AFTER_BREW": "Na het brouwen", - "BREW_HEADER_CUPPING": "Proeven", - "BEANS_CONSUMED": "Archieveer", - "NAV_MANAGE_PARAMETERS": "Parameters beheren", - "NAV_SORT_PARAMETERS": "Sorteer parameters", - "NAV_DEFAULT_PARAMETERS": "Standaardparameters definiëren", - "PAGE_SORT_PARAMETERS_DESCRIPTION": "Versleep parameters om te bepalen in welke volgorde ze worden weergegeven.", - "PAGE_MANAGE_PARAMETERS_DESCRIPTION": "Geef aan welke parameters moeten worden weergegeven bij het bewerken van brouwinformatie.", - "PAGE_DEFAULT_PARAMETERS_DESCRIPTION": "Markeer welke parameters standaard moeten worden ingesteld op de laatst gebruikte waarde.", - "SORT_PARAMETERS_BEFORE": "Voor het brouwen", - "SORT_PARAMETERS_MEANWHILE": "Tijdens het brouwen", - "SORT_PARAMETERS_AFTER": "Na het brouwen", - "MORE_INFORMATION": "Meer informatie", - "UNDERSTOOD": "Begrepen", - "WELCOME_PAGE_ACTIVATE_ANALYTICS_TITLE": "Analyse en tracking", - "WELCOME_PAGE_ACTIVATE_ANALYTICS_DESCRIPTION": "We willen de app, de website en onze toekomstige diensten voor u voortdurend verbeteren. Om dit te doen, moeten we wat gegevens bijhouden over hoe u de app en de functies ervan gebruikt. Maar we beloven dat we nooit persoonlijke gegevens zullen bijhouden. Om deze beloften waar te maken, gebruiken we Matomo, een open source service gericht op gegevensbeveiliging en privacy die op onze eigen server wordt gehost - dit zorgt ervoor dat alleen wij eigenaar zijn van de gegevens. Onze website biedt alle informatie over de parameters die we bijhouden en bovendien kunt u de broncode bekijken die 100% open source is. Heeft u vragen? Neem dan gerust contact met ons op.", - "ANALYTICS_INFORMATION_TITLE": "Analyse en tracking", - "ANALYTICS_INFORMATION_DESCRIPTION": "Zoals u weet, is de veiligheid van uw gegevens en uw privacy onze topprioriteit. Daarom zijn we overgestapt van Google Analytics naar de open source service Matomo, die zich richt op gegevensbeveiliging en privacy. Deze service wordt gehost op onze eigen server. Dit betekent dat wij volledig eigenaar zijn van de gegevens. De bijgehouden parameters zijn niet gewijzigd en we beloven nog steeds dat we nooit persoonlijke gegevens zullen bijhouden. Op onze website vindt u alle informatie over de parameters die we bijhouden. Bovendien kunt u de broncode bekijken, die 100% open source is. Heeft u vragen? Neem dan gerust contact met ons op.", - "ACTIVATE": "Activeren", - "DO_NOT_ACTIVE": "Niet activeren", - "WELCOME_PAGE_BEAN_TITLE": "Boon", - "WELCOME_PAGE_BEAN_DESCRIPTION": "Koffie zetten zonder bonen is een beetje lastig. Voeg je eerste boon toe om te beginnen!", - "WELCOME_PAGE_BEAN_ADD": "Boon toevoegen", - "SKIP": "Overslaan", - "WELCOME_PAGE_PREPARATION_TITLE": "Bereidingswijze", - "WELCOME_PAGE_PREPARATION_DESCRIPTION": "V60, Aeropress, Espresso - er zijn veel manieren om koffie te zetten. Voeg er minstens één toe.", - "WELCOME_PAGE_PREPARATION_ADD": "Bereidingswijze toevoegen", - "WELCOME_PAGE_MILL_TITLE": "Maler", - "WELCOME_PAGE_MILL_DESCRIPTION": "Bijna klaar, maar je hebt iets nodig om je bonen te malen! Voeg minstens één maler toe.", - "WELCOME_PAGE_MILL_ADD": "Voeg Maler toe", - "WELCOME_PAGE_TITLE": "Welkom!", - "WELCOME_PAGE_BEAN_HEADLINE": "Eerste boon", - "WELCOME_PAGE_PREPARATION_HEADLINE": "Bereidingswijze toevoegen", - "WELCOME_PAGE_MILL_HEADLINE": "Eerste Maler", - "WELCOME_PAGE_LETS_START_HEADLINE": "Nu kunnen we beginnen met brouwen!", - "WELCOME_PAGE_LETS_START_TITLE": "Nu kunnen we beginnen met brouwen!", - "WELCOME_PAGE_LETS_START_DESCRIPTION": "Gefeliciteerd, je bent klaar om de beste koffie van je leven te zetten. Veel plezier en verspreid de liefde voor goede koffie!", - "PREPARATION_TYPE": "Bereidingsmethode", - "PREPARATION_TYPE_NAME": "Naam", - "ARCHIVED": "Gearchiveerd", - "PAGE_SETTINGS_SHOW_ARCHIVED_PREPARATIONS": "Toon gearchiveerde bereidingsmethoden", - "PAGE_SETTINGS_SHOW_ARCHIVED_MILLS": "Toon gearchiveerde malers", - "PAGE_MILL_LIST_NO_ARCHIVED_MILL_EXISTING": "Er zijn nog geen malers gearchiveerd.", - "PAGE_PREPARATION_LIST_NO_ARCHIVED_PREPARATION_EXISTING": "Je hebt geen gearchiveerde bereidingswijzen.", - "TOAST_BREW_ADDED_SUCCESSFULLY": "Brouwsel succesvol toegevoegd", - "TOAST_BREW_REPEATED_SUCCESSFULLY": "Brouwsel succesvol herhaald", - "TOAST_BEAN_ADDED_SUCCESSFULLY": "Boon succesvol toegevoegd", - "TOAST_MILL_ADDED_SUCCESSFULLY": "Maler succesvol toegevoegd", - "TOAST_PREPARATION_ADDED_SUCCESSFULLY": "Bereidingswijze succesvol toegevoegd", - "TOAST_WATER_ADDED_SUCCESSFULLY": "Water succesvol toegevoegd", - "TOAST_BREW_DELETED_SUCCESSFULLY": "Brouwsel is verwijderd", - "TOAST_BEAN_DELETED_SUCCESSFULLY": "Boon is verwijderd", - "TOAST_GREEN_BEAN_DELETED_SUCCESSFULLY": "Groene boon is verwijderd", - "TOAST_MILL_DELETED_SUCCESSFULLY": "Maler is verwijderd", - "TOAST_WATER_DELETED_SUCCESSFULLY": "Water is verwijderd", - "TOAST_PREPARATION_DELETED_SUCCESSFULLY": "Bereidingswijze is verwijderd", - "TOAST_BREW_EDITED_SUCCESSFULLY": "Brouwsel is bewerkt", - "TOAST_BEAN_EDITED_SUCCESSFULLY": "Boon is bewerkt", - "TOAST_MILL_EDITED_SUCCESSFULLY": "Maler is bewerkt", - "TOAST_PREPARATION_EDITED_SUCCESSFULLY": "Bereiding is bewerkt", - "TOAST_WATER_EDITED_SUCCESSFULLY": "Water is bewerkt", - "TOAST_BEAN_ARCHIVED_SUCCESSFULLY": "Boon is gearchiveerd", - "TOAST_MILL_ARCHIVED_SUCCESSFULLY": "Maler is gearchiveerd", - "TOAST_PREPARATION_ARCHIVED_SUCCESSFULLY": "Bereidingsmethode is gearchiveerd", - "TOAST_WATER_ARCHIVED_SUCCESSFULLY": "Water is gearchiveerd", - "BEAN_WEIGHT_ALREADY_USED": "{{gramUsed}} g van {{gramTotal}} g ( {{leftOver}} g)", - "PREPARATION_TYPE_CUSTOM_PREPARATION": "Aangepaste bereidingsmethode", - "PREPARATION_TYPE_AEROPRESS": "Aeropress", - "PREPARATION_TYPE_V60": "V60", - "PREPARATION_TYPE_CHEMEX": "Chemex", - "PREPARATION_TYPE_BIALETTI": "Bialetti", - "PREPARATION_TYPE_PORTAFILTER": "Espresso machine", - "PREPARATION_TYPE_KALITA_WAVE": "Kalita Wave", - "PREPARATION_TYPE_FRENCH_PRESS": "French Press", - "PREPARATION_TYPE_SWANNECK": "Swan Neck", - "PREPARATION_TYPE_DRIPPER": "Dripper", - "PREPARATION_TYPE_DELTER_PRESS": "Delter Press", - "PREPARATION_TYPE_COLD_BREW": "Koud brouwsel", - "PREPARATION_TYPE_AEROPRESS_INVERTED": "Aeropress Omgekeerd", - "PREPARATION_TYPE_TURKISH": "Turks", - "PREPARATION_TYPE_BLUE_DRIPPER": "Blue Dripper", - "PREPARATION_TYPE_ADD_CUSTOM": "Aangepaste methode toevoegen", - "PREPARATION_TYPE_GINA": "Gina", - "PREPARATION_TYPE_KONO": "Kono", - "PREPARATION_TYPE_ORIGAMI": "Origami", - "PREPARATION_TYPE_CAFELAT": "Cafelat", - "PREPARATION_TYPE_OREA": "Orea", - "PREPARATION_TYPE_COLD_DRIP": "Cold Drip", - "PREPARATION_TYPE_HAND_LEVER": "Handmatige hendel", - "PREPARATION_TYPE_FLAIR": "Flair", - "PREPARATION_TYPE_APRIL_BREWER": "April Brewer", - "PREPARATION_TYPE_ESPRO_BLOOM": "Espreo Bloom", - "PREPARATION_TYPE_FELLOW_STAGG": "Fellow Stagg", - "PREPARATION_TYPE_HSIAO_50": "Hsiao 50", - "PREPARATION_TYPE_KARLSBADER_KANNE": "Karlsbader", - "PREPARATION_TYPE_MOCCA_MASTER": "Mocca Master", - "PREPARATION_TYPE_SIPHON": "Sifon", - "CHOOSE_BEANS": "Selecteer bonen", - "CHOOSE_BEAN": "Selecteer boon", - "CHOOSE_WATERS": "Selecteer wateren", - "CHOOSE_WATER": "Selecteer water", - "CHOOSE_PREPARATIONS": "Selecteer bereidingsmethoden", - "CHOOSE_PREPARATION": "Selecteer bereidingsmethode", - "CHOOSE_MILLS": "Selecteer molens", - "CHOOSE_MILL": "Selecteer molen", - "BEAN": { - "PLACE_HOLDER": { - "BEAN_DATA_NAME": "Naam toevoegen", - "BEAN_DATA_ROAST_NAME": "Voeg de mate van branding toe", - "BEAN_DATA_ROASTING_DATE": "Wanneer werden de bonen geroosterd?", - "BEAN_DATA_ROASTER": "Wie heeft de bonen geroosterd?", - "BEAN_DATA_VARIETY": "Voeg de koffievariant toe", - "BEAN_DATA_PROCESSING": "Koffieverwerking, bijvoorbeeld gewassen", - "BEAN_DATA_COUNTRY": "Waar komt de boon vandaan?", - "BEAN_DATA_MIX": "Wat is de melange ratio?", - "BEAN_DATA_AROMATICS": "Voeg smaken toe", - "BEAN_DATA_WEIGHT": "Gewicht van de bonen", - "BEAN_DATA_COST": "Hoeveel hebben de bonen gekost?", - "BEAN_DATA_REGION": "Regio toevoegen", - "BEAN_DATA_FARM": "Voeg de boerderij toe", - "BEAN_DATA_FARMER": "Voeg de boer toe", - "BEAN_DATA_ELEVATION": "Op welke hoogte werden de bonen geteeld", - "BEAN_DATA_HARVEST_TIME": "wanneer zijn de bonen geoogst?", - "BEAN_DATA_BUY_DATE": "Wanneer zijn de bonen gekocht?", - "BEAN_DATA_PERCENTAGE": "Voeg het percentage van deze boon in de melange toe", - "BEAN_DATA_CERTIFICATION": "Voeg de bonencertificering toe (bijv. fairtrade, bio)", - "BEAN_DATA_ROASTING_TYPE": "Voeg brand type toe", - "BEAN_DATA_DECAFFEINATED": "Is deze koffie cafeïnevrij", - "BEAN_DATA_URL": "Voeg de website-url toe", - "BEAN_DATA_EAN": "Voeg het EAN- of artikelnummer toe", - "BEAN_DATA_CUPPING_POINTS": "Voeg de cuppingpunten toe", - "BEAN_DATA_PURCHASING_PRICE": "Voeg de aankoopprijs toe", - "BEAN_DATA_FOB_PRICE": "Voeg de FOB-prijs toe", - "NOTES": "Voeg notities toe over deze bonen", - "CHOOSE_DATA_ROASTER": "Kies een brander", - "CHOOSE_DATA_ROASTING_TYPE": "Kies type branding", - "BEAN_DATA_BEST_DATE": "Wanneer is de beste datum om de bonen te gebruiken?", - "BEAN_DATA_OPEN_DATE": "Wanneer is de zak bonen geopend?", - "FROZEN_NOTES": "Zijn er opmerkingen over de bevroren koffie? Bijvoorbeeld in welke vriezer je het hebt bewaard." - } - }, - "PREPARATION": { - "PLACE_HOLDER": { - "PREPARATION_TYPE_NAME": "Voeg een naam toe", - "NOTES": "Voeg notities toe over deze bereidingsmethode" - } - }, - "MILL": { - "PLACE_HOLDER": { - "NAME": "Voeg een naam toe", - "NOTES": "Voeg notities toe over deze grinder" - } - }, - "BREW": { - "PLACE_HOLDER": { - "BREW_DATA_GRIND_SIZE": "Voer de maalinstelling in", - "BREW_DATA_GRIND_WEIGHT": "Vul de hoeveelheid koffie in (gr)", - "BREW_DATA_BREW_TEMPERATURE": "Voer de brouwtemperatuur in", - "BREW_DATA_PREPARATION_METHOD": "Selecteer bereidingsmethode", - "BREW_DATA_BEAN_TYPE": "Selecteer bonen", - "BREW_DATA_MILL": "Selecteer een molen", - "BREW_DATA_MILL_SPEED": "Voer de maalsnelheid in", - "BREW_DATA_MILL_TIMER": "Voer de maal tijd in", - "BREW_DATA_PRESSURE_PROFILE": "Druk\/stroomprofiel, brouwmethodologie", - "BREW_DATA_TEMPERATURE_TIME": "Voer de tijd in die de machine heeft mogen opwarmen", - "BREW_DATA_COFFEE_BLOOMING_TIME": "Hoe lang is de bloom?", - "BREW_DATA_COFFEE_FIRST_DRIP_TIME": "Wanneer verscheen de eerste druppel?", - "BREW_DATA_BREW_QUANTITY": "Hoeveel water heb je gebruikt om te brouwen?", - "BREW_DATA_COFFEE_TYPE": "Voer de koffiestijl in (bijv. ristretto)", - "BREW_DATA_COFFEE_CONCENTRATION": "Voer de koffie concentratie in", - "BREW_DATA_TDS": "Wat was de gemeten hoeveelheid opgeloste vaste stoffen (TDS)?", - "BREW_DATA_NOTES": "Voeg notities toe over dit brouwsel", - "BREW_DATA_BREW_BEVERAGE_QUANTITY": "Voeg totale drank hoeveelheid toe", - "BREW_DATA_PREPARATION_METHOD_TOOL": "Kies je bereiding tools", - "BREW_DATA_WATER": "Kies het gebruikte water", - "BREW_DATA_BEAN_WEIGHT_IN": "Welk gewicht aan bonen heb je gebruikt?" - } - }, - "ROASTED_BEFORE": "Geroosterd voor", - "DAY_OLD": "dag oud", - "DAYS_OLD": "dagen oud", - "BEANS_AMOUNT_USED": "Verbruikt", - "CUPPING_BREW": "Proeven", - "COFFEE_DRUNKEN_QUANTITY": "Koffie dronken", - "IMAGE_DELETED": "Afbeelding is verwijderd", - "IMAGE_NOT_DELETED": "Afbeelding kon niet worden verwijderd", - "EXTERNAL_STORAGE_NOT_SUPPORTED": "Sorry, externe opslag wordt niet ondersteund", - "BEANS_ARCHIVED": "Gearchiveerd", - "TAB_ARCHIVE": "Archief", - "TODAY": "Vandaag", - "PLEASE_WAIT": "Even geduld aub...", - "PREPARATION_STYLE_POUR_OVER": "Pourover", - "PREPARATION_STYLE_ESPRESSO": "Espresso", - "PREPARATION_STYLE_FULL_IMMERSION": "Immersie", - "PREPARATION_STYLE_PERCOLATION": "Percolatie", - "PREPARATION_TYPE_STYLE": "Bereidingsstijl", - "PAGE_SETTINGS_FAST_REPEAT_DESCRIPTION": "Activeert een nieuw menu-item - hiermee kun je een brouwsel kopieren.", - "PAGE_SETTINGS_TRACK_BREW_COORDINATES_DESCRIPTION": "Sla geolocatie gegevens op voor elk brouwsel.", - "PAGE_SETTINGS_TRACK_CAFFEINE_CONSUMPTION_DESCRIPTION": "Bewaar de hoeveelheid geconsumeerde cafeïne", - "UPDATE_TITLE": "Wat is er nieuw?", - "NEXT": "Volgende", - "CUSTOM_PARAMETERS": "Parameters aanpassen", - "CUSTOM_DEFAULT_PARAMETERS": "Standaard", - "CUSTOM_MANAGE_PARAMETERS": "Beheer", - "CUSTOM_SORT_PARAMETERS": "Sorteer", - "BREW_PARAMETER_CUSTOMIZE_TITLE": "Aangepaste parameters voor elke bereidingsmethode", - "BREW_PARAMETER_CUSTOMIZE_DESCRIPTION": "Wilt u voor elke bereidingsmethode aangepaste parameters kiezen? Navigeer naar \"Methoden\", open het menu van de specifieke bereidingsmethode en kies \"Parameters aanpassen\". Daar kunt u kiezen welke parameters voor deze bereiding worden gebruikt!", - "BREW_DATA_BREW_QUANTITY_TOOLTIP": "Hoeveelheid water (niet bruikbaar voor espresso)", - "BREW_DATA_COFFEE_FIRST_DRIP_TIME_TOOLTIP": "Eerste druppel tijd van de koffie (alleen espresso)", - "BREW_DATA_PREPARATION_METHOD_TOOLTIP": "Voorbereiding (alleen aanpasbaar indien actief)", - "PAGE_SETTINGS_GENERAL": "Algemene instellingen", - "EDIT_PREPARATION_CUSTOM_PARAMETERS": "Parameters aanpassen", - "ENABLE_PREPARATION_CUSTOM_PARAMETERS": "Aangepaste parameters gebruiken", - "BEAN_ADD_ANOTHER_SORT": "Voeg een andere boon type toe", - "BEAN_SORT": "Boon Type", - "BEAN_SORT_INFORMATION": "Informatie over de variëteit", - "BEAN_SORT_MORE_INFORMATION": "Meer informatie", - "NAVIGATE_TO_PREPARATION_METHODS": "Navigeer naar bereidingsmethoden", - "PREPARATION_TOOLS": "bereidingsmiddelen", - "PREPARATION_TOOLS_INFORMATION": "Voeg verschillende mandjes toe, WDT-gereedschappen voor uw espressomachine; stoffen, papieren of gaasfilters voor uw filterkoffie, enz.", - "PREPARATION_TOOLS_PLACEHOLDER": "Papieren of stoffen filter, VST 20g, 14g mandje, enz.", - "PREPARATION_PARAMETERS_CUSTOMIZED": "Aangepaste parameters", - "BEANS_WEIGHT_AVAILABLE": "Beschikbare bonen", - "SORT_ORDER": "Sorteervolgorde wijzigen", - "ASCENDING": "Oplopend", - "DESCENDING": "Aflopend", - "SORT_AFTER": "Sorteren na", - "BEAN_SORT_NAME_OF_BEAN": "Boon naam", - "BEAN_SORT_ROASTER": "Brander", - "BEAN_SORT_ROASTING_DATE": "Datum van roosteren", - "BEAN_TAB_ROAST_INFORMATION": "Geroosterde informatie", - "BEAN_TAB_GENERAL_INFORMATION": "Algemeen", - "BEAN_TAB_SORT_INFORMATION": "Informatie over de variëteit", - "PAGE_SETTINGS_MANAGE_ARCHIVE": "Archief beheren", - "LAST_USE": "Laatst gebruikt", - "SEARCH": "Zoeken", - "OVERVIEW": "Overzicht", - "BEAN_HEADER_ADDITIONALE_INFORMATION": "Aanvullende informatie", - "THREE_DEE_TOUCH_ACTION_BREW": "Brouw", - "THREE_DEE_TOUCH_ACTION_BEAN": "Boon", - "THREE_DEE_TOUCH_ACTION_PREPARATION": "Bereidingsmethode", - "THREE_DEE_TOUCH_ACTION_MILL": "Koffiemolen", - "PAGE_CREDITS_NOT_EXISTING": "Geen inhoud", - "TIMER_HOUR": "Uren", - "TIMER_MINUTES": "Minuten", - "TIMER_SECONDS": "Seconden", - "EXCEL": { - "BEAN": { - "CREATION_DATE": "Aanmaakdatum", - "ID": "Bonen-ID" - }, - "PREPARATION": { - "CREATION_DATE": "Aanmaakdatum", - "ID": "bereidingsmiddelen" - }, - "GRINDER": { - "CREATION_DATE": "Aanmaakdatum", - "ID": "Molen-ID" - } - }, - "EXCEL_EXPORT": "Excel-export", - "HEALTH_KIT_QUESTION_TITLE": "cafeïneconsumptie opslaan", - "HEALTH_KIT_QUESTION_MESSAGE": "Na activering wordt de geschatte cafeïne van elk brouwsel automatisch opgeslagen in Apple Health.", - "NAV_ROASTING_SECTION": "Bonen roosteren", - "ROASTING_SECTION": { - "NAV_GREEN_BEANS": "Groene bonen", - "NAV_ROASTING_MACHINE": "Roostermachine", - "ROASTING_MACHINE": { - "TOTAL_ROAST_QUANTITY": "Totaalgewicht geroosterd", - "TOTAL_ROAST_COUNT": "Aantal brandingen" - }, - "GREEN_BEAN": { - "ADD": "Toevoegen", - "EDIT": "Bewerking", - "DETAIL": "Groene bonen details", - "ROASTABLE": "Roosterbaar", - "NO_ROASTS_YET": "Nog geen gebrande bonen" - }, - "BEAN": { - "DROP_TEMPERATURE": "Eindtemperatuur van de gebrande boon", - "ROAST_LENGTH": "brand lengte", - "ROASTER_MACHINE": "Roostermachine", - "GREEN_BEAN_WEIGHT": "Gewicht van groene bonen", - "OUTSIDE_TEMPERATURE": "Omgevingstemperatuur", - "HUMIDITY": "Vochtigheid", - "FIRST_CRACK_MINUTE": "eerst kraak minuut", - "FIRST_CRACK_TEMPERATURE": "eerste kraak temperatuur", - "SECOND_CRACK_MINUTE": "Tweede kraak minuut", - "SECOND_CRACK_TEMPERATURE": "tweede kraak temperatuur", - "PLACE_HOLDER": { - "DROP_TEMPERATURE": "Eindtemperatuur van de gebrande boon", - "ROAST_LENGTH": "brand lengte", - "ROASTER_MACHINE": "Roostermachine", - "GREEN_BEAN_WEIGHT": "Gewicht van groene bonen", - "OUTSIDE_TEMPERATURE": "Omgevingstemperatuur", - "HUMIDITY": "Vochtigheid", - "FIRST_CRACK_MINUTE": "Eerste kraak minuut", - "FIRST_CRACK_TEMPERATURE": "Eerste kraak temperatuur", - "SECOND_CRACK_TEMPERATURE": "Tweede kraak temperatuur", - "SECOND_CRACK_MINUTE": "Tweede kraak minuut" - } - } - }, - "PAGE_SETTINGS_MANAGE_SECTIONS": "Meer secties", - "PAGE_SETTINGS_SHOW_ROASTING_SECTION": "Activeer brandersectie", - "PAGE_SETTINGS_SHOW_WATER_SECTION": "Activeer water sectie", - "PAGE_SETTINGS_SHOW_CUPPING_SECTION": "Activeer cupping-sectie", - "BEAN_DATA_BUY_DATE": "Aankoopdatum", - "BEAN_SORT_CREATION_DATE": "Aanmaakdatum", - "BEAN_SORT_PURCHASE_DATE": "Aankoopdatum", - "BEAN_ROAST_COUNT": "Roost aantal", - "TRANSFER_ROAST": "Geroosterd", - "BEAN_TAB_LINKED_ROASTS": "Geroosterd", - "BEAN_DATA_WEIGHT_AFTER_ROASTING": "Gewicht na het roosteren", - "TOAST_GREEN_BEAN_ADDED_SUCCESSFULLY": "Groene bonen toegevoegd", - "TOAST_GREEN_BEAN_EDITED_SUCCESSFULLY": "Groene boon bewerkt", - "TOAST_GREEN_BEAN_ARCHIVED_SUCCESSFULLY": "Groene bonen gearchiveerd", - "TOAST_ROASTING_MACHINE_ADDED_SUCCESSFULLY": "Roostermachine toegevoegd", - "TOAST_ROASTING_MACHINE_EDITED_SUCCESSFULLY": "Brandmachine bewerkt", - "TOAST_ROASTING_MACHINE_ARCHIVED_SUCCESSFULLY": "Brander gearchiveerd", - "DELETE_ROASTING_MACHINE_QUESTION": "Brander verwijderen? Alle gebrande bonen waarnaar verwezen wordt, worden bijgewerkt en niet verwijderd.", - "TOAST_ROASTING_MACHINE_DELETED_SUCCESSFULLY": "Brander verwijderd", - "EDIT_ROASTING_MACHINE": "Bewerk", - "DETAIL_ROASTING_MACHINE": "Details van de brander", - "DELETE_WATER_QUESTION": "Water verwijderen? Alle gerefereerde brouwsels worden bijgewerkt en niet verwijderd", - "ROASTING_MACHINE": { - "PLACE_HOLDER": { - "NAME": "Voeg een naam toe", - "NOTES": "Opmerkingen toevoegen voor deze brander" - } - }, - "NAV_ROASTING_MACHINE": "koffierooster machines", - "PAGE_ROASTING_MACHINE_LIST_NO_MACHINES_EXISTING": "U hebt geen koffiebrander toegevoegd", - "PAGE_ROASTING_MACHINE_LIST_NO_ARCHIVED_MACHINES_EXISTING": "je hebt geen koffiebrander gearchiveerd", - "CHOOSE_ROASTING_MACHINES": "koffiebrander", - "CHOOSE_ROASTING_MACHINE": "koffiebrander", - "POPOVER_BREWS_OPTION_TOGGLE_FAVOURITE": "Favoriet", - "TOAST_BREW_FAVOURITE_ADDED": "Favoriet toegevoegd", - "TOAST_BREW_FAVOURITE_REMOVED": "Favoriet verwijderd", - "BREW_FILTER_JUST_FAVOURITE": "Favorieten", - "STATISTICS_PREPARATION_USAGES": "Bereidingsmethode gebruik", - "STATISTICS_PREPARATION_TIMELINE_USAGES": "Gebruiksgeschiedenis bereidingsmethode", - "STATISTICS_GRINDER_TIMELINE_USAGES": "Gebruiksgeschiedenis van de Maler", - "ACCEPT": "Accepteer", - "STATISTIC_TAB_GENERAL": "Algemeen", - "STATISTIC_TAB_BREWS": "Brouwsels", - "STATISTIC_TAB_BEANS": "Bonen", - "STATISTIC_TAB_PREPARATIONS": "Voorbereidingen", - "STATISTIC_TAB_GRINDERS": "Malers", - "PAGE_STATISTICS_BREW_PER_DAYPROCESSES": "Brouwsels per dag", - "PAGE_STATISTICS_BREW_TIME": "Brouwtijd", - "PAGE_STATISTICS_PHOTOS_TAKEN": "Foto's gemaakt", - "PAGE_SETTINGS_IMAGE_QUALITY": "afbeelding kwaliteit", - "PAGE_SETTINGS_IMAGE_QUALITY_TOOLTIP": "Bepaal in welke kwaliteit uw afbeeldingen moeten worden opgeslagen. Dit kan uw dataverbruik verlagen.", - "PAGE_SETTINGS_BREW_RATING": "Brouw beoordeling", - "PAGE_SETTINGS_BREW_RATING_TOOLTIP": "Is de standaard '-1 tot 5' niet de juiste beoordeling voor u? U kunt in plaats daarvan een '-1 tot 100' schaal gebruiken", - "UPDATE_ENTRY_OF": "Update item {{index}} van {{count}}", - "WEBSITE": "Website", - "SHARE": "Deel", - "ANDROID_FILE_ACCESS_NEEDED_TITLE": "Toegang tot gedeeld bestand vereist", - "ANDROID_FILE_ACCESS_NEEDED_DESCRIPTION": "Om de app volledig te laten werken, vragen we u om bestandstoegang te autoriseren. Anders ontstaan er problemen bij het gebruik van de app. Dit is specifiek nodig voor het automatische back-upsysteem.", - "COULD_NOT_ACCESS_FILE": "We konden het gekozen bestand niet openen", - "WRONG_FILE_FORMAT": "U hebt een niet-ondersteund bestandsformaat gekozen", - "SCAN_BEAN": "Scan pakket", - "CLEAR": "leeg maken", - "BEAN_LOOKS_LIKE_CONSUMED": "Het lijkt erop dat deze bonen op zijn. Wil je ze archiveren?", - "CUPPING_1": "Fruit", - "CUPPING_2": "Citrus", - "CUPPING_3": "Citroen & limonade", - "CUPPING_4": "Limoen", - "CUPPING_5": "Grapefruit", - "CUPPING_6": "Clementine", - "CUPPING_7": "Mandarijn", - "CUPPING_8": "mandarijn sinaasappel", - "CUPPING_9": "sinasappel", - "CUPPING_10": "Appel\/peer", - "CUPPING_11": "Groene appel", - "CUPPING_12": "Rode appel", - "CUPPING_13": "Meloen", - "CUPPING_14": "Watermeloen", - "CUPPING_15": "Honingdauw meloen", - "CUPPING_16": "Cantaloupe meloen", - "CUPPING_17": "Druif", - "CUPPING_18": "Witte druif", - "CUPPING_19": "Groene druif", - "CUPPING_20": "Rode druif", - "CUPPING_21": "Concord-druif", - "CUPPING_22": "Tropisch fruit", - "CUPPING_23": "Litchi", - "CUPPING_24": "Stervrucht", - "CUPPING_25": "Tamarinde", - "CUPPING_26": "Passievrucht", - "CUPPING_27": "Ananas", - "CUPPING_28": "Mango", - "CUPPING_29": "Papaja", - "CUPPING_30": "Kiwi", - "CUPPING_31": "Banaan", - "CUPPING_32": "Kokosnoot", - "CUPPING_33": "Steenvrucht", - "CUPPING_34": "Perzik", - "CUPPING_35": "Nectarine", - "CUPPING_36": "Abrikoos", - "CUPPING_37": "Pruim", - "CUPPING_38": "Kers", - "CUPPING_39": "Zwarte kers", - "CUPPING_40": "Bes", - "CUPPING_41": "Cranberry", - "CUPPING_42": "Framboos", - "CUPPING_43": "Aardbei", - "CUPPING_44": "Bosbes", - "CUPPING_45": "Rode bes", - "CUPPING_46": "Zwarte bes", - "CUPPING_47": "Gedroogd fruit", - "CUPPING_48": "Gouden rozijn", - "CUPPING_49": "Rozijn", - "CUPPING_50": "Gedroogde vijg", - "CUPPING_51": "Gedroogde dadels", - "CUPPING_52": "Gedroogde Pruim", - "CUPPING_53": "Zoet & gebrand", - "CUPPING_54": "Chocolade", - "CUPPING_55": "Cacaonibs", - "CUPPING_56": "Donkere chocolade", - "CUPPING_57": "Bakkers chocolade", - "CUPPING_58": "Bitterzoete chocolade", - "CUPPING_59": "Cacaopoeder", - "CUPPING_60": "Melkchocolade", - "CUPPING_61": "Noot", - "CUPPING_62": "Walnoot", - "CUPPING_63": "Pinda", - "CUPPING_64": "Cashew", - "CUPPING_65": "Pecannoot", - "CUPPING_66": "Hazelnoot", - "CUPPING_67": "Amandel", - "CUPPING_68": "Graan & Graanproducten", - "CUPPING_69": "Zoet broodgebak", - "CUPPING_70": "Granola", - "CUPPING_71": "Graham-cracker", - "CUPPING_72": "Rogge", - "CUPPING_73": "Tarwe", - "CUPPING_74": "Gerst", - "CUPPING_75": "Vers brood", - "CUPPING_76": "Zoet & Suikerachtig", - "CUPPING_77": "Vanille", - "CUPPING_78": "Noga", - "CUPPING_79": "Honing", - "CUPPING_80": "Boter", - "CUPPING_81": "Room", - "CUPPING_82": "Marshmallow", - "CUPPING_83": "Rietsuiker", - "CUPPING_84": "Bruine suiker", - "CUPPING_85": "Karamel", - "CUPPING_86": "Ahornsiroop", - "CUPPING_87": "Melasse", - "CUPPING_88": "Cola", - "CUPPING_89": "Geroosterd", - "CUPPING_90": "Toast", - "CUPPING_91": "Verbrande suiker", - "CUPPING_92": "Rokerig", - "CUPPING_93": "Koolstof", - "CUPPING_94": "Plantaardig, hartig en kruidig", - "CUPPING_95": "Kruiden", - "CUPPING_96": "Zwarte peper", - "CUPPING_97": "Witte peper", - "CUPPING_98": "Kaneel", - "CUPPING_99": "Koriander", - "CUPPING_100": "Gember", - "CUPPING_101": "Nootmuskaat", - "CUPPING_102": "Kerrie", - "CUPPING_103": "Drop-anijs", - "CUPPING_104": "Kruidnagel", - "CUPPING_105": "Hartig", - "CUPPING_106": "Leerachtig", - "CUPPING_107": "Vleesachtig", - "CUPPING_108": "Sojasaus", - "CUPPING_109": "Zongedroogde tomaat", - "CUPPING_110": "Tomaat", - "CUPPING_111": "Plantaardig aards kruid", - "CUPPING_112": "Grond", - "CUPPING_113": "Vers hout", - "CUPPING_114": "Ceder", - "CUPPING_115": "Tabak", - "CUPPING_116": "Hooi \/ stro", - "CUPPING_117": "Bladgroenten", - "CUPPING_118": "Olijf", - "CUPPING_119": "Groene peper", - "CUPPING_120": "Pompoen", - "CUPPING_121": "Paddestoel", - "CUPPING_122": "Zoete erwt", - "CUPPING_123": "Sneeuwerwt", - "CUPPING_124": "Grassig", - "CUPPING_125": "Dille", - "CUPPING_126": "Salie", - "CUPPING_127": "Munt", - "CUPPING_128": "Groene thee", - "CUPPING_129": "Zwarte thee", - "CUPPING_130": "Hop", - "CUPPING_131": "Bergamot", - "CUPPING_132": "Bloemrijk", - "CUPPING_133": "Bloemen", - "CUPPING_134": "Hibiscus", - "CUPPING_135": "Rozenbottels", - "CUPPING_136": "Lavendel", - "CUPPING_137": "Magnolia", - "CUPPING_138": "Jasmijn kamperfoelie", - "CUPPING_139": "Oranjebloesem", - "CUPPING_140": "Citroengras", - "WATER_SECTION": { - "NAV_WATER": "Water", - "YOU_GOT_NO_ARCHIVED_WATER": "Je hebt nog geen water gearchiveerd", - "YOU_GOT_NO_WATER": "Je hebt nog geen water toegevoegd", - "CATEGORY_INFORMATION": "Water informatie", - "CATEGORY_GENERAL": "Algemeen", - "WATER_BOTTLE_EXPLANATION": "Waterflessen vermelden de concentratie meestal in eenheden van ppm = mg\/L", - "USED_TIMES": "Aantal keren gebruikt", - "AMOUNT": "Gebruikte hoeveelheid", - "WATER": { - "GENERAL_HARDNESS": "Algemene hardheid (GH)", - "TOTAL_ALKALINITY": "Totale alkaliteit (KH)", - "CALCIUM": "Kalium (Ca)", - "MAGNESIUM": "Magnesium (Mg)", - "SODIUM": "Natrium (Na)", - "TDS": "Totaal opgeloste vaste stoffen (TDS)", - "UNITS": "Eenheden", - "PLACE_HOLDER": { - "GENERAL_HARDNESS": "Algemene hardheid", - "TOTAL_ALKALINITY": "Totale alkaliteit", - "CALCIUM": "Kalium (Ca)", - "MAGNESIUM": "Magnesium (Mg)", - "SODIUM": "Natrium (Na)", - "TDS": "Totaal opgeloste vaste stoffen (TDS)", - "NAME": "Voeg waternaam toe", - "NOTES": "Voeg wat notities toe voor je water", - "POTASSIUM": "Kalium (K)", - "CHLORIDE": "Chloride (Cl)", - "SULFATE": "Sulfaat (SO4)" - }, - "WATER_UNIT": { - "UNKNOWN": "Onbekend", - "PPM": "ppm als CaCO3", - "MG_L": "mg\/L", - "MMOL_L": "mmol\/L", - "DH": "°dH" - }, - "POTASSIUM": "Kalium (K)", - "CHLORIDE": "Chloride (Cl)", - "SULFATE": "Sulfaat (SO4)" - } - }, - "BREW_BRIX_CALCULATION": "Graden Brix", - "SET_TDS": "TDS instellen", - "TOTAL_WEIGHT": "Totaal gewicht", - "CALCULATED_WEIGHT": "Berekend gewicht", - "SET_WEIGHT": "Gewicht instellen", - "ADD_FLAVORS_AROMAS_TITLE": "Aroma's \/ Smaken", - "CUSTOM_FLAVORS_AROMAS": "Individueel aroma", - "CUSTOM_FLAVORS_AROMAS_PLACEHOLDER": "Voeg je individuele aroma toe", - "PREDEFINED_FLAVORS_AROMAS": "Veel voorkomende aroma's", - "ADD_AROMA_FLAVOR": "Aroma's\/smaken toevoegen", - "BEAN_WEIGHT_IN_PLACEHOLDER": "Bonen uit de verpakking gehaald", - "VESSEL_PLACEHOLDER": "Naam van het Serveerkan en het leeggewicht ervan", - "GRIND_WEIGHT_PLACEHOLDER": "Gewicht van de gemalen bonen die voor het brouwen worden gebruikt", - "PRESET_BREW_TITLE": "Gebruik laatste brouwsel als voorinstelling", - "CUPPING_BREW_TAB_AROMA": "Aroma", - "CUPPING_BREW_TAB_TASTING": "Systematische cupping", - "WATER_PLACEHOLDER": "Activeer het watergedeelte in het instellingenmenu voor volledige functionaliteit", - "PAGE_SETTINGS_SCALES": "Weegschalen", - "CONNECT": "Verbinden", - "DISCONNECT": "Loskoppelen", - "SCALE": { - "BLUETOOTH_SCAN_RUNNING": "Zoeken naar weegschaal tot 60s", - "BLUETOOTH_NOT_ENABLED": "Bluetooth niet geactiveerd, activeer het om het goed te laten werken", - "CONNECTION_NOT_ESTABLISHED": "Weegschaal niet gevonden, of verbinding kon niet tot stand worden gebracht", - "CONNECTED_SUCCESSFULLY": "Weegschaal verbonden", - "DISCONNECTED_SUCCESSFULLY": "Weegschaal losgekoppeld", - "DISCONNECTED_UNPLANNED": "Weegschaal onverwachts losgekoppeld", - "REQUEST_PERMISSION": { - "LOCATION": "Om Bluetooth-weegschalen te vinden, heeft de app toegang nodig tot de locatie.", - "BLUETOOTH": "Om bluetooth-weegschalen te vinden, heeft de app toegang tot bluetooth nodig" - }, - "INFORMATION_DESCRIPTION": "Ondersteunde weegschalen zijn: Decent Scale, Acaia, Felicita, Hiroia Jimmy, Skale 2, DiFluid Microbalance, Smartchef Scale, Blackcoffee.io, Bookoo Mini Scale en Eureka Precisa. Let op: Als de Eureka Precisa een negatieve waarde ontvangt, stopt de timer" - }, - "QR": { - "WRONG_QRCODE_DESCRIPTION": "Ongeldige QR-code of onbekende inhoud", - "WRONG_QRCODE_TITLE": "Fout", - "WRONG_LINK_DESCRIPTION": "Ongeldige QR-link", - "WRONG_LINK_TITLE": "Fout", - "SERVER": { - "ERROR_OCCURED": "Er is een fout opgetreden. De QR-code kon niet worden gelezen. Probeer het opnieuw.", - "BEAN_NOT_APPROVED": "Boon is nog niet goedgekeurd, probeer het later nog eens" - }, - "BEAN_SUCCESSFULLY_SCANNED": "Bean succesvol gescand", - "BEAN_SUCCESSFULLY_REFRESHED": "Boon succesvol bijgewerkt", - "IMAGES_GETTING_DOWNLOADED": "Afbeeldingen downloaden" - }, - "RETRY_CONNECT": "Verbinding opnieuw proberen", - "SMART_SCALE_STAY_CONNECTED_ON_APP_MINIMIZE": "Houd de weegschaal verbonden, zelfs als de app op de achtergrond draait", - "BREW_FLOW_WEIGHT": "Gewicht", - "BREW_FLOW_WEIGHT_PER_SECOND": "Stroom (afgevlakt)", - "ROAST_TYPE_UNKNOWN": "onbekend", - "ROAST_TYPE_CINNAMON_ROAST": "Cinnamon Branding", - "ROAST_TYPE_AMERICAN_ROAST": "Amerikaans Branding", - "ROAST_TYPE_NEW_ENGLAND_ROAST": "Nieuw-Engeland Branding", - "ROAST_TYPE_HALF_CITY_ROAST": "Half City Branding", - "ROAST_TYPE_MODERATE_LIGHT_ROAST": "Matig-lichte branding", - "ROAST_TYPE_CITY_ROAST": "City Branding", - "ROAST_TYPE_CITY_PLUS_ROAST": "City+ Branding", - "ROAST_TYPE_FULL_CITY_ROAST": "Full City Branding", - "ROAST_TYPE_FULL_CITY_PLUS_ROAST": "Full City + Branding", - "ROAST_TYPE_ITALIAN_ROAST": "Ialiaanse Branding", - "ROAST_TYPE_VIEANNA_ROAST": "Weense Branding", - "ROAST_TYPE_FRENCH_ROAST": "Franse Branding", - "ROAST_TYPE_CUSTOM_ROAST": "Aangepast", - "BEAN_MIX_UNKNOWN": "onbekend", - "BEAN_MIX_SINGLE_ORIGIN": "Single Origin", - "BEAN_MIX_BLEND": "Melange", - "BEAN_ROASTING_TYPE_FILTER": "Filter", - "BEAN_ROASTING_TYPE_ESPRESSO": "Espresso", - "BEAN_ROASTING_TYPE_OMNI": "Omni", - "BEAN_ROASTING_TYPE_UNKNOWN": "Onbekend", - "SMART_SCALE_LOG": "Activeer logbestanden voor slimme weegschaal (alleen voor foutopsporing)", - "TOAST_PREPARATION_TOOL_EDITED_SUCCESSFULLY": "Voorbereidingstool bewerkt", - "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE": "Bluetooth-weegschaal", - "PAGE_SETTINGS_TAB_GENERAL": "Algemeen", - "SMART_SCALE_TARE_ON_BREW": "Tarraweegschaal op nieuw brouwsel", - "SMART_SCALE_TARE_ON_START_TIMER": "Tarra weegschaal bij het starten van de timer", - "PAGE_SETTINGS_BREW_RATING_STEPS": "Beoordelingsstappen", - "BREW_AVG_FLOW_WEIGHT_PER_SECOND": "Ø Stroom", - "CUSTOM_LIST_VIEW_PARAMETERS": "Parameters voor lijstweergave", - "NAV_LIST_VIEW_CUSTOM_PARAMETERS": "Parameters voor lijstweergave", - "PAGE_LIST_VIEW_CUSTOM_PARAMETERS_DESCRIPTION": "Bepaal welke parameters moeten worden weergegeven op de tegels van de lijstweergave", - "BREW_DATA_VESSEL_NAME_WEIGHT": "Serveerkan Naam\/Gewicht", - "IGNORE_NEGATIVE_VALUES": "Negeer negatieve gewichtswaarden", - "IGNORE_ANOMALY_VALUES": "Negeer afwijkende waarden", - "IGNORE_ANOMALY_VALUES_TOOLTIP": "Bijvoorbeeld: Een kopje rond bewegen op de weegschaal", - "TOAST_BEAN_FAVOURITE_ADDED": "Favoriet toegevoegd", - "TOAST_BEAN_FAVOURITE_REMOVED": "Favoriet verwijderd", - "QR_CODE_SCANNER_INFORMATION_TITLE": "QR-code", - "QR_CODE_SCANNER_INFORMATION_DESCRIPTION": "Alle gescande boneninformatie komt rechtstreeks van de branderij. Mocht u onjuiste of misleidende informatie aantreffen, laat het mij dan weten via e-mail: info@beanconqueror.com.", - "DONT_SHOW_AGAIN": "Niet meer weergeven", - "ARCHIVED_TOOLS": "Gearchiveerd gereedschap", - "UNARCHIVE": "Herstel", - "PAGE_SETTINGS_HIDE_ARCHIVED_BREWS_DASHBOARD": "Toon gearchiveerde brouwsels op dashboard", - "PAGE_SETTINGS_HIDE_ARCHIVED_BREWS_DASHBOARD_DESCRIPTION": "Moeten gearchiveerde brouwsels op de startpagina worden weergegeven?", - "COPY": "Kopiëren", - "TOAST_PREPARATION_METHOD_REPEATED_SUCCESSFULLY": "Bereidingsmethode succesvol gekopieerd", - "PREPARATION_TYPE_CAFEC_FLOWER": "Cafec Flower", - "PREPARATION_TYPE_DECEMBER_DRIPPER": "December Dripper", - "PREPARATION_TYPE_DECENT_ESPRESSO": "Decent Espresso", - "PREPARATION_TYPE_HARIO_SWITCH": "Hario Switch", - "PREPARATION_TYPE_HARIO_WOODNECK": "Hario Woodneck", - "PREPARATION_TYPE_RATIO_SIX_COFFEE_BREWER": "Ratio Six-koffiezetapparaat", - "PREPARATION_TYPE_ROK": "ROK", - "PREPARATION_TYPE_TORNADO_DUO": "Tornado-duo", - "PREPARATION_TYPE_TRICOLATE": "Tricolate", - "QR_CODE_REFRESH_DATA_MESSAGE": "Alle informatie voor deze boon wordt overschreven, doorgaan?", - "POPOVER_QR_CODE_REFRESH": "Gegevens opnieuw laden", - "BREW_FLOW_WEIGHT_REALTIME": "Stroom (realtime)", - "SMART_SCALE_STOP_TIMER_ON_BREW": "Stop de timer van de weegschaal bij een nieuw brouwsel", - "SMART_SCALE_RESET_TIMER_ON_BREW": "Reset de weegschaaltimer bij een nieuw brouwsel", - "BREW_PRESSURE_FLOW": "Druk", - "BREW_TEMPERATURE_REALTIME": "Temperatuur", - "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_SHOW_GRAPHS_FILTER": "Grafieken weergeven voor filter", - "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_SHOW_GRAPHS_ESPRESSO": "Toon grafieken voor espresso", - "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE": "Drukapparaat", - "PAGE_SETTINGS_TAB_BLUETOOTH_TEMPERATURE": "Temperatuur apparaat", - "PRESSURE_LOG": "Activeer logbestanden voor drukapparaat", - "PRESSURE_THRESHOLD_ACTIVE": "Starttimer met vooraf gedefinieerde druk", - "PRESSURE_THRESHOLD_BAR": "Drempelwaarde druk", - "TIMER_MILLISECONDS": "MS", - "PAGE_SETTINGS_BREW_ENABLE_MILLISECONDS": "Milliseconden?", - "PAGE_SETTINGS_BREW_ENABLE_MILLISECONDS_DESCRIPTION": "Gebruik milliseconden om uw brouwsels nog nauwkeuriger te analyseren", - "PAGE_SETTINGS_BEAN_RATING": "Bonen beoordeling", - "PAGE_SETTINGS_BEAN_RATING_TOOLTIP": "Is de standaard '0 tot 5' niet de juiste beoordeling voor u? U kunt in plaats daarvan een '0 tot 100'-schaal gebruiken", - "PAGE_SETTINGS_BEAN_RATING_STEPS": "Stappen voor het beoordelen van bonen", - "COFFEE_GRAMS_BREWED": "grammen gebrouwen", - "SMART_SCALE_MAXIMIZE_ON_START_TIMER": "Maximaliseer realtime-grafiek bij het starten van de timer", - "PRESSURE": { - "CONNECTION_NOT_ESTABLISHED": "Drukapparaat niet gevonden, of verbinding kon niet tot stand worden gebracht", - "CONNECTED_SUCCESSFULLY": "Drukapparaat aangesloten", - "DISCONNECTED_SUCCESSFULLY": "Drukapparaat losgekoppeld", - "DISCONNECTED_UNPLANNED": "Drukapparaat onverwachts losgekoppeld", - "STAY_CONNECTED_ON_APP_MINIMIZE": "Houd het drukapparaat aangesloten, zelfs als de app op de achtergrond draait", - "INFORMATION_DESCRIPTION": "Ondersteunde apparaten zijn: Popsicle, Pressensor, Smart Espresso Profiler, Bookoo Espresso Monitor", - "BLUETOOTH_SCAN_RUNNING": "Zoeken naar drukapparaat gedurende maximaal 60 seconden", - "BLUETOOTH_NOT_ENABLED": "Bluetooth niet geactiveerd, activeer het om het goed te laten werken", - "REQUEST_PERMISSION": { - "LOCATION": "Om drukapparaten te vinden, heeft de app toegang nodig tot de locatie.", - "BLUETOOTH": "Om drukapparaten te vinden, heeft de app toegang tot Bluetooth nodig" - } - }, - "POPOVER_BLUETOOTH_ACTION_RECONNECT_SCALE": "Weegschaal opnieuw verbinden", - "POPOVER_BLUETOOTH_ACTION_RECONNECT_PRESSURE_DEVICE": "Drukapparaat opnieuw aansluiten", - "POPOVER_SHOW_BREWS": "Toon brouwsels", - "LAST_USED_GRIND_SIZE_SETTING": "Laatste maalstand", - "LAST_USED_BEAN": "Laatste boon", - "PAGE_SETTINGS_BREW_MILLISECONDS_DECIMAL_PLACES_DESCRIPTION": "Hoeveel decimalen moeten er worden weergegeven?", - "SMART_SCALE_COMMAND_DELAY": "Opdracht vertraging", - "SMART_SCALE_COMMAND_DELAY_TOOLTIP": "Hoeveel tijd moet er verstrijken tussen elk bluetooth-commando?", - "SUPPORT_ME": "Donatie", - "PAGE_SETTINGS_BREW_DISPLAY_BEAN_IMAGE": "Bonenafbeeldingen op brouwsels weergeven?", - "PAGE_SETTINGS_BREW_DISPLAY_BEAN_IMAGE_DESCRIPTION": "Als er een bonenafbeelding bestaat, wordt deze gebruikt in plaats van de afbeelding van de bereidingsmethode", - "DOWNLOAD_XLSX": "Excel downloaden", - "DOWNLOAD_JSON": "JSON downloaden", - "NAV_BEAN_PARAMS": "Boon Parameters", - "BEAN_DATA_ROAST_NAME_TYPE": "Brand graad", - "ENABLE_BEAN_SORT_INFORMATION": "Variëteit-informatie inschakelen", - "BEAN_SORT_MIX": "Bonenmix", - "PAGE_MANAGE_BEAN_PARAMETERS_DESCRIPTION": "Markeer welke parameters moeten worden weergegeven bij het bewerken van bean-informatie.", - "PAGE_BEAN_LIST_VIEW_CUSTOM_PARAMETERS_DESCRIPTION": "Bepaal de parameters die moeten worden weergegeven in lijstweergaven voor bonen", - "BEAN_DATA_NAME_TOOLTIP": "De naam van de bonen", - "BEAN_DATA_ROASTER_TOOLTIP": "Wie is de brander", - "BEAN_DATA_BUY_DATE_TOOLTIP": "Wanneer heb je de bonen gekocht?", - "BEAN_DATA_ROASTING_DATE_TOOLTIP": "Wat is de branddatum van de bonen?", - "BEAN_DATA_ROASTING_TYPE_TOOLTIP": "Voor welke bereidingswijze is deze boon geroosterd, bijvoorbeeld Filter", - "BEAN_DATA_ROAST_NAME_TOOLTIP": "Wat is de brandingsgraad van de bonen?", - "BEAN_DATA_ROAST_NAME_TYPE_TOOLTIP": "Welk type branding (Cinnamon, New England, etc.)", - "BREW_DATA_RATING_TOOLTIP": "Je waardering", - "BEAN_SORT_MIX_TOOLTIP": "Zijn de bonen een melange?", - "BEAN_DATA_WEIGHT_TOOLTIP": "Hoeveel wegen de bonen?", - "BEAN_DATA_COST_TOOLTIP": "Hoeveel hebben de bonen gekost?", - "BEAN_DATA_AROMATICS_TOOLTIP": "Welke aroma's\/smaken hebben de bonen?", - "BEAN_DATA_CUPPING_POINTS_TOOLTIP": "Hoeveel cupping-punten heeft de boon?", - "BEAN_DATA_DECAFFEINATED_TOOLTIP": "Is het cafeïnevrij?", - "BEAN_DATA_URL_TOOLTIP": "URL naar de winkel", - "BEAN_DATA_EAN_TOOLTIP": "Het EAN\/artikelnummer", - "NOTES_TOOLTIP": "Aantekeningen gemaakt", - "BREW_DATA_ATTACHMENTS_TOOLTIP": "Foto's toevoegen?", - "ENABLE_BEAN_SORT_INFORMATION_TOOLTIP": "Meer informatie over de bonen: waar komen ze vandaan, wanneer zijn ze geoogst, etc.", - "BEAN_DATA_COUNTRY_TOOLTIP": "Uit welk land komen de bonen?", - "BEAN_DATA_REGION_TOOLTIP": "Uit welke regio komen de bonen?", - "BEAN_DATA_FARM_TOOLTIP": "Van welke boerderij komen de bonen?", - "BEAN_DATA_FARMER_TOOLTIP": "Wie verbouwde deze bonen", - "BEAN_DATA_ELEVATION_TOOLTIP": "Op welke hoogte groeiden de bonen?", - "BEAN_DATA_PROCESSING_TOOLTIP": "Hoe zijn de bonen verwerkt (natuurlijk, gewassen, enz.)", - "BEAN_DATA_VARIETY_TOOLTIP": "De Koffieboon Variëteit (SL28 bv.)", - "BEAN_DATA_HARVEST_TIME_TOOLTIP": "In welk jaar\/maand werden deze bonen geoogst?", - "BEAN_DATA_PERCENTAGE_TOOLTIP": "Hoeveel procent van deze bonen zit er in de zak?", - "BEAN_DATA_CERTIFICATION_TOOLTIP": "Welke certificeringen hebben de bonen (bijvoorbeeld fairtrade)", - "BEAN_DATA_PURCHASING_PRICE_TOOLTIP": "Wat was de aankoopprijs van de koffiebrander", - "BEAN_DATA_FOB_PRICE_TOOLTIP": "Wat was de Free-On-Board (FOB) prijs toen de brander het kocht?", - "BEAN_PARAMETER_CUSTOMIZE_TITLE": "Pas de informatie aan die u voor bonen wilt gebruiken", - "BEAN_PARAMETER_CUSTOMIZE_DESCRIPTION": "Er kan veel informatie over de bonen worden ingevoerd of gebruikt. Kies zelf de parameters die u wilt invullen en welke u wilt weergeven", - "BREW_CANT_START_BECAUSE_TIMER_NOT_RESETTED_TITLE": "Timer resetten!", - "BREW_CANT_START_BECAUSE_TIMER_NOT_RESETTED_DESCRIPTION": "Omdat je een Bluetooth-apparaat hebt aangesloten, moet je eerst je timer resetten voordat je kunt beginnen.", - "BREW_FILTER_JUST_CHART_DATA": "Alleen grafieken", - "SCALE_RESET_TRIGGERED_DESCRIPTION": "De bluetooth weegschaal wil zowel de app-timer als de brouwgrafiek resetten, wil je doorgaan?", - "SCALE_RESET_TRIGGERED_TITLE": "Opnieuw instellen?", - "NAV_REPEAT_PARAMETERS": "Herhalingsparameters definiëren", - "PAGE_REPEAT_PARAMETERS_DESCRIPTION": "Markeer welke parameters vooraf moeten worden ingesteld wanneer u een specifieke brouwbeurt herhaalt", - "CUSTOM_REPEAT_PARAMETERS": "Herhalen", - "CUSTOM_REPEAT_PARAMETERS_DESCRIPTION": "Aangepaste herhaling activeren?", - "PAGE_SETTINGS_USE_NUERMIC_KEYBOARD_FOR_GRIND_SIZE": "Numeriek toetsenbord voor maalinstelling?", - "PAGE_SETTINGS_USE_NUERMIC_KEYBOARD_FOR_GRIND_SIZE_DESCRIPTION": "Wilt u een numeriek toetsenbord gebruiken voor de maalinstelling, in plaats van het hele toetsenbord?", - "PREPARATION_DEVICE": { - "TYPE": { - "NONE": "Geen", - "XENIA": "Xenia", - "METICULOUS": "Meticulous" - }, - "URL": "URL-adres", - "CHOOSE_DEVICE": "Kies apparaat", - "CONNECTION": { - "UNSUCCESFULLY": "Er kon geen verbinding met de machine worden gemaakt", - "SUCCESFULLY": "Verbinding met machine was succesvol" - }, - "TYPE_XENIA": { - "TITLE": "Xenia machine", - "PRESS_START_SCRIPT": "Start script bij start", - "FIRST_DRIP_SCRIPT": "Start script bij eerste druppel", - "SCRIPT_AT_WEIGHT": "Gewicht voor scriptuitvoering", - "SCRIPT_LIST_GENERAL_0": "Niets", - "SCRIPT_LIST_GENERAL_1": "Espresso, 25 seconden", - "SCRIPT_LIST_GENERAL_2": "Espresso, eindeloos", - "SCRIPT_LIST_GENERAL_STOP": "Schot stoppen", - "CHOOSE_SCRIPT_AT_WEIGHT": "Kies script voor gewicht bereikt", - "ERROR_NOT_ALL_SCRIPTS_FOUND": "Een of meer scripts konden niet worden gevonden, ze zijn gereset", - "ERROR_CONNECTION_COULD_NOT_BE_ESTABLISHED": "De verbinding met de xenia espressomachine kon niet tot stand worden gebracht. Controleer of u zich op het juiste netwerk (LAN) bevindt, of u het juiste IP-adres hebt ingevoerd, etc.", - "CHECKING_CONNECTION_TO_PORTAFILTER": "Verbinding met Xenia wordt gecontroleerd", - "GRABING_SCRIPTS": "Scripts laden vanuit Xenia", - "BREW_BY_WEIGHT_ACTIVE": "Brouwen op gewicht actief" - }, - "API_VERSION": "Api-versie", - "RESIDUAL_LAG_TIME": "Resterende vertragingstijd", - "RESIDUAL_LAG_TIME_DESCRIPTION": "Stel de tijd in die overeenkomt met het resterende water. Met een naakte portafilter heb je een lagere tijd nodig, met een tuit een hogere. Hoe langzamer uw weegschaal het gewicht rapporteert, hoe meer tijd u nodig heeft", - "TYPE_METICULOUS": { - "TITLE": "Meticulous machine", - "NO_PROFILE": "Geen profiel", - "CHOOSE_PROFILE": "Profiel kiezen", - "SHOT_STARTED": "Brouwen gestart", - "SHOT_ENDED": "Brouwen gestopt" - } - }, - "DEVICE_CONNECTION": "Apparaatverbinding", - "PREPARATION_DEVICE_CONNECTION": "Apparaatverbinding", - "MANUAL_EXPORT_TO_VISUALIZER": "Exporteren naar Visualizer", - "ONLY_FAVOURITES": "Alleen favorieten", - "TEMPERATURE": { - "CONNECTION_NOT_ESTABLISHED": "Temperatuurapparaat niet gevonden, of verbinding kon niet tot stand worden gebracht", - "CONNECTED_SUCCESSFULLY": "Temperatuurapparaat aangesloten", - "DISCONNECTED_SUCCESSFULLY": "Temperatuurapparaat losgekoppeld", - "DISCONNECTED_UNPLANNED": "Temperatuurapparaat onverwachts losgekoppeld", - "STAY_CONNECTED_ON_APP_MINIMIZE": "Houd de thermometer verbonden, zelfs als de app op de achtergrond staat", - "INFORMATION_DESCRIPTION": "Ondersteunde apparaten zijn: ETI Ltd BLE-thermometers (ThermaQ Blue, BlueTherm, enz.), Combustion Inc., Meater (niet Meater+ of Meater 2)", - "BLUETOOTH_SCAN_RUNNING": "Zoeken naar temperatuurapparaat gedurende maximaal 60 seconden", - "BLUETOOTH_NOT_ENABLED": "Bluetooth niet geactiveerd, activeer het om het goed te laten werken", - "REQUEST_PERMISSION": { - "LOCATION": "Om temperatuurmeters te vinden, heeft de app toegang nodig tot de locatie.", - "BLUETOOTH": "Om temperatuurapparaten te vinden, heeft de app toegang tot Bluetooth nodig" - }, - "LOG": "Logbestanden voor temperatuurapparaat activeren", - "THRESHOLD_ACTIVE": "Start timer met vooraf ingestelde temperatuur", - "THRESHOLD_TEMP": "Drempel temperatuur" - }, - "POPOVER_BLUETOOTH_ACTION_RECONNECT_TEMPERATURE_DEVICE": "Temperatuurapparaat opnieuw aansluiten", - "PRESSURE_DEVICE_JUST_VISIBLE_ON_ESPRESSO": "Drukapparaat is alleen bruikbaar bij de bereidingswijze 'Espresso'", - "PRESSURE_MESSAGE_AFTER_CONNECTION": "Bekend gedrag (in analyse): Houd er rekening mee dat het langer kan duren om verbinding te maken nadat u uw telefoon opnieuw hebt opgestart of wanneer u de app langere tijd niet hebt gebruikt. Na deze tijd zou het verbindingsprobleem opgelost moeten zijn.", - "SMART_SCALE_ACAIA_HEARTBEAT_TIMER": "Hartslagtimer - Speciaal voor Acaia Scales", - "SMART_SCALE_ACAIA_HEARTBEAT_TIMER_TOOLTIP": "Alleen gebruikt voor Acaia-weegschalen! - Oudere Acaia-weegschalen hebben een hartslagsignaal nodig. Als u problemen ondervindt, probeer dan de hartslag op een hogere frequentie in te stellen.", - "SHARE_BEAN_URL": "Delen als URL", - "SHARE_BEAN_IMAGE": "Delen als afbeelding", - "SMART_SCALE_ESPRESSO_STOP_ON_NO_WEIGHT_CHANGE": "Espresso - Automatisch stoppen met brouwen", - "SMART_SCALE_ESPRESSO_STOP_ON_NO_WEIGHT_CHANGE_DESCRIPTION": "Deze instelling wordt alleen gebruikt voor brouwsels van het type 'espresso'. Het brouwsel wordt automatisch gestopt wanneer er geen flow wordt gedetecteerd.", - "BREW_DATA_BREW_QUANTITY_TOOLTIP_BREW_RATIO": "De zetverhouding wordt berekend met deze waarde voor alle brouwsels, maar niet voor type 'espresso'.", - "BREW_DATA_BREW_BEVERAGE_QUANTITY_TOOLTIP_BREW_RATIO": "De zetverhouding wordt alleen met deze waarde berekend voor brouwsels van het type 'espresso'", - "SMART_SCALE_DID_NOT_SEND_ANY_WEIGHT_DESCRIPTION": "Het lijkt erop dat de bluetooth weegschaal niet goed is aangesloten. Dit gebeurt meestal op iOS-apparaten met Acaia weegschalen, sluit de weegschaal opnieuw aan en zorg ervoor dat de gewichtstegel wordt bijgewerkt.", - "SMART_SCALE_DID_NOT_SEND_ANY_WEIGHT_TITLE": "Geen gewichtswaarden?", - "SMART_SCALE_ESPRESSO_STOP_ON_NO_WEIGHT_CHANGE_MIN_FLOW_DESCRIPTION": "De stroomsnelheid waarbij het zetten moet worden gestopt. Het zetten wordt pas gestopt als er minimaal 5 seconden zijn verstreken, er 5 gram in de kop zit of als u het gewicht van de gemalen koffie hebt ingevoerd - er is minimaal een zetverhouding van 1:1 bereikt (bijv.: 18 g erin, 18 g eruit). Nadat aan deze voorwaarden is voldaan, wordt de hier ingestelde minimale stroomsnelheid gecontroleerd", - "ONLY_BEST_BREWS": "Alleen de beste brouwsels", - "POPOVER_BEST_BREW": "Beste brouwsel", - "PAGE_SETTINGS_BEST_BREW": "Beste brouwsels activeren", - "PAGE_SETTINGS_BEST_BREW_DESCRIPTION": "Markeer je beste brouwsel voor een specifieke boon. Elke boon kan één beste brouwsel hebben in plaats van meerdere favorieten.", - "PAGE_SETTINGS_TAB_BLUETOOTH_REFRACTOMETER": "Refractometer apparaat", - "REFRACTOMETER": { - "CONNECTION_NOT_ESTABLISHED": "Refractometerapparaat niet gevonden, of verbinding kon niet tot stand worden gebracht", - "CONNECTED_SUCCESSFULLY": "Refractometerapparaat aangesloten", - "DISCONNECTED_SUCCESSFULLY": "Refractometerapparaat losgekoppeld", - "DISCONNECTED_UNPLANNED": "Refractometerapparaat onverwachts losgekoppeld", - "STAY_CONNECTED_ON_APP_MINIMIZE": "Houd de refractometer verbonden, zelfs als de app op de achtergrond draait", - "INFORMATION_DESCRIPTION": "Ondersteunde apparaten zijn: DiFluid R2", - "BLUETOOTH_SCAN_RUNNING": "Zoeken naar refractometer-apparaat gedurende maximaal 60 seconden", - "BLUETOOTH_NOT_ENABLED": "Bluetooth niet geactiveerd, activeer het om het goed te laten werken", - "REQUEST_PERMISSION": { - "LOCATION": "Om refractometerapparaten te vinden, heeft de app toegang nodig tot de locatie.", - "BLUETOOTH": "Om refractometerapparaten te vinden, heeft de app toegang tot Bluetooth nodig" - }, - "LOG": "Activeer logbestanden voor refractometerapparaat", - "READ_END": "Test voltooid - resultaat ontvangen" - }, - "COPIED_TO_CLIPBOARD_SUCCESSFULLY": "Toegevoegd aan klembord", - "COPIED_TO_CLIPBOARD_UNSUCCESSFULLY": "Kan niet worden toegevoegd aan het klembord", - "PAGE_SETTINGS_LANGUAGE_FRENCH": "Frans", - "ANDROID_EXTERNAL_FILE_ACCESS_NOT_POSSIBLE_TITLE": "Gegevens kunnen niet worden opgeslagen vanwege Android-beperkingen", - "ANDROID_EXTERNAL_FILE_ACCESS_NEEDED_DESCRIPTION": "Je Android-telefoon ondersteunt geen externe bestandssystemen, dus je moet het ZIP-bestand downloaden zonder mediabestanden. Ga voor meer informatie naar https:\/\/beanconqueror.com\/faq.", - "PAGE_SETTINGS_SECURITY_CHECK_WHEN_GOING_BACK": "Beveiligingsbericht voor afsluiten?", - "PAGE_SETTINGS_SECURITY_CHECK_WHEN_GOING_BACK_DESCRIPTION": "Controleer of de gegevens zijn gewijzigd bij het toevoegen\/bewerken van bonen of brouwsels; als dat het geval is, wordt er een veiligheidswaarschuwing weergegeven bij het teruggaan.", - "PAGE_BEANS_DISCARD_CONFIRM": "Boon informatie is gewijzigd. Weet je zeker dat je de pagina wilt verlaten zonder op te slaan?", - "PAGE_BREW_DISCARD_CONFIRM": "De brouw informatie is gewijzigd. Weet je zeker dat je de pagina wilt verlaten zonder op te slaan?", - "NO_ENTRIES_FOUND": "Geen vermeldingen gevonden", - "POPOVER_BEANS_OPTION_REPEAT": "Herhaal laatste brouwsel", - "REPEAT_LAST_BREW": "Herhaal laatste brouwsel", - "REPEAT_BEST_BREW": "Herhaal beste brouwsel", - "PAGE_SETTINGS_VISUALIZER_SECTION": "Visualizer", - "VISUALIZER": { - "ACTIVATE": "Visualizer activeren", - "CHOOSE_SERVER": "Server kiezen", - "CONNECTION": { - "UNSUCCESSFULLY": "Er kon geen verbinding tot stand worden gebracht.", - "SUCCESSFULLY": "Verbinding kon tot stand worden gebracht." - }, - "SERVER": { - "VISUALIZER": "Visualizer-server", - "CUSTOM": "Aangepaste server" - }, - "SHOT": { - "UPLOAD_SUCCESSFULLY": "Brouwen geüpload naar visualizer.", - "UPLOAD_UNSUCCESSFULLY": "Brouwen kon niet worden geüpload naar de Visualizer." - }, - "URL": "Server-URL", - "USERNAME": "Gebruikersnaam", - "PASSWORD": "Wachtwoord", - "UPLOAD_AUTOMATIC": "Elke brouwsessie automatisch uploaden?", - "UPLOAD_AUTOMATIC_TOOLTIP": "Zorg ervoor dat u een actieve internetverbinding hebt wanneer u uw brouwsel opslaat.", - "UPLOAD_ALL": "Alle brouwsels uploaden", - "NOT_ALL_SHOTS_UPLOADED": "Niet alle brouwsels konden worden geüpload", - "ALL_SHOTS_UPLOADED": "Alle brouwsels zijn geüpload" - }, - "SMART_SCALE_AUTO_START_LISTENING": "Timer automatisch starten?", - "SMART_SCALE_AUTO_START_LISTENING_DESCRIPTION": "Start de timer automatisch bij het bereiken van de volgende gewichtswaarde", - "CHOOSE_REFERENCE_GRAPH": "Referentiegrafiek selecteren", - "RESET": "Opnieuw instellen", - "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_GRAPHS_AXIS": "Definieer de beginassen van de grafiek", - "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_GRAPHS_AXIS_DESCRIPTION": "Stel de begingrootte van de assen in voor zowel filter- als espressokoffie.", - "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_GRAPHS_FILTER_WEIGHT": "Filter - Gewicht", - "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_GRAPHS_FILTER_FLOW": "Filter - Doorstroming", - "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_GRAPHS_ESPRESSO_WEIGHT": "Espresso - Gewicht", - "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_GRAPHS_ESPRESSO_FLOW": "Espresso - Doorstroming", - "SMART_SCALE_IGNORE_INCOMING_WEIGHT": "Negeer huidige gewichtsoverdracht ", - "SMART_SCALE_IGNORE_INCOMING_WEIGHT_TOOLTIP": "Er verschijnt een nieuwe knop in het brouwgedeelte, die nieuwe gewichtswaarden van de bluetooth weegschaal zal negeren.", - "BREWS_ACTIVE": "Actieve brouwsels", - "BREWS_ARCHIVED": "Gearchiveerde brouwsels", - "GRAPHS": "Grafieken", - "GRAPH_SECTION": { - "NAV_GRAPH": "Grafieken", - "NO_ARCHIVED_ENTRIES": "Geen gearchiveerde vermeldingen", - "NO_ENTRIES": "Geen vermeldingen", - "SECTION_HAS_BEEN_ACTIVATED": "Grafieksectie is geactiveerd" - }, - "TOAST_GRAPH_ARCHIVED_SUCCESSFULLY": "Grafiek gearchiveerd", - "TOAST_GRAPH_DELETED_SUCCESSFULLY": "Grafiek verwijderd", - "TOAST_GRAPH_EDITED_SUCCESSFULLY": "Grafiek bewerkt", - "TOAST_GRAPH_ADD_SUCCESSFULLY": "Grafiek toegevoegd", - "NAV_GRAPH_SECTION": "Grafieken", - "DELETE_GRAPH_QUESTION": "Wilt u deze grafiek verwijderen?", - "PAGE_SETTINGS_SHOW_ARCHIVED_GRAPHS": "Gearchiveerde grafieken weergeven", - "PAGE_SETTINGS_SHOW_GRAPH_SECTION": "Grafieksectie activeren", - "EDIT_GRAPH": "Grafiek bewerken", - "ADD_GRAPH": "Grafiek toevoegen", - "GRAPH": { - "PLACE_HOLDER": { - "NAME": "Grafiek naam", - "NOTES": "Notities" - }, - "UPLOAD": "Grafiek uploaden", - "DELETE": "Grafiek verwijderen", - "UPLOAD_DESCRIPTION": "Importeer een .JSON-bestand dat u kunt downloaden in de brew-detailweergave. U kunt ook gedeelde grafieken van de community importeren die van het type .JSON zijn." - }, - "SHOW_VISUALIZER": "Visualiseerder weergeven", - "NO_BREWS_FOUND": "Geen brouwsels gevonden", - "NO_GRAPHS_FOUND": "Geen grafieken gevonden", - "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_GRAPHS_TIME_AXIS": "Definieer de maximale tijdsas van de grafiek", - "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_GRAPHS_TIME_DESCRIPTION": "Stel het maximum aantal seconden in op de tijd-as voor zowel filterkoffie als espresso.", - "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_GRAPHS_TIME_FILTER_AXIS_NORMAL_SCREEN": "Filter - Normaal scherm", - "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_GRAPHS_TIME_FILTER_AXIS_FULL_SCREEN_SCREEN": "Filter - Volledig scherm", - "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_GRAPHS_TIME_ESPRESSO_AXIS_NORMAL_SCREEN": "Espresso - Normaal scherm", - "PAGE_SETTINGS_TAB_BLUETOOTH_SCALE_GRAPHS_TIME_ESPRESSO_AXIS_FULL_SCREEN_SCREEN": "Espresso - Volledig scherm", - "PAGE_SETTINGS_DATE_FORMAT": "Datum notatie", - "PAGE_SETTINGS_LANGUAGE_FRANCE": "Frans", - "PAGE_SETTINGS_LANGUAGE_INDONESIA": "Indonesisch", - "EXTRACTION_CHART_TITLE": "Extractie grafiek", - "PAGE_SETTINGS_SHOW_BACKUP_ISSUES": "Back-up problemen weergeven", - "PAGE_SETTINGS_SHOW_BACKUP_ISSUES_DESCRIPTION": "Geef een pop-up weer als er geen back-ups naar het bestandssysteem kunnen worden geschreven", - "AUTOMATIC_BACKUP_DID_FAIL": "Automatische back-up werkte niet, zorg ervoor dat u werkende back-ups hebt! Deze informatie kan worden uitgeschakeld in de instellingen", - "INTERNAL_BACKUP_DID_FAIL": "Interne back-up werkte niet, zorg ervoor dat u werkende back-ups hebt! Deze informatie kan worden uitgeschakeld in de instellingen", - "ZIP_BACKUP_FILE_COULD_NOT_BE_BUILD": "ZIP-bestand kon niet worden opgeslagen! Deze informatie kan worden uitgeschakeld in de instellingen", - "SEND_LOGS": "Logboeken verzenden", - "POPOVER_BLUETOOTH_ACTION_RECONNECT_REFRACTOMETER": "Sluit refractometer apparaat opnieuw aan", - "PAGE_SETTINGS_LANGUAGE_ITALIAN": "Italiaans", - "PAGE_SETTINGS_LANGUAGE_POLISH": "Pools", - "BREW_CANT_START_BECAUSE_TIMER_NOT_RESETTED_GENERAL_DESCRIPTION": "Je moet eerst je timer resetten voordat je kunt beginnen.", - "SMART_SCALE_FIRST_DRIP_THRESHOLD": "Eerste druppel drempel", - "SMART_SCALE_FIRST_DRIP_THRESHOLD_TOOLTIP": "Bij welk weegschaalgewicht moet de eerste druppel worden geteld? Standaard: >= 0,1g", - "PAGE_SETTINGS_BREW_TIMER_START_DELAY_ACTIVE": "Zetvertraging starten", - "PAGE_SETTINGS_BREW_TIMER_START_DELAY_ACTIVE_DESCRIPTION": "Stel een vertragingstijd in die een laadspinner weergeeft totdat het brouwen begint", - "STARTING_IN": "Beginnend in ... {{time}}", - "IOS_DATABASE_ISSUE_TITLE": "ATTENTIE !!!!! - DATABASEVERBINDING VERLOREN", - "IOS_DATABASE_ISSUE_DESCRIPTION": "Het spijt ons u te moeten meedelen dat de verbinding met de database is verbroken. Dit probleem is het gevolg van een onopgeloste bug in het systeem van Apple, waarover Beanconqueror geen controle heeft. Om mogelijk gegevensverlies te voorkomen, verzoeken wij u vriendelijk de Beanconqueror-applicatie onmiddellijk geforceerd af te sluiten en opnieuw te openen.", - "RELOAD_APP": "Herstart de app", - "WATER_TYPE_ADD_CUSTOM": "Water op maat", - "WATER_TYPE_THIRD_WAVE_WATER_CLASSIC_LIGHT_ROAST_PROFILE": "Third Wave Water - Klassiek licht geroosterd profiel", - "WATER_TYPE_THIRD_WAVE_WATER_MEDIUM_ROAST_PROFILE": "Third Wave Water - Middel geroosterd profiel", - "WATER_TYPE_THIRD_WAVE_WATER_DARK_ROAST_PROFILE": "Third Wave Water - Donker geroosterd profiel", - "WATER_TYPE_THIRD_WAVE_WATER_ESPRESSO_MACHINE_PROFILE": "Third Wave Water - Espressomachine Profiel", - "WATER_TYPE_THIRD_WAVE_WATER_COLD_BREW_PROFILE": "Third Wave Water - koud brouwprofiel", - "WATER_TYPE_THIRD_WAVE_WATER_LOW_ACID_PROFILE": "Third Wave Water - Laag Zuurprofiel", - "ADD_WATER": "Voeg water toe", - "EXTRACTION_CHART_LABEL_STRONG_UNDEREXTRACTED": "STERK
onderextractie", - "EXTRACTION_CHART_LABEL_STRONG": "Strong
", - "EXTRACTION_CHART_LABEL_STRONG_HARSH": "STERK
ruw", - "EXTRACTION_CHART_LABEL_UNDEREXTRACTED": "ondergeëxtraheerd", - "EXTRACTION_CHART_LABEL_IDEAL": "IDEAAL", - "EXTRACTION_CHART_LABEL_HARSH": "ruw", - "EXTRACTION_CHART_LABEL_WEAK_UNDEREXTRACTED": "ZWAK
onderextractie", - "EXTRACTION_CHART_LABEL_WEAK": "ZWAK
", - "EXTRACTION_CHART_LABEL_WEAK_HARSH": "ZWAK
ruw", - "PAGE_SETTINGS_TEXT_TO_SPEECH_SECTION": "Tekst naar spraak", - "TEXT_TO_SPEECH": { - "ACTIVATE": "Activeer tekst naar spraak", - "BREW_STARTED": "Brouwen gestart", - "BREW_ENDED": "Einde brouwen", - "TIME": "Tijd", - "SPEAK_EVERY_MS": "Spreek elke geselecteerde milliseconde", - "FOLLOWING_NUMBERS_WILL_BE_TEST_SPOKEN": "De volgende nummers worden als test uitgesproken", - "TEST_SPEAK": "Start test spraak", - "PITCH": "Toonhoogte", - "RATE": "beoordeling" - }, - "PAGE_SETTINGS_HAPTIC_FEEDBACK_SECTION": "Haptische feedback", - "HAPTIC_FEEDBACK": { - "ACTIVATE": "Activeer haptische feedback", - "BREW_STARTED": "Trillen bij het starten van het brouwen", - "BREW_STOPPED": "Trillen bij het stoppen van het brouwen", - "TARE": "Trillen bij tarra van de weegschaal" - }, - "CONNECTED": "Verbonden", - "DISCONNECTED": "Losgekoppeld", - "EXPERIMENTAL_FEATURE_DISCLAIMER": "Dit is een experimentele functie", - "SHOW_HOURS": "Toon uren", - "SHOW_MINUTES": "Toon Minuten", - "BEANS_UNARCHIVE": "uit het archief halen", - "TOAST_BEAN_UNARCHIVED_SUCCESSFULLY": "Boon is uit het archief gehaald", - "WATER_TYPE_PURE_COFFEE_WATER": "Zuiver Koffiewater", - "WATER_TYPE_EMPIRICAL_WATER_GLACIAL": "empirical water GLACIAL", - "WATER_TYPE_EMPIRICAL_WATER_SPRING": "empirical water SPRING", - "SORT_PREPARATION_TOOLS": "Sorteer bereidingsmiddelen", - "PREPARATION_TYPE_METICULOUS": "Meticulous", - "EXPORT_CAUTION": "Exporteren exporteert alleen de database, geen afbeeldingen, geen stromingsprofielen . Als deze nodig zijn, ga dan naar Gitbook voor meer informatie", - "POPOVER_FREEZE_COFFEE_BEAN": "Bevries koffieboon", - "POPOVER_UNFREEZE_COFFEE_BEAN": "Koffieboon ontdooien", - "BEAN_POPOVER_EDIT_FREEZE_DATE": "invries datum bewerken", - "BEAN_POPOVER_EDIT_UNFREEZE_DATE": "ontdooi datum bewerken", - "BEAN_POPOVER_LEFT_UNFROZEN": "Niet ingevroren", - "BEAN_POPOVER_FREEZE_PARTIAL_BAG": "Gedeelte zak invriezen (g)", - "FROZEN_BEANS": "Bevroren", - "BEAN_TAB_FROZEN_INFORMATION": "Bevroren notities", - "BEAN_POPOVER_FROZEN_BAGS": "Bevroren zakken", - "BEAN_POPOVER_FROZEN_DELETE_BEAN_MESSAGE": "Je gaat de hele zak invriezen en we hebben geen brouwsels gevonden die ermee gemaakt zijn, wil je de originele zak verwijderen?", - "CREATE_FROZEN_BEANS": "creëer bonen", - "BEAN_POPOVER_COPY_ATTACHMENTS": "Kopieer bijlagen", - "BEAN_POPOVER_COPY_ATTACHMENTS_DESCRIPTION": "Het kopiëren van bijlagen is aan het begin van deze functie gedeactiveerd", - "BEAN_DATA_FROZEN_DATE": "Invries datum", - "BEAN_DATA_UNFROZEN_DATE": "ontdooi datum", - "PAGE_BEANS_LIST_YOU_GOT_NO_FROZEN_BEANS": "Je hebt geen ingevroren bonen", - "BEAN_DATA_FROZEN_ID": "Ingevroren id", - "PAGE_SETTINGS_MANAGE_FEATURES": "Functies beheren", - "ACTIVE_BEAN_FREEZING_FEATURE": "Activeer het invriezen van bonen", - "NAV_FROZEN_BEANS_LIST": "Lijst met ingevroren bonen", - "BEAN_BREW_LIST_VIEW_PARAMETERS": "Boon informatie voor brouwen", - "BEAN_AGE_BY_BREW_DATE": "Leeftijd van de bonen op brouwdatum", - "BEAN_POPOVER_FROZEN_BEAN_WILL_BE_ARCHIVED_NOW_MESSAGE": "Je diepvrieszakken hebben als resultaat dat je originele zak geen gewicht meer heeft, maar je hebt er wel mee gebrouwen, dus beoordeel en stuur naar het archief.", - "BEAN_POPOVER_YOU_CANT_FREEZE_WITH_ZERO_WEIGHT_LEFT": "Je kunt niet bevriezen, want het restgewicht van je zak is nul", - "BEAN_DATA_BEST_DATE": "Beste Bonen Datum", - "BEAN_DATA_BEST_DATE_TOOLTIP": "Wanneer is de beste datum om de bonen te gebruiken?", - "BEAN_DATA_OPEN_DATE": "Openingsdatum van de zak", - "BEAN_DATA_OPEN_DATE_TOOLTIP": "Wanneer heb je de zak bonen geopend?", - "BEAN_FREEZING_STORAGE_TYPE_UNKNOWN": "Onbekend", - "BEAN_FREEZING_STORAGE_TYPE_COFFEE_BAG": "Koffiezak", - "BEAN_FREEZING_STORAGE_TYPE_COFFEE_JAR": "Koffie pot", - "BEAN_FREEZING_STORAGE_TYPE_ZIP_LOCK": "Ritssluiting", - "BEAN_FREEZING_STORAGE_TYPE_VACUUM_SEALED": "Vacuüm verzegeld", - "BEAN_FREEZING_STORAGE_TYPE_TUBE": "Buis", - "BEAN_DATA_FROZEN_STORAGE_TYPE": "Vriezer opslagtype", - "PAGE_SETTINGS_BREW_RATING_CHANGED_BREWS_NOT_VISIBLE": "U heeft de maximale beoordeling van uw brouwsels gewijzigd, maar u heeft uw brouwsels al hoger beoordeeld dan uw werkelijke maximum. Deze worden dus niet weergegeven bij de normale filterinstellingen.", - "PAGE_SETTINGS_BEAN_RATING_CHANGED_BEANS_NOT_VISIBLE": "U heeft de maximale beoordeling voor bonen gewijzigd, maar u heeft bonen al hoger beoordeeld dan uw werkelijke maximum. Ze worden dus niet weergegeven met de normale filterinstellingen.", - "BEAN_DATA_FROZEN_NOTE": "Bevroren notities", - "IGNORE_NEGATIVE_VALUES_DESCRIPTION": "Als je dit activeert, loopt de grafiek één seconde achter", - "IGNORE_ANOMALY_VALUES_DESCRIPTION": "Als je dit activeert, loopt de grafiek één seconde achter", - "SAVE_LOGFILES_TO_NOTES_FROM_MACHINE": "Bewaar de machine logs bij het opslaan van een brouwsel" -} \ No newline at end of file diff --git a/src/components/brew-information/brew-information.component.ts b/src/components/brew-information/brew-information.component.ts index a1455d89..209b94c7 100644 --- a/src/components/brew-information/brew-information.component.ts +++ b/src/components/brew-information/brew-information.component.ts @@ -401,7 +401,6 @@ export class BrewInformationComponent implements OnInit { htmlToImage .toPng(this.cardEl.nativeElement) .then((_dataURL) => { - console.log(_dataURL); // On iOS we need to do this a second time, because the rendering doesn't render everything (strange thing) setTimeout(() => { htmlToImage From 82669b37d28ef4eed53487e5d45d7b6840aaf10a Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Fri, 30 Aug 2024 23:03:44 +0200 Subject: [PATCH 26/55] Changes --- src/app/brew/brew-add/brew-add.component.html | 4 +- .../brew/brew-edit/brew-edit.component.html | 4 +- ...reparation-connected-device.component.html | 4 +- .../preparation-connected-device.component.ts | 8 ++ .../beanconqueror-preparation-sanremo-you.svg | 35 +++++++++ .../beanconqueror-sanremo-you-logo.svg | 73 ++++++++++++------- src/assets/i18n/en.json | 6 +- src/classes/preparation/preparation.ts | 2 + .../sanremo/sanremoYOUDevice.ts | 7 +- src/classes/settings/settings.ts | 4 +- .../brew-brewing-graph.component.ts | 32 +++++--- ...-brewing-preparation-device.component.html | 13 ++++ ...ew-brewing-preparation-device.component.ts | 3 + .../sanremo/sanremoYOUMode.ts | 4 + .../sanremoYOU/iSanremoYOUParams.ts | 3 + 15 files changed, 156 insertions(+), 46 deletions(-) create mode 100644 src/assets/custom-ion-icons/beanconqueror-preparation-sanremo-you.svg create mode 100755 src/enums/preparationDevice/sanremo/sanremoYOUMode.ts diff --git a/src/app/brew/brew-add/brew-add.component.html b/src/app/brew/brew-add/brew-add.component.html index b74ee3c5..1ebe900a 100644 --- a/src/app/brew/brew-add/brew-add.component.html +++ b/src/app/brew/brew-add/brew-add.component.html @@ -21,11 +21,11 @@ - + - + diff --git a/src/app/brew/brew-edit/brew-edit.component.html b/src/app/brew/brew-edit/brew-edit.component.html index 462b5e87..3531024c 100644 --- a/src/app/brew/brew-edit/brew-edit.component.html +++ b/src/app/brew/brew-edit/brew-edit.component.html @@ -11,11 +11,11 @@ - + - + diff --git a/src/app/preparation/preparation-connected-device/preparation-connected-device.component.html b/src/app/preparation/preparation-connected-device/preparation-connected-device.component.html index 071f6557..85fca6bc 100644 --- a/src/app/preparation/preparation-connected-device/preparation-connected-device.component.html +++ b/src/app/preparation/preparation-connected-device/preparation-connected-device.component.html @@ -50,7 +50,7 @@

{{"PREPARATION_DEVICE.RESIDUAL_LAG_TIME" | translate }}  - @@ -96,7 +96,7 @@

{{"PREPARATION_DEVICE.RESIDUAL_LAG_TIME" | translate }}  - diff --git a/src/app/preparation/preparation-connected-device/preparation-connected-device.component.ts b/src/app/preparation/preparation-connected-device/preparation-connected-device.component.ts index f6c2ce88..a4b55d94 100644 --- a/src/app/preparation/preparation-connected-device/preparation-connected-device.component.ts +++ b/src/app/preparation/preparation-connected-device/preparation-connected-device.component.ts @@ -14,6 +14,9 @@ import { UISettingsStorage } from '../../../services/uiSettingsStorage'; import { Settings } from '../../../classes/settings/settings'; import { environment } from '../../../environments/environment'; import { PREPARATION_TYPES } from '../../../enums/preparations/preparationTypes'; +import { SanremoYOUParams } from '../../../classes/preparationDevice/sanremo/sanremoYOUDevice'; +import { MeticulousParams } from '../../../classes/preparationDevice/meticulous/meticulousDevice'; +import { XeniaParams } from '../../../classes/preparationDevice/xenia/xeniaDevice'; @Component({ selector: 'app-preparation-connected-device', @@ -58,13 +61,18 @@ export class PreparationConnectedDeviceComponent { if (this.data.type === PREPARATION_TYPES.METICULOUS) { this.data.connectedPreparationDevice.type = PreparationDeviceType.METICULOUS; + this.data.connectedPreparationDevice.customParams = + new MeticulousParams(); } if (this.data.type === PREPARATION_TYPES.XENIA) { this.data.connectedPreparationDevice.type = PreparationDeviceType.XENIA; + this.data.connectedPreparationDevice.customParams = new XeniaParams(); } if (this.data.type === PREPARATION_TYPES.SANREMO_YOU) { this.data.connectedPreparationDevice.type = PreparationDeviceType.SANREMO_YOU; + this.data.connectedPreparationDevice.customParams = + new SanremoYOUParams(); } } } diff --git a/src/assets/custom-ion-icons/beanconqueror-preparation-sanremo-you.svg b/src/assets/custom-ion-icons/beanconqueror-preparation-sanremo-you.svg new file mode 100644 index 00000000..2bee8008 --- /dev/null +++ b/src/assets/custom-ion-icons/beanconqueror-preparation-sanremo-you.svg @@ -0,0 +1,35 @@ + + + + diff --git a/src/assets/custom-ion-icons/beanconqueror-sanremo-you-logo.svg b/src/assets/custom-ion-icons/beanconqueror-sanremo-you-logo.svg index 076d07b5..65a29f37 100644 --- a/src/assets/custom-ion-icons/beanconqueror-sanremo-you-logo.svg +++ b/src/assets/custom-ion-icons/beanconqueror-sanremo-you-logo.svg @@ -2,34 +2,55 @@ - - + + + diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 4bf29546..9e83e5a9 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -1067,7 +1067,11 @@ }, "TYPE_SANREMO_YOU": { "TITLE": "Sanremo YOU Machine", - "STOP_AT_WEIGHT": "Stop at weight" + "STOP_AT_WEIGHT": "Stop at weight", + "SELECT_MODE": "Select mode", + "MODE_LISTENING": "Listening", + "MODE_CONTROL": "Control" + } }, "DEVICE_CONNECTION": "Device connection", diff --git a/src/classes/preparation/preparation.ts b/src/classes/preparation/preparation.ts index d6d35f76..299f7a2c 100755 --- a/src/classes/preparation/preparation.ts +++ b/src/classes/preparation/preparation.ts @@ -254,6 +254,8 @@ export class Preparation implements IPreparation { return 'beanconqueror-preparation-tricolate'; case PREPARATION_TYPES.METICULOUS: return 'beanconqueror-preparation-meticulous'; + case PREPARATION_TYPES.SANREMO_YOU: + return 'beanconqueror-preparation-sanremo-you'; default: return 'beanconqueror-preparation-custom'; } diff --git a/src/classes/preparationDevice/sanremo/sanremoYOUDevice.ts b/src/classes/preparationDevice/sanremo/sanremoYOUDevice.ts index c36b1dad..4cf6023f 100644 --- a/src/classes/preparationDevice/sanremo/sanremoYOUDevice.ts +++ b/src/classes/preparationDevice/sanremo/sanremoYOUDevice.ts @@ -3,6 +3,7 @@ import { HttpClient } from '@angular/common/http'; import { Preparation } from '../../preparation/preparation'; import { ISanremoYOUParams } from '../../../interfaces/preparationDevices/sanremoYOU/iSanremoYOUParams'; +import { SanremoYOUMode } from '../../../enums/preparationDevice/sanremo/sanremoYOUMode'; declare var cordova; export class SanremoYOUDevice extends PreparationDevice { public scriptList: Array<{ INDEX: number; TITLE: string }> = []; @@ -176,8 +177,10 @@ export class SanremoYOUDevice extends PreparationDevice { export class SanremoYOUParams implements ISanremoYOUParams { public stopAtWeight: number = 0; - public residualLagTime: number = 1.35; + public residualLagTime: number = 0.9; + public selectedMode: SanremoYOUMode = SanremoYOUMode.LISTENING; constructor() { - this.residualLagTime = 1.35; + this.residualLagTime = 0.9; + this.selectedMode = SanremoYOUMode.LISTENING; } } diff --git a/src/classes/settings/settings.ts b/src/classes/settings/settings.ts index 3a4839c1..1adf7cb1 100755 --- a/src/classes/settings/settings.ts +++ b/src/classes/settings/settings.ts @@ -387,8 +387,8 @@ export class Settings implements ISettings { }; this.graph_pressure = { - upper: 0, - lower: 9, + lower: 0, + upper: 9, }; this.brew_rating = 5; diff --git a/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts b/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts index c0aa6771..eb7f588c 100644 --- a/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts +++ b/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts @@ -63,6 +63,7 @@ import { UIGraphStorage } from '../../../services/uiGraphStorage.service'; import regression from 'regression'; import { TextToSpeechService } from '../../../services/textToSpeech/text-to-speech.service'; import { SanremoYOUDevice } from '../../../classes/preparationDevice/sanremo/sanremoYOUDevice'; +import { SanremoYOUMode } from '../../../enums/preparationDevice/sanremo/sanremoYOUMode'; declare var Plotly; @@ -1052,6 +1053,9 @@ export class BrewBrewingGraphComponent implements OnInit { fixedrange: true, range: [suggestedMinPressure, suggestedMaxPressure], }; + if (suggestedMaxPressure <= 10) { + layout['yaxis4']['dtick'] = 1; + } } const temperatureDevice = this.bleManager.getTemperatureDevice(); if ( @@ -1787,7 +1791,9 @@ export class BrewBrewingGraphComponent implements OnInit { this.brewComponent?.brewBrewingPreparationDeviceEl?.getPreparationDeviceType() === PreparationDeviceType.SANREMO_YOU && _event !== 'sanremo_you' && - this.machineStopScriptWasTriggered === false + this.machineStopScriptWasTriggered === false && + this.data.preparationDeviceBrew.params.selectedMode === + SanremoYOUMode.CONTROLLING ) { this.machineStopScriptWasTriggered = true; this.uiLog.log(`Sanremo YOU - Pause button pressed, stop shot`); @@ -2320,13 +2326,19 @@ export class BrewBrewingGraphComponent implements OnInit { ) { const prepDeviceCall: SanremoYOUDevice = this.brewComponent .brewBrewingPreparationDeviceEl.preparationDevice as SanremoYOUDevice; - prepDeviceCall.startShot().catch((_msg) => { - this.uiLog.log('We could not start shot on sanremo: ' + _msg); - this.uiToast.showInfoToast( - 'We could not start shot on sanremo: ' + _msg, - false - ); - }); + + if ( + this.data.preparationDeviceBrew?.params.selectedMode === + SanremoYOUMode.CONTROLLING + ) { + prepDeviceCall.startShot().catch((_msg) => { + this.uiLog.log('We could not start shot on sanremo: ' + _msg); + this.uiToast.showInfoToast( + 'We could not start shot on sanremo: ' + _msg, + false + ); + }); + } if ( this.settings.bluetooth_scale_maximize_on_start_timer === true && @@ -3012,7 +3024,9 @@ export class BrewBrewingGraphComponent implements OnInit { } } else if ( this.brewComponent.brewBrewingPreparationDeviceEl.getPreparationDeviceType() === - PreparationDeviceType.SANREMO_YOU + PreparationDeviceType.SANREMO_YOU && + this.data.preparationDeviceBrew.params.selectedMode === + SanremoYOUMode.CONTROLLING ) { /**We call this function before the if, because we still log the data**/ const thresholdHit = this.calculateBrewByWeight( diff --git a/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.html b/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.html index 1c314521..0c923610 100644 --- a/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.html +++ b/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.html @@ -83,10 +83,23 @@ {{"PREPARATION_DEVICE.TYPE_SANREMO_YOU.TITLE" | translate }} + + + + {{"PREPARATION_DEVICE.TYPE_SANREMO_YOU.MODE_LISTENING" | translate}} + + + {{"PREPARATION_DEVICE.TYPE_SANREMO_YOU.MODE_CONTROL" | translate}} + + + + + diff --git a/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.ts b/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.ts index f84204e2..cb340e75 100644 --- a/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.ts +++ b/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.ts @@ -44,6 +44,7 @@ import { SanremoYOUDevice, SanremoYOUParams, } from '../../../classes/preparationDevice/sanremo/sanremoYOUDevice'; +import { SanremoYOUMode } from '../../../enums/preparationDevice/sanremo/sanremoYOUMode'; @Component({ selector: 'brew-brewing-preparation-device', templateUrl: './brew-brewing-preparation-device.component.html', @@ -433,4 +434,6 @@ export class BrewBrewingPreparationDeviceComponent implements OnInit { } }, 50); } + + protected readonly SanremoYOUMode = SanremoYOUMode; } diff --git a/src/enums/preparationDevice/sanremo/sanremoYOUMode.ts b/src/enums/preparationDevice/sanremo/sanremoYOUMode.ts new file mode 100755 index 00000000..c66e9caa --- /dev/null +++ b/src/enums/preparationDevice/sanremo/sanremoYOUMode.ts @@ -0,0 +1,4 @@ +export enum SanremoYOUMode { + LISTENING = 'LISTENING', + CONTROLLING = 'CONTROLLING', +} diff --git a/src/interfaces/preparationDevices/sanremoYOU/iSanremoYOUParams.ts b/src/interfaces/preparationDevices/sanremoYOU/iSanremoYOUParams.ts index a04a7335..f753a638 100644 --- a/src/interfaces/preparationDevices/sanremoYOU/iSanremoYOUParams.ts +++ b/src/interfaces/preparationDevices/sanremoYOU/iSanremoYOUParams.ts @@ -1,4 +1,7 @@ +import { SanremoYOUMode } from '../../../enums/preparationDevice/sanremo/sanremoYOUMode'; + export interface ISanremoYOUParams { stopAtWeight: number; residualLagTime: number; + selectedMode: SanremoYOUMode; } From b2f49023a60b5f78745b95feb5d67f17265c6d7e Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Sat, 31 Aug 2024 22:03:00 +0200 Subject: [PATCH 27/55] Adding download button to download import files --- src/app/settings/settings.page.html | 8 ++++++-- src/app/settings/settings.page.ts | 6 ++++++ src/assets/i18n/en.json | 3 ++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/app/settings/settings.page.html b/src/app/settings/settings.page.html index dcd95cff..cd21b706 100644 --- a/src/app/settings/settings.page.html +++ b/src/app/settings/settings.page.html @@ -59,14 +59,18 @@

{{"EXPORT" | translate}}

{{"EXCEL_EXPORT" | translate}} - + {{"IMPORT_ROASTED_BEANS_EXCEL" | translate}} - + {{"IMPORT_GREEN_BEANS_EXCEL" | translate}} + + {{"DOWNLOAD_IMPORT_EXCEL_TEMPLATES" | translate}} + + diff --git a/src/app/settings/settings.page.ts b/src/app/settings/settings.page.ts index fcae3d57..787af73a 100644 --- a/src/app/settings/settings.page.ts +++ b/src/app/settings/settings.page.ts @@ -1287,6 +1287,12 @@ export class SettingsPage { } } + public downloadImportExcelTemplates() { + this.uiHelper.openExternalWebpage( + 'https://beanconqueror.gitbook.io/beanconqueror/resources/files' + ); + } + public importBeansExcel(_type: string = 'roasted'): void { if (this.platform.is('cordova')) { this.uiAnalytics.trackEvent( diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 9e83e5a9..952181d8 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -1345,5 +1345,6 @@ "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS": "Define the axes for the pressure graph", "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_DESCRIPTION": "Set the starting and end size of the axes for pressure", "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_RANGE": "Pressure axes", - "PAGE_SETTINGS_LANGUAGE_DUTCH": "Dutch" + "PAGE_SETTINGS_LANGUAGE_DUTCH": "Dutch", + "DOWNLOAD_IMPORT_EXCEL_TEMPLATES": "Download import templates" } From 41c42973d5fab5dc01e67ccbf14992ef36c291da Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Sat, 31 Aug 2024 23:17:44 +0200 Subject: [PATCH 28/55] #783 - Make pressure graph resizeable --- .../graph-detail/graph-detail.component.ts | 12 ++++++ .../brew-brewing-graph.component.ts | 42 +++++++++++-------- .../graph-display-card.component.ts | 11 +++++ 3 files changed, 48 insertions(+), 17 deletions(-) diff --git a/src/app/graph-section/graph/graph-detail/graph-detail.component.ts b/src/app/graph-section/graph/graph-detail/graph-detail.component.ts index a0ead927..113e182f 100644 --- a/src/app/graph-section/graph/graph-detail/graph-detail.component.ts +++ b/src/app/graph-section/graph/graph-detail/graph-detail.component.ts @@ -174,6 +174,16 @@ export class GraphDetailComponent implements OnInit { }, }; + const graph_pressure_settings = this.settings.graph_pressure; + const suggestedMinPressure: number = graph_pressure_settings.lower; + let suggestedMaxPressure = graph_pressure_settings.upper; + try { + if (this.pressureTrace?.y.length > 0) { + suggestedMaxPressure = Math.max(...this.pressureTrace.y); + suggestedMaxPressure = Math.ceil(suggestedMaxPressure + 1); + } + } catch (ex) {} + layout['yaxis4'] = { title: '', titlefont: { color: '#05C793' }, @@ -183,6 +193,7 @@ export class GraphDetailComponent implements OnInit { side: 'right', showgrid: false, position: 0.93, + range: [suggestedMinPressure, suggestedMaxPressure], visible: true, }; @@ -348,6 +359,7 @@ export class GraphDetailComponent implements OnInit { } } } + if ( this.flow_profile_raw?.pressureFlow && this.flow_profile_raw.pressureFlow.length > 0 diff --git a/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts b/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts index eb7f588c..60e98e2e 100644 --- a/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts +++ b/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts @@ -1053,9 +1053,6 @@ export class BrewBrewingGraphComponent implements OnInit { fixedrange: true, range: [suggestedMinPressure, suggestedMaxPressure], }; - if (suggestedMaxPressure <= 10) { - layout['yaxis4']['dtick'] = 1; - } } const temperatureDevice = this.bleManager.getTemperatureDevice(); if ( @@ -1116,6 +1113,16 @@ export class BrewBrewingGraphComponent implements OnInit { }, }; + const graph_pressure_settings = this.settings.graph_pressure; + const suggestedMinPressure = graph_pressure_settings.lower; + let suggestedMaxPressure = graph_pressure_settings.upper; + try { + if (this.pressureTrace?.y.length > 0) { + suggestedMaxPressure = Math.max(...this.pressureTrace.y); + suggestedMaxPressure = Math.ceil(suggestedMaxPressure + 1); + } + } catch (ex) {} + layout['yaxis4'] = { title: '', titlefont: { color: '#05C793' }, @@ -1125,7 +1132,7 @@ export class BrewBrewingGraphComponent implements OnInit { side: 'right', showgrid: false, position: 0.93, - range: [0, 12], + range: [suggestedMinPressure, suggestedMaxPressure], visible: true, }; @@ -1588,6 +1595,18 @@ export class BrewBrewingGraphComponent implements OnInit { newLayoutIsNeeded = true; } } + if (this.pressureTrace?.y?.length > 0) { + // #783 + const lastPressureData: number = + this.pressureTrace.y[this.pressureTrace.y.length - 1]; + if (lastPressureData > this.lastChartLayout.yaxis4.range[1]) { + this.lastChartLayout.yaxis4.range[1] = Math.ceil( + lastPressureData + 1 + ); + newLayoutIsNeeded = true; + } + } + if (newLayoutIsNeeded) { Plotly.relayout( this.profileDiv.nativeElement, @@ -1596,18 +1615,7 @@ export class BrewBrewingGraphComponent implements OnInit { } }, 25); } else { - const delay = moment(new Date()) - .startOf('day') - .add('seconds', 0) - .toDate() - .getTime(); - const delayedTime: number = moment(new Date()) - .startOf('day') - .add('seconds', this.brewComponent.timer.getSeconds() + 5) - .toDate() - .getTime(); - this.lastChartLayout.xaxis.range = [delay, delayedTime]; - Plotly.relayout(this.profileDiv.nativeElement, this.lastChartLayout); + // Not needed anymore } } catch (ex) {} }); @@ -2134,7 +2142,7 @@ export class BrewBrewingGraphComponent implements OnInit { ); weight = weight + genRand(0.1, 2, 2); pressure = Math.floor( - (crypto.getRandomValues(new Uint8Array(1))[0] / Math.pow(2, 8)) * 11 + (crypto.getRandomValues(new Uint8Array(1))[0] / Math.pow(2, 8)) * 16 ); temperature = Math.floor( (crypto.getRandomValues(new Uint8Array(1))[0] / Math.pow(2, 8)) * 90 diff --git a/src/components/graph-display-card/graph-display-card.component.ts b/src/components/graph-display-card/graph-display-card.component.ts index 9896b7fb..4fd0b40e 100644 --- a/src/components/graph-display-card/graph-display-card.component.ts +++ b/src/components/graph-display-card/graph-display-card.component.ts @@ -159,6 +159,16 @@ export class GraphDisplayCardComponent implements OnInit { }, }; + const graph_pressure_settings = this.settings.graph_pressure; + const suggestedMinPressure: number = graph_pressure_settings.lower; + let suggestedMaxPressure = graph_pressure_settings.upper; + try { + if (this.pressureTrace?.y.length > 0) { + suggestedMaxPressure = Math.max(...this.pressureTrace.y); + suggestedMaxPressure = Math.ceil(suggestedMaxPressure + 1); + } + } catch (ex) {} + layout['yaxis4'] = { title: '', titlefont: { color: '#05C793' }, @@ -168,6 +178,7 @@ export class GraphDisplayCardComponent implements OnInit { side: 'right', fixedrange: true, showgrid: false, + range: [suggestedMinPressure, suggestedMaxPressure], position: 0.93, visible: true, }; From d78ad04780d673faa3058e3164fcbfc9f121578a Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Sun, 1 Sep 2024 21:53:15 +0200 Subject: [PATCH 29/55] #784 - Fixing sorting of preparation tools --- .../preparation-sort-tools.component.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/app/preparation/preparation-sort-tools/preparation-sort-tools.component.ts b/src/app/preparation/preparation-sort-tools/preparation-sort-tools.component.ts index ad1c717f..d0eac0ff 100644 --- a/src/app/preparation/preparation-sort-tools/preparation-sort-tools.component.ts +++ b/src/app/preparation/preparation-sort-tools/preparation-sort-tools.component.ts @@ -2,6 +2,7 @@ import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; import { Preparation } from '../../../classes/preparation/preparation'; import { ModalController } from '@ionic/angular'; +import { UIHelper } from '../../../services/uiHelper'; @Component({ selector: 'app-preparation-sort-tools', @@ -20,7 +21,8 @@ export class PreparationSortToolsComponent implements OnInit { @Input() public preparation: Preparation; constructor( private readonly modalController: ModalController, - private readonly changeDetectorRef: ChangeDetectorRef + private readonly changeDetectorRef: ChangeDetectorRef, + private readonly uiHelper: UIHelper ) {} public ngOnInit() { @@ -40,13 +42,12 @@ export class PreparationSortToolsComponent implements OnInit { reorderVar.splice(ev.detail.to, 0, reorderVar.splice(ev.detail.from, 1)[0]); - const swapElements = (myArray, index1, index2) => { - [myArray[index1], myArray[index2]] = [myArray[index2], myArray[index1]]; - }; - - swapElements(this.preparation.tools, ev.detail.from, ev.detail.to); + this.preparation.tools.splice( + ev.detail.to, + 0, + this.preparation.tools.splice(ev.detail.from, 1)[0] + ); - // console.log(this.settings.brew_order); // Finish the reorder and position the item in the DOM based on // where the gesture ended. This method can also be called directly // by the reorder group From 12bf334852e531bfad35c05bd045e8c26e211fb8 Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Sun, 1 Sep 2024 22:22:54 +0200 Subject: [PATCH 30/55] Remove things. --- src/components/brew-information/brew-information.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/brew-information/brew-information.component.html b/src/components/brew-information/brew-information.component.html index a60b6043..7d174be6 100644 --- a/src/components/brew-information/brew-information.component.html +++ b/src/components/brew-information/brew-information.component.html @@ -1,5 +1,5 @@
- + From bcb114919158d5597aea1a4319ed4b5ff3857fa5 Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Sun, 1 Sep 2024 23:33:05 +0200 Subject: [PATCH 31/55] #785 - Make it possible to slide cards to show graph --- src/app/app.scss | 27 +- .../brew-information.component.html | 407 +++++++++--------- .../brew-information.component.scss | 10 + .../brew-information.component.ts | 38 ++ .../graph-display-card.component.ts | 6 +- .../photo-add/photo-add.component.html | 2 +- .../photo-view/photo-view.component.html | 2 +- .../photo-popover.component.html | 2 +- .../update-popover.component.html | 2 +- .../welcome-popover.component.html | 2 +- 10 files changed, 283 insertions(+), 215 deletions(-) diff --git a/src/app/app.scss b/src/app/app.scss index a2c6f01c..e760198f 100644 --- a/src/app/app.scss +++ b/src/app/app.scss @@ -944,24 +944,27 @@ swiper-container { --swiper-scrollbar-drag-bg-color: rgba(var(--ion-text-color-rgb, 0, 0, 0), 0.5); } -swiper-slide { - display: flex; - position: relative; +swiper-container.swiper { + swiper-slide { + display: flex; + position: relative; - flex-direction: column; - flex-shrink: 0; - align-items: center; - justify-content: center; + flex-direction: column; + flex-shrink: 0; + align-items: center; + justify-content: center; - width: 100%; - height: 100%; + width: 100%; + height: 100%; - font-size: 18px; + font-size: 18px; - text-align: center; - box-sizing: border-box; + text-align: center; + box-sizing: border-box; + } } + swiper-slide img { width: auto; max-width: 100%; diff --git a/src/components/brew-information/brew-information.component.html b/src/components/brew-information/brew-information.component.html index 7d174be6..f4869a3e 100644 --- a/src/components/brew-information/brew-information.component.html +++ b/src/components/brew-information/brew-information.component.html @@ -1,247 +1,260 @@ -
- - - - - - - - -
- -
-
- -
- -
-
- - - - - - - - - {{ brew.config.unix_timestamp | formatDate:[settings?.date_format] }}{{ brew.config.unix_timestamp | formatDate:["HH:mm"] }} - - - - - - - - - -
- - () - - {{ bean?.name }} ({{ bean?.roaster }}) -
-
- + + + + + + + + + + +
+ +
+
+ +
+ +
+
+ + + + + + + + + {{ brew.config.unix_timestamp | formatDate:[settings?.date_format] }}{{ brew.config.unix_timestamp | formatDate:["HH:mm"] }} + + + + + + + + + +
+ + () + + {{ bean?.name }} ({{ bean?.roaster }}) +
+
+ - - - - + + + + - {{ this.uiHelper.toFixedIfNecessary(brew.rating, 2) }} - -
-
-
-
-
- - -
-
-
-
- - - + {{ this.uiHelper.toFixedIfNecessary(brew.rating, 2) }} + + + +
+
+
+ + +
+
+
+
+ + + - {{ "BREW_DATA_PREPARATION_METHOD" | translate }} -
- {{ preparation?.name }} -
- {{ "BREW_DATA_PREPARATION_METHOD" | translate }} +
+ {{ preparation?.name }} +
+ - {{ "BREW_DATA_MILL" |translate }} -
- {{ mill?.name }} -
- - {{ "BREW_DATA_IN_OUT_BR" | translate }} -
- - {{ brew?.grind_weight | number : '.0-2' }}gr - / {{ brew.brew_quantity | number : '.0-2' }}{{ brewQuantityEnum[brew?.brew_quantity_type] }} ({{ brew?.getBrewRatio() }} - ) - - - {{ brew?.grind_weight | number : '.0-2' }}gr - / {{ brew.brew_beverage_quantity | number : '.0-2' }}{{ brewQuantityEnum[brew?.brew_beverage_quantity_type] }} ({{ brew?.getBrewRatio() }} - ) - -
- {{ "BREW_DATA_MILL" |translate }} +
+ {{ mill?.name }} +
+ + {{ "BREW_DATA_IN_OUT_BR" | translate }} +
+ + {{ brew?.grind_weight | number : '.0-2' }}gr + / {{ brew.brew_quantity | number : '.0-2' }}{{ brewQuantityEnum[brew?.brew_quantity_type] }} ({{ brew?.getBrewRatio() }} + ) + + + {{ brew?.grind_weight | number : '.0-2' }}gr + / {{ brew.brew_beverage_quantity | number : '.0-2' }}{{ brewQuantityEnum[brew?.brew_beverage_quantity_type] }} ({{ brew?.getBrewRatio() }} + ) + +
+ - {{ 'BREW_DATA_TIME' | translate }} -
- {{ brew?.getFormattedTotalCoffeeBrewTime() }} -
- 0 && uiBrewHelper.fieldVisible(settings.visible_list_view_parameters.mill_speed, preparation?.visible_list_view_parameters.mill_speed, preparation?.use_custom_parameters))" size='6'> - {{ 'BREW_DATA_GRIND_SIZE' | translate }} -
- {{ brew?.grind_size }} @ {{ brew?.mill_speed }} -
- - {{ 'BREW_DATA_TDS_EY' | translate }} -
- {{ brew?.tds }}, %{{ brew?.getExtractionYield() }} -
- 0 && brew?.brew_beverage_quantity>0' size='6'> + {{ 'BREW_DATA_TDS_EY' | translate }} +
+ {{ brew?.tds }}, %{{ brew?.getExtractionYield() }} +
+ - {{ 'BREW_DATA_BREW_TEMPERATURE' | translate }} -
- {{ brew?.brew_temperature }} -
- - {{ 'BREW_DATA_PRESSURE_PROFILE' | translate }} -
- {{ brew?.pressure_profile }} -
- {{ 'BREW_DATA_PRESSURE_PROFILE' | translate }} +
+ {{ brew?.pressure_profile }} +
+ - {{ "BREW_DATA_BEAN_WEIGHT_IN" | translate }} -
- {{ brew?.bean_weight_in }} -
- {{ "BREW_DATA_BEAN_WEIGHT_IN" | translate }} +
+ {{ brew?.bean_weight_in }} +
+ - {{ "BREW_DATA_MILL_TIMER" | translate }} -
- {{ brew?.mill_timer }} -
- +
+ {{ brew?.mill_timer }} +
+ - {{ "BREW_DATA_VESSEL_NAME_WEIGHT" | translate }} -
- {{ brew?.vessel_name }} / {{ brew?.vessel_weight }} -
- {{ "BREW_DATA_VESSEL_NAME_WEIGHT" | translate }} +
+ {{ brew?.vessel_name }} / {{ brew?.vessel_weight }} +
+ - {{ "BREW_DATA_TEMPERATURE_TIME" | translate }} -
- {{ brew?.getFormattedTotalCoffeeTemperatureTime() }} -
- {{ "BREW_DATA_TEMPERATURE_TIME" | translate }} +
+ {{ brew?.getFormattedTotalCoffeeTemperatureTime() }} +
+ - {{ "BREW_DATA_COFFEE_BLOOMING_TIME" | translate }} -
- {{ brew?.getFormattedTotalCoffeeBloomingTime() }} -
- {{ "BREW_DATA_COFFEE_BLOOMING_TIME" | translate }} +
+ {{ brew?.getFormattedTotalCoffeeBloomingTime() }} +
+ - {{ "BREW_DATA_COFFEE_FIRST_DRIP_TIME" | translate }} -
- {{ brew?.getFormattedTotalCoffeeFirstDripTime() }} -
- +
+ {{ brew?.getFormattedTotalCoffeeFirstDripTime() }} +
+ - {{ "BREW_DATA_WATER" | translate }} -
- {{ brew?.getWater().name }} -
- +
+ {{ brew?.getWater().name }} +
+ - {{ "BREW_DATA_COFFEE_TYPE" | translate }} -
- {{ brew?.coffee_type }} -
- +
+ {{ brew?.coffee_type }} +
+ - {{ "BREW_DATA_COFFEE_CONCENTRATION" | translate }} -
- {{ brew?.coffee_concentration }} -
- {{ "BREW_DATA_COFFEE_CONCENTRATION" | translate }} +
+ {{ brew?.coffee_concentration }} +
+ - {{ "BREW_INFORMATION_BEAN_AGE" | translate }} -
- {{ brew?.getCalculatedBeanAge() }} -
- {{ "BREW_INFORMATION_BEAN_AGE" | translate }} +
+ {{ brew?.getCalculatedBeanAge() }} +
+ - {{ "BREW_DATA_PREPARATION_METHOD_TOOL" | translate }} -
- {{ brew?.getPreparationToolName(uuid) }} - -
-
- - {{ 'BREW_DATA_FLAVOR' | translate }} -
- {{ getCuppedBrewFlavors() }} -
- +
+ {{ brew?.getPreparationToolName(uuid) }} + +
+
+ + {{ 'BREW_DATA_FLAVOR' | translate }} +
+ {{ getCuppedBrewFlavors() }} +
+ - {{ "BREW_DATA_NOTES" | translate }} -
- -
{{ brew?.note }}
-
-
+ {{ "BREW_DATA_NOTES" | translate }} +
+ +
{{ brew?.note }}
+
+
+ +
+
+
+
+ + + + + + + + + + - - - -
{ event.stopPropagation(); event.stopImmediatePropagation(); diff --git a/src/components/graph-display-card/graph-display-card.component.ts b/src/components/graph-display-card/graph-display-card.component.ts index 4fd0b40e..4334b623 100644 --- a/src/components/graph-display-card/graph-display-card.component.ts +++ b/src/components/graph-display-card/graph-display-card.component.ts @@ -31,6 +31,7 @@ export class GraphDisplayCardComponent implements OnInit { @Input() public meticulousHistoryData: HistoryListingEntry; @Input() public chartWidth: number; + @Input() public chartHeight: number; public flow_profile_raw: BrewFlow = new BrewFlow(); @@ -97,7 +98,10 @@ export class GraphDisplayCardComponent implements OnInit { chartWidth = this.chartWidth; } - const chartHeight: number = 150; + let chartHeight: number = 150; + if (this.chartHeight) { + chartHeight = this.chartHeight; + } let tickFormat = '%S'; diff --git a/src/components/photo-add/photo-add.component.html b/src/components/photo-add/photo-add.component.html index c2975a83..099304c2 100644 --- a/src/components/photo-add/photo-add.component.html +++ b/src/components/photo-add/photo-add.component.html @@ -2,7 +2,7 @@  {{"ADD_PHOTO" | translate}}
- + diff --git a/src/components/photo-view/photo-view.component.html b/src/components/photo-view/photo-view.component.html index eec87872..9b25041d 100644 --- a/src/components/photo-view/photo-view.component.html +++ b/src/components/photo-view/photo-view.component.html @@ -1,5 +1,5 @@
- + diff --git a/src/popover/photo-popover/photo-popover.component.html b/src/popover/photo-popover/photo-popover.component.html index ecb0bc64..feb076c2 100644 --- a/src/popover/photo-popover/photo-popover.component.html +++ b/src/popover/photo-popover/photo-popover.component.html @@ -10,7 +10,7 @@
- + diff --git a/src/popover/update-popover/update-popover.component.html b/src/popover/update-popover/update-popover.component.html index 5cb174de..376a1bb6 100644 --- a/src/popover/update-popover/update-popover.component.html +++ b/src/popover/update-popover/update-popover.component.html @@ -1,6 +1,6 @@ - +
diff --git a/src/popover/welcome-popover/welcome-popover.component.html b/src/popover/welcome-popover/welcome-popover.component.html index 97da5ca4..4098a24a 100644 --- a/src/popover/welcome-popover/welcome-popover.component.html +++ b/src/popover/welcome-popover/welcome-popover.component.html @@ -30,7 +30,7 @@ - +
From c8e1ee7917b6b910f87645221e229021f32db7af Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Mon, 2 Sep 2024 20:49:22 +0200 Subject: [PATCH 32/55] #785 - Show graph in slider is now rightly extending + paddings --- .../brew-popover-actions.component.html | 4 ++++ .../graph-detail/graph-detail.component.ts | 19 +++++++++---------- src/assets/i18n/en.json | 3 ++- src/classes/devices/bokooScale.ts | 10 ++++++++++ .../brew-information.component.html | 6 +++--- .../brew-information.component.scss | 5 +++++ .../brew-information.component.ts | 13 +++++++++++-- .../brew-brewing-graph.component.ts | 6 ++++++ .../graph-display-card.component.ts | 1 - src/enums/brews/brewAction.ts | 1 + src/services/uiGraphHelper.ts | 10 ++++++++++ 11 files changed, 61 insertions(+), 17 deletions(-) diff --git a/src/app/brew/brew-popover-actions/brew-popover-actions.component.html b/src/app/brew/brew-popover-actions/brew-popover-actions.component.html index 02819e3e..79e2f04b 100644 --- a/src/app/brew/brew-popover-actions/brew-popover-actions.component.html +++ b/src/app/brew/brew-popover-actions/brew-popover-actions.component.html @@ -55,6 +55,10 @@ {{"SHOW_VISUALIZER" | translate}} + + + + {{"SHOW_GRAPH" | translate}} diff --git a/src/app/graph-section/graph/graph-detail/graph-detail.component.ts b/src/app/graph-section/graph/graph-detail/graph-detail.component.ts index 113e182f..8999fb49 100644 --- a/src/app/graph-section/graph/graph-detail/graph-detail.component.ts +++ b/src/app/graph-section/graph/graph-detail/graph-detail.component.ts @@ -11,7 +11,6 @@ import BeanconquerorFlowTestDataDummy from '../../../../assets/BeanconquerorFlow import { BrewFlow } from '../../../../classes/brew/brewFlow'; import { Settings } from '../../../../classes/settings/settings'; import { TranslateService } from '@ngx-translate/core'; -import { Graph } from '../../../../classes/graph/graph'; import { IGraph } from '../../../../interfaces/graph/iGraph'; import GRAPH_TRACKING from '../../../../data/tracking/graphTracking'; import { UIAnalytics } from '../../../../services/uiAnalytics'; @@ -19,6 +18,7 @@ import { UIHelper } from '../../../../services/uiHelper'; import { ModalController, Platform } from '@ionic/angular'; import { UIFileHelper } from '../../../../services/uiFileHelper'; import { UISettingsStorage } from '../../../../services/uiSettingsStorage'; +import { IBrew } from '../../../../interfaces/brew/iBrew'; declare var Plotly; @Component({ selector: 'app-graph-detail', @@ -44,9 +44,9 @@ export class GraphDetailComponent implements OnInit { @ViewChild('profileDiv', { read: ElementRef, static: true }) public profileDiv: ElementRef; - public data: Graph = new Graph(); - @Input() private graph: IGraph; + @Input() private brew: IBrew; + @Input() private flowProfileData: any; constructor( @@ -68,8 +68,9 @@ export class GraphDetailComponent implements OnInit { GRAPH_TRACKING.ACTIONS.DETAIL ); if (this.graph) { - this.data = this.uiHelper.copyData(this.graph); - await this.readFlowProfile(); + await this.readFlowProfile(this.graph.flow_profile); + } else if (this.brew) { + await this.readFlowProfile(this.brew.flow_profile); } else { this.flow_profile_raw = this.uiHelper.cloneData(this.flowProfileData); } @@ -408,13 +409,11 @@ export class GraphDetailComponent implements OnInit { }, 100); } - private async readFlowProfile() { + private async readFlowProfile(_path) { if (this.platform.is('cordova')) { - if (this.graph.flow_profile !== '') { + if (_path !== '') { try { - const jsonParsed = await this.uiFileHelper.getJSONFile( - this.graph.flow_profile - ); + const jsonParsed = await this.uiFileHelper.getJSONFile(_path); this.flow_profile_raw = jsonParsed; } catch (ex) {} } diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 952181d8..f8b9d034 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -1346,5 +1346,6 @@ "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_DESCRIPTION": "Set the starting and end size of the axes for pressure", "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_RANGE": "Pressure axes", "PAGE_SETTINGS_LANGUAGE_DUTCH": "Dutch", - "DOWNLOAD_IMPORT_EXCEL_TEMPLATES": "Download import templates" + "DOWNLOAD_IMPORT_EXCEL_TEMPLATES": "Download import templates", + "SHOW_GRAPH": "Graph anzeigen" } diff --git a/src/classes/devices/bokooScale.ts b/src/classes/devices/bokooScale.ts index ad314498..a2052073 100644 --- a/src/classes/devices/bokooScale.ts +++ b/src/classes/devices/bokooScale.ts @@ -49,6 +49,16 @@ export class BookooScale extends BluetoothScale { await this.write(new Uint8Array([0x03, 0x0a, 0x01, 0x00, 0x00, 0x08])); } + public async tareAndStartTimerModeAuto() { + this.weight.smoothed = 0; + this.weight.actual = 0; + this.weight.oldSmoothed = 0; + this.weight.old = 0; + this.setWeight(0); + + await this.write(new Uint8Array([0x03, 0x0a, 0x07, 0x00, 0x00, 0x00])); + } + public override disconnectTriggered(): void { this.logger.log('Disconnecting...'); this.deattachNotification(); diff --git a/src/components/brew-information/brew-information.component.html b/src/components/brew-information/brew-information.component.html index f4869a3e..5d02bbf9 100644 --- a/src/components/brew-information/brew-information.component.html +++ b/src/components/brew-information/brew-information.component.html @@ -2,7 +2,7 @@ - + - - + + diff --git a/src/components/brew-information/brew-information.component.scss b/src/components/brew-information/brew-information.component.scss index 693bf49f..03b0008a 100644 --- a/src/components/brew-information/brew-information.component.scss +++ b/src/components/brew-information/brew-information.component.scss @@ -9,6 +9,7 @@ max-height:60px!important; } ion-card.brew-layout { + width:100%; margin-top:0px; margin-bottom:0px; margin-left:10px; @@ -54,6 +55,7 @@ top: -5px; } } + ion-card.dashboard-layout { @@ -151,6 +153,9 @@ width: 100%; height: 100%; } + .add-bottom-spacing{ + margin-bottom:20px; + } } diff --git a/src/components/brew-information/brew-information.component.ts b/src/components/brew-information/brew-information.component.ts index 637b9a0c..abe007b8 100644 --- a/src/components/brew-information/brew-information.component.ts +++ b/src/components/brew-information/brew-information.component.ts @@ -36,9 +36,10 @@ import * as htmlToImage from 'html-to-image'; import { UIFileHelper } from '../../services/uiFileHelper'; import { BrewFlow } from '../../classes/brew/brewFlow'; -import { UIBeanStorage } from '../../services/uiBeanStorage'; + import { UIBeanHelper } from '../../services/uiBeanHelper'; import { VisualizerService } from '../../services/visualizerService/visualizer-service.service'; +import { UIGraphHelper } from '../../services/uiGraphHelper'; declare var window; @Component({ selector: 'brew-information', @@ -93,7 +94,8 @@ export class BrewInformationComponent implements OnInit { private readonly platform: Platform, private readonly uiFileHelper: UIFileHelper, private readonly uiBeanHelper: UIBeanHelper, - private readonly visualizerService: VisualizerService + private readonly visualizerService: VisualizerService, + private readonly uiGraphHelper: UIGraphHelper ) {} public ngOnInit() { @@ -140,6 +142,10 @@ export class BrewInformationComponent implements OnInit { this.brewAction.emit([BREW_ACTION.DETAIL, this.brew]); } + public async showBrewGraph() { + await this.uiGraphHelper.detailBrewGraph(this.brew); + } + public getElementOffsetWidth() { if (this.brewInformationContainer?.nativeElement?.offsetWidth) { return this.brewInformationContainer?.nativeElement?.offsetWidth - 50; @@ -391,6 +397,9 @@ export class BrewInformationComponent implements OnInit { case BREW_ACTION.SHOW_VISUALIZER: await this.showVisualizerShot(); break; + case BREW_ACTION.SHOW_GRAPH: + await this.showBrewGraph(); + break; default: break; } diff --git a/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts b/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts index 60e98e2e..74e5bcfe 100644 --- a/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts +++ b/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts @@ -64,6 +64,7 @@ import regression from 'regression'; import { TextToSpeechService } from '../../../services/textToSpeech/text-to-speech.service'; import { SanremoYOUDevice } from '../../../classes/preparationDevice/sanremo/sanremoYOUDevice'; import { SanremoYOUMode } from '../../../enums/preparationDevice/sanremo/sanremoYOUMode'; +import { BookooScale } from '../../../classes/devices/bokooScale'; declare var Plotly; @@ -2393,6 +2394,11 @@ export class BrewBrewingGraphComponent implements OnInit { await this.timerReset(undefined); await this.brewComponent.timer.resetWithoutEmit(false); + if (scale.getScaleType() === ScaleType.BOKOOSCALE) { + //const bookooScale: BookooScale = scale as BookooScale; + //await bookooScale.tareAndStartTimerModeAuto(); + } + this.brewComponent.timer.checkChanges(); this.checkChanges(); diff --git a/src/components/graph-display-card/graph-display-card.component.ts b/src/components/graph-display-card/graph-display-card.component.ts index 4334b623..023557a7 100644 --- a/src/components/graph-display-card/graph-display-card.component.ts +++ b/src/components/graph-display-card/graph-display-card.component.ts @@ -227,7 +227,6 @@ export class GraphDisplayCardComponent implements OnInit { try { Plotly.purge(this.profileDiv.nativeElement); } catch (ex) {} - const graphSettings = this.settings.graph.FILTER; this.weightTrace = { x: [], diff --git a/src/enums/brews/brewAction.ts b/src/enums/brews/brewAction.ts index 96dc6f74..3cc746fa 100755 --- a/src/enums/brews/brewAction.ts +++ b/src/enums/brews/brewAction.ts @@ -14,4 +14,5 @@ export enum BREW_ACTION { RATING = 'RATING', TOGGLE_BEST_BREW = 'TOGGLE_BEST_BREW', SHOW_VISUALIZER = 'SHOW_VISUALIZER', + SHOW_GRAPH = 'SHOW_GRAPH', } diff --git a/src/services/uiGraphHelper.ts b/src/services/uiGraphHelper.ts index 75fe2582..aa464a23 100644 --- a/src/services/uiGraphHelper.ts +++ b/src/services/uiGraphHelper.ts @@ -16,6 +16,7 @@ import { FileEntry } from '@awesome-cordova-plugins/file'; import { UILog } from './uiLog'; import { UIFileHelper } from './uiFileHelper'; import BeanconquerorFlowTestDataDummy from '../assets/BeanconquerorFlowTestDataFourth.json'; +import { Brew } from '../classes/brew/brew'; /** * Handles every helping functionalities @@ -70,6 +71,15 @@ export class UIGraphHelper { await modal.present(); await modal.onWillDismiss(); } + public async detailBrewGraph(_brew: Brew) { + const modal = await this.modalController.create({ + component: GraphDetailComponent, + id: GraphDetailComponent.COMPONENT_ID, + componentProps: { brew: _brew }, + }); + await modal.present(); + await modal.onWillDismiss(); + } public async detailGraphRawData(_flowData: any) { const modal = await this.modalController.create({ From de977c03a3670caac388cd0a1af14487fe0702bb Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Mon, 2 Sep 2024 22:03:11 +0200 Subject: [PATCH 33/55] #785 - Show graph in slider is now rightly extending + paddings --- .../brew-information.component.html | 5 +- .../brew-information.component.scss | 5 +- .../brew-information.component.ts | 63 +++++++++---------- .../welcome-popover.component.ts | 6 -- 4 files changed, 38 insertions(+), 41 deletions(-) diff --git a/src/components/brew-information/brew-information.component.html b/src/components/brew-information/brew-information.component.html index 5d02bbf9..85e0b065 100644 --- a/src/components/brew-information/brew-information.component.html +++ b/src/components/brew-information/brew-information.component.html @@ -246,9 +246,10 @@ - + + - + diff --git a/src/components/brew-information/brew-information.component.scss b/src/components/brew-information/brew-information.component.scss index 03b0008a..6ec6155b 100644 --- a/src/components/brew-information/brew-information.component.scss +++ b/src/components/brew-information/brew-information.component.scss @@ -151,13 +151,16 @@ position: relative; width: 100%; - height: 100%; + + height: auto; } + .add-bottom-spacing{ margin-bottom:20px; } + } diff --git a/src/components/brew-information/brew-information.component.ts b/src/components/brew-information/brew-information.component.ts index abe007b8..3537da56 100644 --- a/src/components/brew-information/brew-information.component.ts +++ b/src/components/brew-information/brew-information.component.ts @@ -10,7 +10,7 @@ import { } from '@angular/core'; import { Brew } from '../../classes/brew/brew'; import { UISettingsStorage } from '../../services/uiSettingsStorage'; -import { ModalController, Platform } from '@ionic/angular'; +import { MenuController, ModalController, Platform } from '@ionic/angular'; import { BREW_ACTION } from '../../enums/brews/brewAction'; import { BrewPopoverActionsComponent } from '../../app/brew/brew-popover-actions/brew-popover-actions.component'; import { Bean } from '../../classes/bean/bean'; @@ -53,14 +53,16 @@ export class BrewInformationComponent implements OnInit { @ViewChild('card', { read: ElementRef }) public cardEl: ElementRef; + public slideOpts = { + allowTouchMove: false, + speed: 400, + slide: 4, + }; + @ViewChild('swiper', { static: false }) public brewInformationSlider: | ElementRef | undefined; - @ViewChild('ionCardParent', { static: false }) public ionCardParent: - | ElementRef - | any; - @ViewChild('brewInformationContainer', { read: ElementRef, static: false }) public brewInformationContainer: ElementRef; @@ -77,6 +79,9 @@ export class BrewInformationComponent implements OnInit { public settings: Settings = null; + public informationContainerHeight: number = undefined; + public informationContainerWidth: number = undefined; + constructor( private readonly uiSettingsStorage: UISettingsStorage, public readonly uiBrewHelper: UIBrewHelper, @@ -95,7 +100,8 @@ export class BrewInformationComponent implements OnInit { private readonly uiFileHelper: UIFileHelper, private readonly uiBeanHelper: UIBeanHelper, private readonly visualizerService: VisualizerService, - private readonly uiGraphHelper: UIGraphHelper + private readonly uiGraphHelper: UIGraphHelper, + private readonly menu: MenuController ) {} public ngOnInit() { @@ -104,6 +110,25 @@ export class BrewInformationComponent implements OnInit { this.bean = this.brew.getBean(); this.preparation = this.brew.getPreparation(); this.mill = this.brew.getMill(); + + setTimeout(() => { + /**We calculcate the information here, to avoid expression-changed in angular, because it always triggered while scrolling cause of calucation functions**/ + this.informationContainerHeight = + this.brewInformationContainer?.nativeElement?.offsetHeight - 50; + this.informationContainerWidth = + this.brewInformationContainer?.nativeElement?.offsetWidth - 50; + + /**If we slide on a bigger tablet, somehow ionic triggering the menu when sliding from right to left, thats why we need to attach us to touchstart/end and to ignore the slide...**/ + this.brewInformationSlider?.nativeElement.swiper.on( + 'touchStart', + () => { + this.menu.swipeGesture(false); + } + ); + this.brewInformationSlider?.nativeElement.swiper.on('touchEnd', () => { + this.menu.swipeGesture(true); + }); + }, 150); } } @@ -146,32 +171,6 @@ export class BrewInformationComponent implements OnInit { await this.uiGraphHelper.detailBrewGraph(this.brew); } - public getElementOffsetWidth() { - if (this.brewInformationContainer?.nativeElement?.offsetWidth) { - return this.brewInformationContainer?.nativeElement?.offsetWidth - 50; - } - return 0; - } - public getElementOffsetHeight() { - if (this.brewInformationContainer?.nativeElement?.offsetHeight) { - return this.brewInformationContainer?.nativeElement?.offsetHeight - 50; - } - return 0; - } - - public getElementHeight() { - if (this.ionCardParent?.el.offsetHeight) { - return this.ionCardParent?.el.offsetHeight; - } - return 200; - } - public getElementWidth() { - if (this.ionCardParent?.el.offsetWidth) { - return this.ionCardParent?.el.offsetWidth; - } - return 200; - } - public async showBrewActions(event): Promise { event.stopPropagation(); event.stopImmediatePropagation(); diff --git a/src/popover/welcome-popover/welcome-popover.component.ts b/src/popover/welcome-popover/welcome-popover.component.ts index 38c67d8a..25145648 100644 --- a/src/popover/welcome-popover/welcome-popover.component.ts +++ b/src/popover/welcome-popover/welcome-popover.component.ts @@ -12,12 +12,6 @@ import { UIPreparationHelper } from '../../services/uiPreparationHelper'; styleUrls: ['./welcome-popover.component.scss'], }) export class WelcomePopoverComponent implements OnInit { - public slideOpts = { - allowTouchMove: false, - speed: 400, - slide: 4, - }; - public slide: number = 1; @ViewChild('slider', { static: false }) public welcomeSlider: | ElementRef From 012020cb8089d38d1b94de4da54bdcfcdd10478e Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Tue, 3 Sep 2024 22:18:42 +0200 Subject: [PATCH 34/55] #785 - Show graph in slider is now rightly extending + paddings --- src/assets/i18n/en.json | 39 +++++++++++++++++++++++++- src/classes/preparation/preparation.ts | 2 ++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index f8b9d034..d9ce4762 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -1347,5 +1347,42 @@ "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_RANGE": "Pressure axes", "PAGE_SETTINGS_LANGUAGE_DUTCH": "Dutch", "DOWNLOAD_IMPORT_EXCEL_TEMPLATES": "Download import templates", - "SHOW_GRAPH": "Graph anzeigen" + "SHOW_GRAPH": "Show graph", + "UPDATE_TEXT_TITLE_TITLE": { + "7.5.0": { + "TITLE": "Version 7.5.0: What's new", + "DESCRIPTION": [ + "New language", + "Dutch support - Thanks to Ygg", + "", + "Brews", + "Collapsing a brew-list is now possible", + "Displaying if a frozen bean was used", + "Bean image now visible on selection", + "Read and import a history shot from the Meticulous", + "Brew list entries are now swipe able when they have graph", + "", + "Beans", + "Collapsing the bean list is now possible", + "Beans are now sortable by bean age", + "Sorting are not reset anymore when archiving a bean", + "", + "Green beans", + "Green beans are now importable - See Settings", + "", + "Settings", + "Set the starting axis for the pressure", + "Import green beans or roasted beans", + "", + "Data corruption detection", + "Beanconqueror now checks if somehow a data corruption occured, and if yes, it shows you a popup to make it possible to import a backup", + "", + "Other", + "Fixing sorting of preparation tools", + "Fixing frozen bean calculation to fix rounding issues", + "Some technical changes in the code", + "Small tweaks" + ] + } + } } diff --git a/src/classes/preparation/preparation.ts b/src/classes/preparation/preparation.ts index 299f7a2c..4e0aa9b1 100755 --- a/src/classes/preparation/preparation.ts +++ b/src/classes/preparation/preparation.ts @@ -256,6 +256,8 @@ export class Preparation implements IPreparation { return 'beanconqueror-preparation-meticulous'; case PREPARATION_TYPES.SANREMO_YOU: return 'beanconqueror-preparation-sanremo-you'; + case PREPARATION_TYPES.XENIA: + return 'beanconqueror-preparation-portafilter'; default: return 'beanconqueror-preparation-custom'; } From 5e05dad44bf4d77a0724bfeae06e8e2f4bef1dda Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Tue, 3 Sep 2024 22:29:04 +0200 Subject: [PATCH 35/55] Fixing tests, love it. --- .../preparation-add-type.component.spec.ts | 8 +++++++- .../preparation-sort-tools.component.spec.ts | 10 ++++++++++ src/app/settings/settings.page.ts | 1 - .../brew-information.component.spec.ts | 2 ++ 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/app/preparation/preparation-add-type/preparation-add-type.component.spec.ts b/src/app/preparation/preparation-add-type/preparation-add-type.component.spec.ts index e1e07077..d96d9c12 100644 --- a/src/app/preparation/preparation-add-type/preparation-add-type.component.spec.ts +++ b/src/app/preparation/preparation-add-type/preparation-add-type.component.spec.ts @@ -7,6 +7,7 @@ import { TranslateModule } from '@ngx-translate/core'; import { UIHelper } from '../../../services/uiHelper'; import { NavParamsMock, UIHelperMock } from '../../../classes/mock'; import { FormsModule } from '@angular/forms'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; describe('PreparationAddTypeComponent', () => { let component: PreparationAddTypeComponent; @@ -15,7 +16,12 @@ describe('PreparationAddTypeComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ declarations: [PreparationAddTypeComponent], - imports: [IonicModule.forRoot(), TranslateModule.forRoot(), FormsModule], + imports: [ + IonicModule.forRoot(), + TranslateModule.forRoot(), + FormsModule, + HttpClientTestingModule, + ], providers: [ { provide: Storage }, { diff --git a/src/app/preparation/preparation-sort-tools/preparation-sort-tools.component.spec.ts b/src/app/preparation/preparation-sort-tools/preparation-sort-tools.component.spec.ts index e8acfd72..c4aafce8 100644 --- a/src/app/preparation/preparation-sort-tools/preparation-sort-tools.component.spec.ts +++ b/src/app/preparation/preparation-sort-tools/preparation-sort-tools.component.spec.ts @@ -4,6 +4,10 @@ import { IonicModule } from '@ionic/angular'; import { PreparationSortToolsComponent } from './preparation-sort-tools.component'; import { TranslateModule } from '@ngx-translate/core'; import { Preparation } from 'src/classes/preparation/preparation'; +import { InAppBrowser } from '@awesome-cordova-plugins/in-app-browser/ngx'; +import { File } from '@awesome-cordova-plugins/file/ngx'; +import { SocialSharing } from '@awesome-cordova-plugins/social-sharing/ngx'; +import { FileTransfer } from '@awesome-cordova-plugins/file-transfer/ngx'; describe('PreparationSortToolsComponent', () => { let component: PreparationSortToolsComponent; @@ -17,6 +21,12 @@ describe('PreparationSortToolsComponent', () => { TranslateModule.forChild(), TranslateModule.forRoot(), ], + providers: [ + { provide: InAppBrowser }, + { provide: File }, + { provide: SocialSharing }, + { provide: FileTransfer }, + ], }).compileComponents(); })); diff --git a/src/app/settings/settings.page.ts b/src/app/settings/settings.page.ts index 787af73a..93928d2f 100644 --- a/src/app/settings/settings.page.ts +++ b/src/app/settings/settings.page.ts @@ -1360,7 +1360,6 @@ export class SettingsPage { ); } } else { - this.__importDummyData(); } } diff --git a/src/components/brew-information/brew-information.component.spec.ts b/src/components/brew-information/brew-information.component.spec.ts index eb87c076..1f962c9a 100644 --- a/src/components/brew-information/brew-information.component.spec.ts +++ b/src/components/brew-information/brew-information.component.spec.ts @@ -19,6 +19,7 @@ import { Bean } from '../../classes/bean/bean'; import { Preparation } from '../../classes/preparation/preparation'; import { Mill } from '../../classes/mill/mill'; import { PipesModule } from 'src/pipes/pipes.module'; +import { FileChooser } from '@awesome-cordova-plugins/file-chooser/ngx'; describe('BrewInformationComponent', () => { let component: BrewInformationComponent; @@ -42,6 +43,7 @@ describe('BrewInformationComponent', () => { { provide: UIImage, useClass: UIImageMock }, { provide: SocialSharing }, { provide: FileTransfer }, + { provide: FileChooser }, ], }).compileComponents(); })); From a6bfc9d4b7412d631ed2e0420f20e734cd0b5ad7 Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Wed, 4 Sep 2024 20:55:21 +0200 Subject: [PATCH 36/55] Adding argos temperature --- src/app/settings/settings.page.html | 6 +- src/app/settings/settings.page.ts | 69 ++++++++++++++----- src/classes/devices/argosThermometer.ts | 18 +++-- .../brew-brewing-graph.component.ts | 16 ++--- 4 files changed, 74 insertions(+), 35 deletions(-) diff --git a/src/app/settings/settings.page.html b/src/app/settings/settings.page.html index cd21b706..39465cb3 100644 --- a/src/app/settings/settings.page.html +++ b/src/app/settings/settings.page.html @@ -51,10 +51,14 @@

{{"EXPORT" | translate}}

- + Temp: Export Brew By Weight - Xenia + + Temp: Export Brew By Weight - Sanremo + + {{"EXCEL_EXPORT" | translate}} diff --git a/src/app/settings/settings.page.ts b/src/app/settings/settings.page.ts index 93928d2f..63d1ef7a 100644 --- a/src/app/settings/settings.page.ts +++ b/src/app/settings/settings.page.ts @@ -1219,46 +1219,79 @@ export class SettingsPage { return `${newValue}`; } - public doWeHaveBrewByWeights(): boolean { + public doWeHaveBrewByWeights(_type: string): boolean { const allPreparations = this.uiPreparationStorage.getAllEntries(); for (const prep of allPreparations) { if ( + _type === 'xenia' && prep.connectedPreparationDevice.type === PreparationDeviceType.XENIA ) { return true; + } else if ( + _type === 'sanremo' && + prep.connectedPreparationDevice.type === + PreparationDeviceType.SANREMO_YOU + ) { + return true; } } } - public async exportBrewByWeight() { + public async exportBrewByWeight(_type: string) { await this.uiAlert.showLoadingSpinner(); try { - const allXeniaPreps = []; + const allPreps = []; let allPreparations = this.uiPreparationStorage.getAllEntries(); // Just take 60, else the excel will be exploding. allPreparations = allPreparations.reverse().slice(0, 60); for (const prep of allPreparations) { if ( + _type === 'xenia' && prep.connectedPreparationDevice.type === PreparationDeviceType.XENIA ) { - allXeniaPreps.push(prep); + allPreps.push(prep); + } else if ( + _type === 'sanremo' && + prep.connectedPreparationDevice.type === + PreparationDeviceType.SANREMO_YOU + ) { + allPreps.push(prep); } } - const allBrewsWithProfiles = this.uiBrewStorage - .getAllEntries() - .filter( - (e) => - e.flow_profile !== null && - e.flow_profile !== undefined && - e.flow_profile !== '' && - allXeniaPreps.find( - (pr) => pr.config.uuid === e.method_of_preparation - ) && - e.preparationDeviceBrew && - e.preparationDeviceBrew.params && - e.preparationDeviceBrew.params.brew_by_weight_active === true - ); + let allBrewsWithProfiles = []; + + if (_type === 'xenia') { + allBrewsWithProfiles = this.uiBrewStorage + .getAllEntries() + .filter( + (e) => + e.flow_profile !== null && + e.flow_profile !== undefined && + e.flow_profile !== '' && + allPreps.find( + (pr) => pr.config.uuid === e.method_of_preparation + ) && + e.preparationDeviceBrew && + e.preparationDeviceBrew.params && + e.preparationDeviceBrew.params.brew_by_weight_active === true + ); + } else if (_type === 'sanremo') { + allBrewsWithProfiles = this.uiBrewStorage + .getAllEntries() + .filter( + (e) => + e.flow_profile !== null && + e.flow_profile !== undefined && + e.flow_profile !== '' && + allPreps.find( + (pr) => pr.config.uuid === e.method_of_preparation + ) && + e.preparationDeviceBrew && + e.preparationDeviceBrew.params && + e.preparationDeviceBrew.params.stopAtWeight > 0 + ); + } const allBrewFlows: Array<{ BREW: Brew; FLOW: BrewFlow }> = []; for await (const brew of allBrewsWithProfiles) { diff --git a/src/classes/devices/argosThermometer.ts b/src/classes/devices/argosThermometer.ts index 8957aadc..a812905e 100644 --- a/src/classes/devices/argosThermometer.ts +++ b/src/classes/devices/argosThermometer.ts @@ -44,23 +44,27 @@ export class ArgosThermometer extends TemperatureDevice { ArgosThermometer.TEMPERATURE_CHAR_UUID, async (_data: any) => { - let newData = new Uint8Array(_data.slice(0, -1)); - this.parseStatusUpdate(newData); + const rawData = _data; //new Uint8Array(_data.slice(0, -1)); + this.parseStatusUpdate(rawData); }, (_data: any) => {} ); } - private parseStatusUpdate(temperatureRawStatus: Uint8Array) { + private parseStatusUpdate(temperatureRawStatus: any) { this.logger.log( 'temperatureRawStatus received is: ' + temperatureRawStatus ); + const formatNumber = new Intl.NumberFormat(undefined, { + minimumIntegerDigits: 2, + }).format; - const temperature_in_f = - temperatureRawStatus[-1] << (8 + temperatureRawStatus[-2]); - console.log('New temperature inc' + temperature_in_f); - this.setTemperature(temperature_in_f, temperatureRawStatus); + const setPoint = + ((temperatureRawStatus.getUint16(5, true) / 127) * 5) / 9 - 32; // Convert from F to C + const data = formatNumber(setPoint); + + this.setTemperature(Number(data), temperatureRawStatus); } private deattachNotification() { diff --git a/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts b/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts index 74e5bcfe..264e43c6 100644 --- a/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts +++ b/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts @@ -1875,6 +1875,7 @@ export class BrewBrewingGraphComponent implements OnInit { .brewBrewingPreparationDeviceEl.preparationDevice as SanremoYOUDevice; this.stopFetchingDataFromSanremoYOU(); + const setSanremoData = () => { const temp = prepDeviceCall.getTemperature(); const press = prepDeviceCall.getPressure(); @@ -2529,8 +2530,8 @@ export class BrewBrewingGraphComponent implements OnInit { const isEspressoBrew: boolean = this.data.getPreparation().style_type === PREPARATION_STYLE_TYPE.ESPRESSO; - this.textToSpeechWeightInterval = setInterval(() => { - this.ngZone.runOutsideAngular(() => { + this.ngZone.runOutsideAngular(() => { + this.textToSpeechWeightInterval = setInterval(() => { if (this.flowProfileTempAll.length > 0) { const actualScaleWeight = this.flowProfileTempAll.slice(-1)[0].weight; @@ -2550,18 +2551,15 @@ export class BrewBrewingGraphComponent implements OnInit { } } } - }); - }, this.settings.text_to_speech_interval_rate); - - this.textToSpeechTimerInterval = setInterval(() => { - this.ngZone.runOutsideAngular(() => { + }, this.settings.text_to_speech_interval_rate); + this.textToSpeechTimerInterval = setInterval(() => { this.textToSpeech.speak( this.translate.instant('TEXT_TO_SPEECH.TIME') + ' ' + this.data.brew_time ); - }); - }, 5000); + }, 5000); + }); } } From 55c9c894ebb3d4232ce48870523fc35f6f9b23ca Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Wed, 4 Sep 2024 22:23:25 +0200 Subject: [PATCH 37/55] Adding the templates for import usage --- .../Green_Bean_Import_Template.xlsx | Bin 0 -> 13721 bytes .../Roasted_Bean_Import_Template.xlsx | Bin 0 -> 134332 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 resources/excel-templates/Green_Bean_Import_Template.xlsx create mode 100644 resources/excel-templates/Roasted_Bean_Import_Template.xlsx diff --git a/resources/excel-templates/Green_Bean_Import_Template.xlsx b/resources/excel-templates/Green_Bean_Import_Template.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..cacd3bdd292330c5cda8b78816fc43111013ac1d GIT binary patch literal 13721 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 literal 0 HcmV?d00001 From 2789e455f1e7b076c335b1f1ff28ec0dc4864b80 Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Wed, 4 Sep 2024 23:00:32 +0200 Subject: [PATCH 38/55] Disable swiper if swiper hast just one slide, to make it possible to slide the menu still, because swiper grabs it else --- .../brew-information.component.ts | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/components/brew-information/brew-information.component.ts b/src/components/brew-information/brew-information.component.ts index 3537da56..4706c8da 100644 --- a/src/components/brew-information/brew-information.component.ts +++ b/src/components/brew-information/brew-information.component.ts @@ -118,16 +118,24 @@ export class BrewInformationComponent implements OnInit { this.informationContainerWidth = this.brewInformationContainer?.nativeElement?.offsetWidth - 50; - /**If we slide on a bigger tablet, somehow ionic triggering the menu when sliding from right to left, thats why we need to attach us to touchstart/end and to ignore the slide...**/ - this.brewInformationSlider?.nativeElement.swiper.on( - 'touchStart', - () => { - this.menu.swipeGesture(false); - } - ); - this.brewInformationSlider?.nativeElement.swiper.on('touchEnd', () => { - this.menu.swipeGesture(true); - }); + if (this.brew.flow_profile) { + /**If we slide on a bigger tablet, somehow ionic triggering the menu when sliding from right to left, thats why we need to attach us to touchstart/end and to ignore the slide...**/ + this.brewInformationSlider?.nativeElement.swiper.on( + 'touchStart', + () => { + //We got two slides + this.menu.swipeGesture(false); + } + ); + this.brewInformationSlider?.nativeElement.swiper.on( + 'touchEnd', + () => { + this.menu.swipeGesture(true); + } + ); + } else { + this.brewInformationSlider?.nativeElement.swiper.disable(); + } }, 150); } } From bd0bd39fb2defb1623b851ab1c9177ae0a40a284 Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Thu, 5 Sep 2024 20:59:44 +0200 Subject: [PATCH 39/55] #788 - Removing the donation --- src/app/app.component.html | 24 +----------------------- src/app/app.component.ts | 28 +++------------------------- 2 files changed, 4 insertions(+), 48 deletions(-) diff --git a/src/app/app.component.html b/src/app/app.component.html index 6b5e19ef..5926893c 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -54,27 +54,9 @@ {{pages.settings.title | translate}} - - -

{{"SUPPORT_ME" | translate}}
-
- - - - - - - - - - - - - - - + @@ -99,10 +81,6 @@ - - - -
Date: Thu, 5 Sep 2024 21:00:43 +0200 Subject: [PATCH 40/55] Changes --- src/assets/i18n/en.json | 7 ++- .../sanremo/sanremoYOUDevice.ts | 45 +++++++++++++-- .../brew-information.component.ts | 36 ++++++++++-- .../brew-brewing-graph.component.html | 6 +- .../brew-brewing-graph.component.scss | 5 ++ .../brew-brewing-graph.component.ts | 57 +++++++++++-------- ...-brewing-preparation-device.component.html | 15 ++++- .../sanremo/sanremoYOUMode.ts | 5 +- 8 files changed, 132 insertions(+), 44 deletions(-) diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index d9ce4762..31ba45ea 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -1070,8 +1070,11 @@ "STOP_AT_WEIGHT": "Stop at weight", "SELECT_MODE": "Select mode", "MODE_LISTENING": "Listening", - "MODE_CONTROL": "Control" - + "MODE_CONTROL": "Control", + "MANUAL_CONTROLLING": "Manual Control", + "PROFILE_P1_CONTROLLING": "Profile P1 Control", + "PROFILE_P2_CONTROLLING": "Profile P2 Control", + "PROFILE_P3_CONTROLLING": "Profile P3 Control" } }, "DEVICE_CONNECTION": "Device connection", diff --git a/src/classes/preparationDevice/sanremo/sanremoYOUDevice.ts b/src/classes/preparationDevice/sanremo/sanremoYOUDevice.ts index 4cf6023f..0cb02307 100644 --- a/src/classes/preparationDevice/sanremo/sanremoYOUDevice.ts +++ b/src/classes/preparationDevice/sanremo/sanremoYOUDevice.ts @@ -4,11 +4,13 @@ import { Preparation } from '../../preparation/preparation'; import { ISanremoYOUParams } from '../../../interfaces/preparationDevices/sanremoYOU/iSanremoYOUParams'; import { SanremoYOUMode } from '../../../enums/preparationDevice/sanremo/sanremoYOUMode'; + declare var cordova; export class SanremoYOUDevice extends PreparationDevice { public scriptList: Array<{ INDEX: number; TITLE: string }> = []; private connectionURL: string = ''; + private statusPhase: number = 0; constructor(protected httpClient: HttpClient, _preparation: Preparation) { super(httpClient, _preparation); @@ -105,9 +107,11 @@ export class SanremoYOUDevice extends PreparationDevice { const parsedJSON = JSON.parse(response.data); const temp = parsedJSON.tempBoilerCoffe; const press = parsedJSON.pumpPress * 10; + const statusPhase = parsedJSON.statusPhase; this.temperature = temp; this.pressure = press; + this.statusPhase = statusPhase; if (_callback) { _callback(); } @@ -119,13 +123,22 @@ export class SanremoYOUDevice extends PreparationDevice { ); } - public startShot() { + public startShot(_mode: SanremoYOUMode) { const promise = new Promise((resolve, reject) => { const options = { method: 'get', }; - let urlAdding = '/api/action/man'; + let urlAdding = ''; + if (_mode === SanremoYOUMode.MANUAL_CONTROLLING) { + urlAdding = '/api/action/man'; + } else if (_mode === SanremoYOUMode.PROFILE_P1_CONTROLLING) { + urlAdding = '/api/action/p1'; + } else if (_mode === SanremoYOUMode.PROFILE_P2_CONTROLLING) { + urlAdding = '/api/action/p2'; + } else if (_mode === SanremoYOUMode.PROFILE_P3_CONTROLLING) { + urlAdding = '/api/action/p3'; + } cordova.plugin.http.sendRequest( this.getPreparation().connectedPreparationDevice.url + urlAdding, @@ -146,13 +159,35 @@ export class SanremoYOUDevice extends PreparationDevice { }); return promise; } - public stopShot() { - const promise = new Promise((resolve, reject) => { + public stopShot(_mode: SanremoYOUMode) { + const promise = new Promise(async (resolve, reject) => { const options = { method: 'get', }; - let urlAdding = '/api/action/man'; + let urlAdding = ''; + if (_mode === SanremoYOUMode.MANUAL_CONTROLLING) { + urlAdding = '/api/action/man'; + } else if (_mode === SanremoYOUMode.PROFILE_P1_CONTROLLING) { + urlAdding = '/api/action/p1'; + } else if (_mode === SanremoYOUMode.PROFILE_P2_CONTROLLING) { + urlAdding = '/api/action/p2'; + } else if (_mode === SanremoYOUMode.PROFILE_P3_CONTROLLING) { + urlAdding = '/api/action/p3'; + } + + if (_mode !== SanremoYOUMode.MANUAL_CONTROLLING) { + await new Promise((resolveIntern) => { + this.fetchRuntimeData(() => { + resolveIntern(undefined); + }); + }); + if (this.statusPhase === 0) { + //Machine has already stoped, skipp. + reject(undefined); + return; + } + } cordova.plugin.http.sendRequest( this.getPreparation().connectedPreparationDevice.url + urlAdding, diff --git a/src/components/brew-information/brew-information.component.ts b/src/components/brew-information/brew-information.component.ts index 4706c8da..92fe952f 100644 --- a/src/components/brew-information/brew-information.component.ts +++ b/src/components/brew-information/brew-information.component.ts @@ -48,7 +48,7 @@ declare var window; }) export class BrewInformationComponent implements OnInit { @Input() public brew: Brew; - @Input() public collapsed: boolean = false; + public _collapsed: boolean = undefined; @Input() public layout: string = 'brew'; @ViewChild('card', { read: ElementRef }) public cardEl: ElementRef; @@ -104,6 +104,34 @@ export class BrewInformationComponent implements OnInit { private readonly menu: MenuController ) {} + @Input() set collapsed(value: boolean) { + let retrigger: boolean = false; + if (value !== this._collapsed && this._collapsed !== undefined) { + //Retrigger + retrigger = true; + } + this._collapsed = value; + + if (retrigger) { + //When setting the container to undefined, the *ngIf removes the graph, and setting after that the new height, the element will be spawned correctly. + this.informationContainerWidth = undefined; + setTimeout(() => { + this.calculcationInformationContainer(); + }, 50); + } + } + + get collapsed(): boolean { + return this._collapsed; + } + + private calculcationInformationContainer() { + /**We calculcate the information here, to avoid expression-changed in angular, because it always triggered while scrolling cause of calucation functions**/ + this.informationContainerHeight = + this.brewInformationContainer?.nativeElement?.offsetHeight - 50; + this.informationContainerWidth = + this.brewInformationContainer?.nativeElement?.offsetWidth - 50; + } public ngOnInit() { if (this.brew) { this.settings = this.uiSettingsStorage.getSettings(); @@ -112,11 +140,7 @@ export class BrewInformationComponent implements OnInit { this.mill = this.brew.getMill(); setTimeout(() => { - /**We calculcate the information here, to avoid expression-changed in angular, because it always triggered while scrolling cause of calucation functions**/ - this.informationContainerHeight = - this.brewInformationContainer?.nativeElement?.offsetHeight - 50; - this.informationContainerWidth = - this.brewInformationContainer?.nativeElement?.offsetWidth - 50; + this.calculcationInformationContainer(); if (this.brew.flow_profile) { /**If we slide on a bigger tablet, somehow ionic triggering the menu when sliding from right to left, thats why we need to attach us to touchstart/end and to ignore the slide...**/ diff --git a/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.html b/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.html index 3c6db90c..11fd6ceb 100644 --- a/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.html +++ b/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.html @@ -37,7 +37,7 @@
? g
-
({{data.getBrewRatio()}})
+
({{data.getBrewRatio()}})
@@ -48,8 +48,8 @@ style="padding-right:5px;"> -
? g/s
-
? g/s
+
? g/s
+
? g/s
diff --git a/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.scss b/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.scss index 12eb90d4..eb6fc3c8 100644 --- a/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.scss +++ b/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.scss @@ -106,4 +106,9 @@ font-size: 30px; } } + .information-tile-no-overflow { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } } diff --git a/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts b/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts index 264e43c6..fa2b20d8 100644 --- a/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts +++ b/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts @@ -1801,20 +1801,22 @@ export class BrewBrewingGraphComponent implements OnInit { PreparationDeviceType.SANREMO_YOU && _event !== 'sanremo_you' && this.machineStopScriptWasTriggered === false && - this.data.preparationDeviceBrew.params.selectedMode === - SanremoYOUMode.CONTROLLING + this.data.preparationDeviceBrew.params.selectedMode !== + SanremoYOUMode.LISTENING ) { this.machineStopScriptWasTriggered = true; this.uiLog.log(`Sanremo YOU - Pause button pressed, stop shot`); const prepDeviceCall: SanremoYOUDevice = this.brewComponent ?.brewBrewingPreparationDeviceEl?.preparationDevice as SanremoYOUDevice; - prepDeviceCall.stopShot().catch((_msg) => { - this.uiToast.showInfoToast( - 'We could not stop - manual triggered: ' + _msg, - false - ); - this.uiLog.log('We could not stop - manual triggered: ' + _msg); - }); + prepDeviceCall + .stopShot(this.data.preparationDeviceBrew.params.selectedMode) + .catch((_msg) => { + this.uiToast.showInfoToast( + 'We could not stop - manual triggered: ' + _msg, + false + ); + this.uiLog.log('We could not stop - manual triggered: ' + _msg); + }); this.stopFetchingDataFromSanremoYOU(); } @@ -2338,16 +2340,18 @@ export class BrewBrewingGraphComponent implements OnInit { .brewBrewingPreparationDeviceEl.preparationDevice as SanremoYOUDevice; if ( - this.data.preparationDeviceBrew?.params.selectedMode === - SanremoYOUMode.CONTROLLING + this.data.preparationDeviceBrew?.params.selectedMode !== + SanremoYOUMode.LISTENING ) { - prepDeviceCall.startShot().catch((_msg) => { - this.uiLog.log('We could not start shot on sanremo: ' + _msg); - this.uiToast.showInfoToast( - 'We could not start shot on sanremo: ' + _msg, - false - ); - }); + prepDeviceCall + .startShot(this.data.preparationDeviceBrew?.params.selectedMode) + .catch((_msg) => { + this.uiLog.log('We could not start shot on sanremo: ' + _msg); + this.uiToast.showInfoToast( + 'We could not start shot on sanremo: ' + _msg, + false + ); + }); } if ( @@ -2939,10 +2943,15 @@ export class BrewBrewingGraphComponent implements OnInit { .brewBrewingPreparationDeviceEl.preparationDevice as SanremoYOUDevice; this.uiLog.log(`Sanremo YOU Stop: ${_actualScaleWeight}`); - prepDeviceCall.stopShot().catch((_msg) => { - this.uiToast.showInfoToast('We could not stop at weight: ' + _msg, false); - this.uiLog.log('We could not start script at weight: ' + _msg); - }); + prepDeviceCall + .stopShot(this.data.preparationDeviceBrew.params.selectedMode) + .catch((_msg) => { + this.uiToast.showInfoToast( + 'We could not stop at weight: ' + _msg, + false + ); + this.uiLog.log('We could not start script at weight: ' + _msg); + }); // This will be just called once, we stopped the shot and now we check if we directly shall stop or not if ( @@ -3037,8 +3046,8 @@ export class BrewBrewingGraphComponent implements OnInit { } else if ( this.brewComponent.brewBrewingPreparationDeviceEl.getPreparationDeviceType() === PreparationDeviceType.SANREMO_YOU && - this.data.preparationDeviceBrew.params.selectedMode === - SanremoYOUMode.CONTROLLING + this.data.preparationDeviceBrew.params.selectedMode !== + SanremoYOUMode.LISTENING ) { /**We call this function before the if, because we still log the data**/ const thresholdHit = this.calculateBrewByWeight( diff --git a/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.html b/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.html index 0c923610..716cb30d 100644 --- a/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.html +++ b/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.html @@ -89,12 +89,21 @@ {{"PREPARATION_DEVICE.TYPE_SANREMO_YOU.MODE_LISTENING" | translate}} - - {{"PREPARATION_DEVICE.TYPE_SANREMO_YOU.MODE_CONTROL" | translate}} + + {{"PREPARATION_DEVICE.TYPE_SANREMO_YOU.MANUAL_CONTROLLING" | translate}} + + + {{"PREPARATION_DEVICE.TYPE_SANREMO_YOU.PROFILE_P1_CONTROLLING" | translate}} + + + {{"PREPARATION_DEVICE.TYPE_SANREMO_YOU.PROFILE_P2_CONTROLLING" | translate}} + + + {{"PREPARATION_DEVICE.TYPE_SANREMO_YOU.PROFILE_P3_CONTROLLING" | translate}}
- + diff --git a/src/enums/preparationDevice/sanremo/sanremoYOUMode.ts b/src/enums/preparationDevice/sanremo/sanremoYOUMode.ts index c66e9caa..1eaf2fd5 100755 --- a/src/enums/preparationDevice/sanremo/sanremoYOUMode.ts +++ b/src/enums/preparationDevice/sanremo/sanremoYOUMode.ts @@ -1,4 +1,7 @@ export enum SanremoYOUMode { LISTENING = 'LISTENING', - CONTROLLING = 'CONTROLLING', + MANUAL_CONTROLLING = 'MANUAL_CONTROLLING', + PROFILE_P1_CONTROLLING = 'PROFILE_P1_CONTROLLING', + PROFILE_P2_CONTROLLING = 'PROFILE_P2_CONTROLLING', + PROFILE_P3_CONTROLLING = 'PROFILE_P3_CONTROLLING', } From 8b340a3cef1794e665a9ebd8acb2ecf7081aadda Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Thu, 5 Sep 2024 21:07:01 +0200 Subject: [PATCH 41/55] Changes --- .../brews/brew-brewing-graph/brew-brewing-graph.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts b/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts index fa2b20d8..7240df8b 100644 --- a/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts +++ b/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts @@ -2765,9 +2765,9 @@ export class BrewBrewingGraphComponent implements OnInit { * We try to match also turbo-shots which are like 7-8 grams. * Scales with just 3 values per second would be like 7 / 3 values per second = 2.33g increase each tick. * So we won't get jump from like 1 to 10 gram, then to like 40 grams - * Update 26.08.24 - We change from 5 to 6, because we had one shot where the value jumped from 0 to 5,5 and we didn't track anymore + * Update 26.08.24 - We change from 5 to 7, because we had one shot where the value jumped from 0 to 5,5 and we didn't track anymore */ - const plausibleEspressoWeightIncreaseBound: number = 6; + const plausibleEspressoWeightIncreaseBound: number = 7; risingFactorOK = entryBeforeVal + plausibleEspressoWeightIncreaseBound >= weight; From 055c73827bf6551f7cd3f8990d9c3aa05e8957d0 Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Thu, 5 Sep 2024 22:01:36 +0200 Subject: [PATCH 42/55] Fixing timeouts for android --- .../brew-information/brew-information.component.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/brew-information/brew-information.component.ts b/src/components/brew-information/brew-information.component.ts index 92fe952f..71589351 100644 --- a/src/components/brew-information/brew-information.component.ts +++ b/src/components/brew-information/brew-information.component.ts @@ -139,6 +139,11 @@ export class BrewInformationComponent implements OnInit { this.preparation = this.brew.getPreparation(); this.mill = this.brew.getMill(); + /**On Android we somehow need a bit more ms for the calc... specific on older once**/ + let timeoutMS = 350; + if (this.platform.is('ios')) { + timeoutMS = 150; + } setTimeout(() => { this.calculcationInformationContainer(); @@ -160,7 +165,7 @@ export class BrewInformationComponent implements OnInit { } else { this.brewInformationSlider?.nativeElement.swiper.disable(); } - }, 150); + }, timeoutMS); } } From 88374de012654f931fbcb51347f003366f600e34 Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Sat, 7 Sep 2024 22:00:02 +0200 Subject: [PATCH 43/55] Adding target weight line --- src/assets/i18n/en.json | 4 +- src/assets/js/matomo.js | 2862 +++++++++-------- .../brew-brewing-graph.component.ts | 81 +- ...-brewing-preparation-device.component.html | 11 +- ...ew-brewing-preparation-device.component.ts | 31 + 5 files changed, 1585 insertions(+), 1404 deletions(-) diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 31ba45ea..f38dd487 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -1074,7 +1074,9 @@ "MANUAL_CONTROLLING": "Manual Control", "PROFILE_P1_CONTROLLING": "Profile P1 Control", "PROFILE_P2_CONTROLLING": "Profile P2 Control", - "PROFILE_P3_CONTROLLING": "Profile P3 Control" + "PROFILE_P3_CONTROLLING": "Profile P3 Control", + "NO_PROFILE_TARGET_WEIGHT_INFORMATION": "You did not define a target weight, the chosen profile will be fully executed", + "NO_MANUAL_TARGET_WEIGHT_INFORMATION": "You did not define a target weight, please stop extraction manually" } }, "DEVICE_CONNECTION": "Device connection", diff --git a/src/assets/js/matomo.js b/src/assets/js/matomo.js index ec939f98..dcb2a84c 100644 --- a/src/assets/js/matomo.js +++ b/src/assets/js/matomo.js @@ -394,41 +394,60 @@ if (typeof window.Matomo !== 'object') { av = String(av); if ( av.indexOf('?' + aw + '=') === -1 && - av.indexOf('&' + aw + '=') === -1 + av.indexOf('&' + aw + '=') === -1 && + av.indexOf('#' + aw + '=') === -1 ) { return av; } + var aB = ''; + var aD = av.indexOf('#'); + if (aD !== -1) { + aB = av.substr(aD + 1); + av = av.substr(0, aD); + } var ax = av.indexOf('?'); - if (ax === -1) { - return av; + var au = ''; + var aA = av; + if (ax > -1) { + au = av.substr(ax + 1); + aA = av.substr(0, ax); } - var au = av.substr(ax + 1); - var aB = av.substr(0, ax); + var az = function (aF) { + var aH; + var aG = aF.length - 1; + for (aG; aG >= 0; aG--) { + aH = aF[aG].split('=')[0]; + if (aH === aw) { + aF.splice(aG, 1); + } + } + return aF; + }; if (au) { - var aC = ''; - var aE = au.indexOf('#'); - if (aE !== -1) { - aC = au.substr(aE + 1); - au = au.substr(0, aE); + var aC = az(au.split('&')).join('&'); + if (aC) { + aA += '?' + aC; } - var ay; - var aA = au.split('&'); - var az = aA.length - 1; - for (az; az >= 0; az--) { - ay = aA[az].split('=')[0]; - if (ay === aw) { - aA.splice(az, 1); - } + } + if (aB && aB.indexOf('=') > 0) { + var ay = aB.charAt(0) === '?'; + if (ay) { + aB = aB.substr(1); } - var aD = aA.join('&'); - if (aD) { - aB = aB + '?' + aD; + var aE = az(aB.split('&')).join('&'); + if (aE) { + aA += '#'; + if (ay) { + aA += '?'; + } + aA += aE; } - if (aC) { - aB += '#' + aC; + } else { + if (aB) { + aA += '#' + aB; } } - return aB; + return aA; } function e(aw, av) { var au = '[\\?&#]' + av + '=([^&#]*)'; @@ -1497,27 +1516,27 @@ if (typeof window.Matomo !== 'object') { function U(ct, cn) { var bV = this, bo = 'mtm_consent', - c0 = 'mtm_cookie_consent', - c9 = 'mtm_consent_removed', + c1 = 'mtm_cookie_consent', + da = 'mtm_consent_removed', ch = af(K.domain, X.location.href, O()), - dh = P(ch[0]), + di = P(ch[0]), bZ = p(ch[1]), bA = p(ch[2]), - df = false, + dg = false, cx = 'GET', - dA = cx, + dC = cx, aQ = 'application/x-www-form-urlencoded; charset=UTF-8', cR = aQ, aM = ct || '', bU = '', - dp = '', + dr = '', cD = '', cj = cn || '', bL = '', b0 = '', bf, bu = '', - dw = [ + dy = [ '7z', 'aac', 'apk', @@ -1601,15 +1620,15 @@ if (typeof window.Matomo !== 'object') { 'z', 'zip', ], - aG = [dh], + aG = [di], bM = [], cS = ['.paypal.com'], cy = [], bY = [], bj = [], bW = 500, - dk = true, - c6, + dl = true, + c7, bg, b4, b1, @@ -1624,20 +1643,54 @@ if (typeof window.Matomo !== 'object') { 'utm_medium', ], bT = ['pk_kwd', 'mtm_kwd', 'piwik_kwd', 'matomo_kwd', 'utm_term'], + cV = [ + 'mtm_campaign', + 'matomo_campaign', + 'mtm_cpn', + 'pk_campaign', + 'piwik_campaign', + 'pk_cpn', + 'utm_campaign', + 'mtm_keyword', + 'matomo_kwd', + 'mtm_kwd', + 'pk_keyword', + 'piwik_kwd', + 'pk_kwd', + 'utm_term', + 'mtm_source', + 'pk_source', + 'utm_source', + 'mtm_medium', + 'pk_medium', + 'utm_medium', + 'mtm_content', + 'pk_content', + 'utm_content', + 'mtm_cid', + 'pk_cid', + 'utm_id', + 'mtm_clid', + 'mtm_group', + 'pk_group', + 'mtm_placement', + 'pk_placement', + ], bv = '_pk_', aD = 'pk_vid', ba = 180, - dm, + dp, bC, b5 = false, aR = 'Lax', bx = false, - dd, + de, bp, + dm = true, bI, - c7 = 33955200000, + c8 = 33955200000, cE = 1800000, - dv = 15768000000, + dx = 15768000000, bd = true, bR = false, bs = false, @@ -1649,22 +1702,22 @@ if (typeof window.Matomo !== 'object') { bz = {}, bG = 200, cN = {}, - dq = {}, - dx = {}, + ds = {}, + dz = {}, a3 = {}, co = [], by = false, ck = false, cp = [], cu = false, - cY = false, + cZ = false, ax = false, - dy = false, - da = false, + dA = false, + db = false, aW = false, bn = w(), cT = null, - dn = null, + dq = null, a0, bO, cl = ar, @@ -1673,210 +1726,221 @@ if (typeof window.Matomo !== 'object') { bN = false, cK = 0, bH = ['id', 'ses', 'cvar', 'ref'], - cX = false, + cY = false, bP = null, - c8 = [], + c9 = [], cM = [], aF = Y++, aE = false, - dl = true, - cV = false; + dn = true, + cW = false; try { bu = K.title; } catch (cU) { bu = ''; } - function aL(dL) { - if (bx && dL !== c9) { + function aL(dN) { + if (bx && dN !== da) { return 0; } - var dJ = new RegExp('(^|;)[ ]*' + dL + '=([^;]*)'), - dK = dJ.exec(K.cookie); - return dK ? W(dK[2]) : 0; + var dL = new RegExp('(^|;)[ ]*' + dN + '=([^;]*)'), + dM = dL.exec(K.cookie); + return dM ? W(dM[2]) : 0; } - bP = !aL(c9); - function dE(dN, dO, dR, dQ, dL, dM, dP) { - if (bx && dN !== c9) { + bP = !aL(da); + function dG(dP, dQ, dT, dS, dN, dO, dR) { + if (bx && dP !== da) { return; } - var dK; - if (dR) { - dK = new Date(); - dK.setTime(dK.getTime() + dR); + var dM; + if (dT) { + dM = new Date(); + dM.setTime(dM.getTime() + dT); } - if (!dP) { - dP = 'Lax'; + if (!dR) { + dR = 'Lax'; } K.cookie = - dN + + dP + '=' + - u(dO) + - (dR ? ';expires=' + dK.toGMTString() : '') + + u(dQ) + + (dT ? ';expires=' + dM.toGMTString() : '') + ';path=' + - (dQ || '/') + - (dL ? ';domain=' + dL : '') + - (dM ? ';secure' : '') + + (dS || '/') + + (dN ? ';domain=' + dN : '') + + (dO ? ';secure' : '') + ';SameSite=' + - dP; - if ((!dR || dR >= 0) && aL(dN) !== String(dO)) { - var dJ = + dR; + if ((!dT || dT >= 0) && aL(dP) !== String(dQ)) { + var dL = 'There was an error setting cookie `' + - dN + + dP + '`. Please check domain and path.'; - ap(dJ); + ap(dL); } } - function cf(dJ) { - var dL, dK; - dJ = j(dJ, aD); - dJ = j(dJ, 'ignore_referrer'); - dJ = j(dJ, 'ignore_referer'); - for (dK = 0; dK < cy.length; dK++) { - dJ = j(dJ, cy[dK]); + function cf(dL) { + var dN, dM; + if (dm !== true && !cY) { + for (dM = 0; dM < cH.length; dM++) { + dL = j(dL, cH[dM]); + } + for (dM = 0; dM < bT.length; dM++) { + dL = j(dL, bT[dM]); + } + for (dM = 0; dM < cV.length; dM++) { + dL = j(dL, cV[dM]); + } + } + dL = j(dL, aD); + dL = j(dL, 'ignore_referrer'); + dL = j(dL, 'ignore_referer'); + for (dM = 0; dM < cy.length; dM++) { + dL = j(dL, cy[dM]); } if (b1) { - dL = new RegExp('#.*'); - return dJ.replace(dL, ''); + dN = new RegExp('#.*'); + return dL.replace(dN, ''); } - return dJ; + return dL; } - function b8(dL, dJ) { - var dM = t(dJ), - dK; - if (dM) { - return dJ; + function b8(dN, dL) { + var dO = t(dL), + dM; + if (dO) { + return dL; } - if (dJ.slice(0, 1) === '/') { - return t(dL) + '://' + d(dL) + dJ; + if (dL.slice(0, 1) === '/') { + return t(dN) + '://' + d(dN) + dL; } - dL = cf(dL); - dK = dL.indexOf('?'); - if (dK >= 0) { - dL = dL.slice(0, dK); + dN = cf(dN); + dM = dN.indexOf('?'); + if (dM >= 0) { + dN = dN.slice(0, dM); } - dK = dL.lastIndexOf('/'); - if (dK !== dL.length - 1) { - dL = dL.slice(0, dK + 1); + dM = dN.lastIndexOf('/'); + if (dM !== dN.length - 1) { + dN = dN.slice(0, dM + 1); } - return dL + dJ; + return dN + dL; } - function c4(dL, dJ) { - var dK; + function c5(dN, dL) { + var dM; + dN = String(dN).toLowerCase(); dL = String(dL).toLowerCase(); - dJ = String(dJ).toLowerCase(); - if (dL === dJ) { + if (dN === dL) { return true; } - if (dJ.slice(0, 1) === '.') { - if (dL === dJ.slice(1)) { + if (dL.slice(0, 1) === '.') { + if (dN === dL.slice(1)) { return true; } - dK = dL.length - dJ.length; - if (dK > 0 && dL.slice(dK) === dJ) { + dM = dN.length - dL.length; + if (dM > 0 && dN.slice(dM) === dL) { return true; } } return false; } - function cB(dJ) { - var dK = document.createElement('a'); - if (dJ.indexOf('//') !== 0 && dJ.indexOf('http') !== 0) { - if (dJ.indexOf('*') === 0) { - dJ = dJ.substr(1); + function cB(dL) { + var dM = document.createElement('a'); + if (dL.indexOf('//') !== 0 && dL.indexOf('http') !== 0) { + if (dL.indexOf('*') === 0) { + dL = dL.substr(1); } - if (dJ.indexOf('.') === 0) { - dJ = dJ.substr(1); + if (dL.indexOf('.') === 0) { + dL = dL.substr(1); } - dJ = 'http://' + dJ; + dL = 'http://' + dL; } - dK.href = x.toAbsoluteUrl(dJ); - if (dK.pathname) { - return dK.pathname; + dM.href = x.toAbsoluteUrl(dL); + if (dM.pathname) { + return dM.pathname; } return ''; } - function be(dK, dJ) { - if (!ao(dJ, '/')) { - dJ = '/' + dJ; + function be(dM, dL) { + if (!ao(dL, '/')) { + dL = '/' + dL; } - if (!ao(dK, '/')) { - dK = '/' + dK; + if (!ao(dM, '/')) { + dM = '/' + dM; } - var dL = dJ === '/' || dJ === '/*'; - if (dL) { + var dN = dL === '/' || dL === '/*'; + if (dN) { return true; } - if (dK === dJ) { + if (dM === dL) { return true; } - dJ = String(dJ).toLowerCase(); - dK = String(dK).toLowerCase(); - if (V(dJ, '*')) { - dJ = dJ.slice(0, -1); - dL = !dJ || dJ === '/'; - if (dL) { + dL = String(dL).toLowerCase(); + dM = String(dM).toLowerCase(); + if (V(dL, '*')) { + dL = dL.slice(0, -1); + dN = !dL || dL === '/'; + if (dN) { return true; } - if (dK === dJ) { + if (dM === dL) { return true; } - return dK.indexOf(dJ) === 0; + return dM.indexOf(dL) === 0; } - if (!V(dK, '/')) { - dK += '/'; + if (!V(dM, '/')) { + dM += '/'; } - if (!V(dJ, '/')) { - dJ += '/'; + if (!V(dL, '/')) { + dL += '/'; } - return dK.indexOf(dJ) === 0; + return dM.indexOf(dL) === 0; } - function aA(dN, dP) { - var dK, dJ, dL, dM, dO; - for (dK = 0; dK < aG.length; dK++) { - dM = P(aG[dK]); - dO = cB(aG[dK]); - if (c4(dN, dM) && be(dP, dO)) { + function aA(dP, dR) { + var dM, dL, dN, dO, dQ; + for (dM = 0; dM < aG.length; dM++) { + dO = P(aG[dM]); + dQ = cB(aG[dM]); + if (c5(dP, dO) && be(dR, dQ)) { return true; } } return false; } - function a6(dM) { - var dK, dJ, dL; - for (dK = 0; dK < aG.length; dK++) { - dJ = P(aG[dK].toLowerCase()); - if (dM === dJ) { + function a6(dO) { + var dM, dL, dN; + for (dM = 0; dM < aG.length; dM++) { + dL = P(aG[dM].toLowerCase()); + if (dO === dL) { return true; } - if (dJ.slice(0, 1) === '.') { - if (dM === dJ.slice(1)) { + if (dL.slice(0, 1) === '.') { + if (dO === dL.slice(1)) { return true; } - dL = dM.length - dJ.length; - if (dL > 0 && dM.slice(dL) === dJ) { + dN = dO.length - dL.length; + if (dN > 0 && dO.slice(dN) === dL) { return true; } } } return false; } - function cJ(dJ) { - var dK, dM, dO, dL, dN; - if (!dJ.length || !cS.length) { + function cJ(dL) { + var dM, dO, dQ, dN, dP; + if (!dL.length || !cS.length) { return false; } - dM = d(dJ); - dO = cB(dJ); - if (dM.indexOf('www.') === 0) { - dM = dM.substr(4); + dO = d(dL); + dQ = cB(dL); + if (dO.indexOf('www.') === 0) { + dO = dO.substr(4); } - for (dK = 0; dK < cS.length; dK++) { - dL = P(cS[dK]); - dN = cB(cS[dK]); - if (dL.indexOf('www.') === 0) { - dL = dL.substr(4); + for (dM = 0; dM < cS.length; dM++) { + dN = P(cS[dM]); + dP = cB(cS[dM]); + if (dN.indexOf('www.') === 0) { + dN = dN.substr(4); } - if (c4(dM, dL) && be(dO, dN)) { + if (c5(dO, dN) && be(dQ, dP)) { return true; } } @@ -1894,28 +1958,28 @@ if (typeof window.Matomo !== 'object') { X.close(); } } - function cF(dJ, dL) { - dJ = dJ.replace('send_image=0', 'send_image=1'); - var dK = new Image(1, 1); - dK.onload = function () { + function cF(dL, dN) { + dL = dL.replace('send_image=0', 'send_image=1'); + var dM = new Image(1, 1); + dM.onload = function () { I = 0; - if (typeof dL === 'function') { - dL({ request: dJ, trackerUrl: aM, success: true }); + if (typeof dN === 'function') { + dN({ request: dL, trackerUrl: aM, success: true }); } }; - dK.onerror = function () { - if (typeof dL === 'function') { - dL({ request: dJ, trackerUrl: aM, success: false }); + dM.onerror = function () { + if (typeof dN === 'function') { + dN({ request: dL, trackerUrl: aM, success: false }); } }; - dK.src = aM + (aM.indexOf('?') < 0 ? '?' : '&') + dJ; + dM.src = aM + (aM.indexOf('?') < 0 ? '?' : '&') + dL; cI(); } - function c1(dJ) { - if (dA === 'POST') { + function c2(dL) { + if (dC === 'POST') { return true; } - return dJ && (dJ.length > 2000 || dJ.indexOf('{"requests"') === 0); + return dL && (dL.length > 2000 || dL.indexOf('{"requests"') === 0); } function aT() { return ( @@ -1924,66 +1988,66 @@ if (typeof window.Matomo !== 'object') { 'function' === typeof Blob ); } - function bh(dN, dQ, dP) { - var dL = aT(); - if (!dL) { + function bh(dP, dS, dR) { + var dN = aT(); + if (!dN) { return false; } - var dM = { type: 'application/x-www-form-urlencoded; charset=UTF-8' }; - var dR = false; - var dK = aM; + var dO = { type: 'application/x-www-form-urlencoded; charset=UTF-8' }; + var dT = false; + var dM = aM; try { - var dJ = new Blob([dN], dM); - if (dP && !c1(dN)) { - dJ = new Blob([], dM); - dK = dK + (dK.indexOf('?') < 0 ? '?' : '&') + dN; + var dL = new Blob([dP], dO); + if (dR && !c2(dP)) { + dL = new Blob([], dO); + dM = dM + (dM.indexOf('?') < 0 ? '?' : '&') + dP; } - dR = g.sendBeacon(dK, dJ); - } catch (dO) { + dT = g.sendBeacon(dM, dL); + } catch (dQ) { return false; } - if (dR && typeof dQ === 'function') { - dQ({ - request: dN, + if (dT && typeof dS === 'function') { + dS({ + request: dP, trackerUrl: aM, success: true, isSendBeacon: true, }); } cI(); - return dR; + return dT; } - function du(dK, dL, dJ) { - if (!N(dJ) || null === dJ) { - dJ = true; + function dw(dM, dN, dL) { + if (!N(dL) || null === dL) { + dL = true; } - if (m && bh(dK, dL, dJ)) { + if (m && bh(dM, dN, dL)) { return; } setTimeout(function () { - if (m && bh(dK, dL, dJ)) { + if (m && bh(dM, dN, dL)) { return; } - var dO; + var dQ; try { - var dN = X.XMLHttpRequest + var dP = X.XMLHttpRequest ? new X.XMLHttpRequest() : X.ActiveXObject ? new ActiveXObject('Microsoft.XMLHTTP') : null; - dN.open('POST', aM, true); - dN.onreadystatechange = function () { + dP.open('POST', aM, true); + dP.onreadystatechange = function () { if ( this.readyState === 4 && !(this.status >= 200 && this.status < 300) ) { - var dP = m && bh(dK, dL, dJ); - if (!dP && dJ) { - cF(dK, dL); + var dR = m && bh(dM, dN, dL); + if (!dR && dL) { + cF(dM, dN); } else { - if (typeof dL === 'function') { - dL({ - request: dK, + if (typeof dN === 'function') { + dN({ + request: dM, trackerUrl: aM, success: false, xhr: this, @@ -1991,49 +2055,49 @@ if (typeof window.Matomo !== 'object') { } } } else { - if (this.readyState === 4 && typeof dL === 'function') { - dL({ request: dK, trackerUrl: aM, success: true, xhr: this }); + if (this.readyState === 4 && typeof dN === 'function') { + dN({ request: dM, trackerUrl: aM, success: true, xhr: this }); } } }; - dN.setRequestHeader('Content-Type', cR); - dN.withCredentials = true; - dN.send(dK); - } catch (dM) { - dO = m && bh(dK, dL, dJ); - if (!dO && dJ) { - cF(dK, dL); + dP.setRequestHeader('Content-Type', cR); + dP.withCredentials = true; + dP.send(dM); + } catch (dO) { + dQ = m && bh(dM, dN, dL); + if (!dQ && dL) { + cF(dM, dN); } else { - if (typeof dL === 'function') { - dL({ request: dK, trackerUrl: aM, success: false }); + if (typeof dN === 'function') { + dN({ request: dM, trackerUrl: aM, success: false }); } } } cI(); }, 50); } - function cv(dK) { - var dJ = new Date(); - var dL = dJ.getTime() + dK; - if (!s || dL > s) { - s = dL; + function cv(dM) { + var dL = new Date(); + var dN = dL.getTime() + dM; + if (!s || dN > s) { + s = dN; } } function bl() { bn = true; cT = new Date().getTime(); } - function dD() { - var dJ = new Date().getTime(); - return !cT || dJ - cT > bg; + function dF() { + var dL = new Date().getTime(); + return !cT || dL - cT > bg; } function aH() { - if (dD()) { + if (dF()) { b4(); } } function a5() { - if (K.visibilityState === 'hidden' && dD()) { + if (K.visibilityState === 'hidden' && dF()) { b4(); } else { if (K.visibilityState === 'visible') { @@ -2041,7 +2105,7 @@ if (typeof window.Matomo !== 'object') { } } } - function dH() { + function dJ() { if (aW || !bg) { return; } @@ -2052,31 +2116,31 @@ if (typeof window.Matomo !== 'object') { ag++; v.addPlugin('HeartBeat' + ag, { unload: function () { - if (aW && dD()) { + if (aW && dF()) { b4(); } }, }); } - function cZ(dN) { - var dK = new Date(); - var dJ = dK.getTime(); - dn = dJ; - if (cY && dJ < cY) { - var dL = cY - dJ; - setTimeout(dN, dL); - cv(dL + 50); - cY += 50; + function c0(dP) { + var dM = new Date(); + var dL = dM.getTime(); + dq = dL; + if (cZ && dL < cZ) { + var dN = cZ - dL; + setTimeout(dP, dN); + cv(dN + 50); + cZ += 50; return; } - if (cY === false) { - var dM = 800; - cY = dJ + dM; + if (cZ === false) { + var dO = 800; + cZ = dL + dO; } - dN(); + dP(); } function aX() { - if (aL(c9)) { + if (aL(da)) { bP = false; } else { if (aL(bo)) { @@ -2084,31 +2148,31 @@ if (typeof window.Matomo !== 'object') { } } } - function b2(dM) { - var dL, - dK = '', - dJ = ''; - for (dL in dx) { - if (Object.prototype.hasOwnProperty.call(dx, dL)) { - dJ += '&' + dL + '=' + dx[dL]; + function b2(dO) { + var dN, + dM = '', + dL = ''; + for (dN in dz) { + if (Object.prototype.hasOwnProperty.call(dz, dN)) { + dL += '&' + dN + '=' + dz[dN]; } } if (a3) { - dK = '&uadata=' + u(X.JSON.stringify(a3)); + dM = '&uadata=' + u(X.JSON.stringify(a3)); } - if (dM instanceof Array) { - for (dL = 0; dL < dM.length; dL++) { - dM[dL] += dK + dJ; + if (dO instanceof Array) { + for (dN = 0; dN < dO.length; dN++) { + dO[dN] += dM + dL; } } else { - dM += dK + dJ; + dO += dM + dL; } - return dM; + return dO; } function av() { return N(g.userAgentData) && D(g.userAgentData.getHighEntropyValues); } - function cG(dJ) { + function cG(dL) { if (by || ck) { return; } @@ -2127,111 +2191,111 @@ if (typeof window.Matomo !== 'object') { 'fullVersionList', ]) .then( - function (dL) { - var dK; - if (dL.fullVersionList) { - delete dL.brands; - delete dL.uaFullVersion; + function (dN) { + var dM; + if (dN.fullVersionList) { + delete dN.brands; + delete dN.uaFullVersion; } - a3 = dL; + a3 = dN; by = true; ck = false; - dJ(); + dL(); }, - function (dK) { + function (dM) { by = true; ck = false; - dJ(); + dL(); } ); } - function bS(dK, dJ, dL) { + function bS(dM, dL, dN) { aX(); if (!bP) { - c8.push([dK, dL]); + c9.push([dM, dN]); return; } - if (dl && !by && av()) { - co.push([dK, dL]); + if (dn && !by && av()) { + co.push([dM, dN]); return; } aE = true; - if (!dd && dK) { - if (cX && bP) { - dK += '&consent=1'; + if (!de && dM) { + if (cY && bP) { + dM += '&consent=1'; } - dK = b2(dK); - cZ(function () { - if (dk && bh(dK, dL, true)) { + dM = b2(dM); + c0(function () { + if (dl && bh(dM, dN, true)) { cv(100); return; } - if (c1(dK)) { - du(dK, dL); + if (c2(dM)) { + dw(dM, dN); } else { - cF(dK, dL); + cF(dM, dN); } - cv(dJ); + cv(dL); }); } if (!aW) { - dH(); + dJ(); } } - function cA(dJ) { - if (dd) { + function cA(dL) { + if (de) { return false; } - return dJ && dJ.length; + return dL && dL.length; } - function dt(dJ, dN) { - if (!dN || dN >= dJ.length) { - return [dJ]; + function dv(dL, dP) { + if (!dP || dP >= dL.length) { + return [dL]; } - var dK = 0; - var dL = dJ.length; - var dM = []; - for (dK; dK < dL; dK += dN) { - dM.push(dJ.slice(dK, dK + dN)); + var dM = 0; + var dN = dL.length; + var dO = []; + for (dM; dM < dN; dM += dP) { + dO.push(dL.slice(dM, dM + dP)); } - return dM; + return dO; } - function dF(dK, dJ) { - if (!cA(dK)) { + function dH(dM, dL) { + if (!cA(dM)) { return; } - if (dl && !by && av()) { - co.push([dK, null]); + if (dn && !by && av()) { + co.push([dM, null]); return; } if (!bP) { - c8.push([dK, null]); + c9.push([dM, null]); return; } aE = true; - cZ(function () { - var dN = dt(dK, 50); - var dL = 0, - dM; - for (dL; dL < dN.length; dL++) { - dM = + c0(function () { + var dP = dv(dM, 50); + var dN = 0, + dO; + for (dN; dN < dP.length; dN++) { + dO = '{"requests":["?' + - b2(dN[dL]).join('","?') + + b2(dP[dN]).join('","?') + '"],"send_image":0}'; - if (dk && bh(dM, null, false)) { + if (dl && bh(dO, null, false)) { cv(100); } else { - du(dM, null, false); + dw(dO, null, false); } } - cv(dJ); + cv(dL); }); } - function a2(dJ) { - return bv + dJ + '.' + cj + '.' + bB; + function a2(dL) { + return bv + dL + '.' + cj + '.' + bB; } - function cc(dL, dK, dJ) { - dE(dL, '', -129600000, dK, dJ); + function cc(dN, dM, dL) { + dG(dN, '', -129600000, dM, dL); } function ci() { if (bx) { @@ -2240,42 +2304,42 @@ if (typeof window.Matomo !== 'object') { if (!N(X.showModalDialog) && N(g.cookieEnabled)) { return g.cookieEnabled ? '1' : '0'; } - var dJ = bv + 'testcookie'; - dE(dJ, '1', undefined, bC, dm, b5, aR); - var dK = aL(dJ) === '1' ? '1' : '0'; - cc(dJ); - return dK; + var dL = bv + 'testcookie'; + dG(dL, '1', undefined, bC, dp, b5, aR); + var dM = aL(dL) === '1' ? '1' : '0'; + cc(dL); + return dM; } function bt() { - bB = cl((dm || dh) + (bC || '/')).slice(0, 4); + bB = cl((dp || di) + (bC || '/')).slice(0, 4); } function ay() { - var dK, dJ; - for (dK = 0; dK < co.length; dK++) { - dJ = typeof co[dK][0]; - if (dJ === 'string') { - bS(co[dK][0], bW, co[dK][1]); + var dM, dL; + for (dM = 0; dM < co.length; dM++) { + dL = typeof co[dM][0]; + if (dL === 'string') { + bS(co[dM][0], bW, co[dM][1]); } else { - if (dJ === 'object') { - dF(co[dK][0], bW); + if (dL === 'object') { + dH(co[dM][0], bW); } } } co = []; } - function c5() { - if (!dl) { + function c6() { + if (!dn) { return {}; } if (av()) { cG(ay); } - if (N(dx.res)) { - return dx; + if (N(dz.res)) { + return dz; } - var dK, - dM, - dN = { + var dM, + dO, + dP = { pdf: 'application/pdf', qt: 'video/quicktime', realp: 'audio/x-pn-realaudio-plugin', @@ -2286,10 +2350,10 @@ if (typeof window.Matomo !== 'object') { }; if (!new RegExp('MSIE').test(g.userAgent)) { if (g.mimeTypes && g.mimeTypes.length) { - for (dK in dN) { - if (Object.prototype.hasOwnProperty.call(dN, dK)) { - dM = g.mimeTypes[dN[dK]]; - dx[dK] = dM && dM.enabledPlugin ? '1' : '0'; + for (dM in dP) { + if (Object.prototype.hasOwnProperty.call(dP, dM)) { + dO = g.mimeTypes[dP[dM]]; + dz[dM] = dO && dO.enabledPlugin ? '1' : '0'; } } } @@ -2299,1005 +2363,1005 @@ if (typeof window.Matomo !== 'object') { N(g.javaEnabled) && g.javaEnabled() ) { - dx.java = '1'; + dz.java = '1'; } if (!N(X.showModalDialog) && N(g.cookieEnabled)) { - dx.cookie = g.cookieEnabled ? '1' : '0'; + dz.cookie = g.cookieEnabled ? '1' : '0'; } else { - dx.cookie = ci(); + dz.cookie = ci(); } } - var dL = parseInt(ac.width, 10); - var dJ = parseInt(ac.height, 10); - dx.res = parseInt(dL, 10) + 'x' + parseInt(dJ, 10); - return dx; + var dN = parseInt(ac.width, 10); + var dL = parseInt(ac.height, 10); + dz.res = parseInt(dN, 10) + 'x' + parseInt(dL, 10); + return dz; } function ca() { - var dK = a2('cvar'), - dJ = aL(dK); - if (dJ && dJ.length) { - dJ = X.JSON.parse(dJ); - if (aa(dJ)) { - return dJ; + var dM = a2('cvar'), + dL = aL(dM); + if (dL && dL.length) { + dL = X.JSON.parse(dL); + if (aa(dL)) { + return dL; } } return {}; } - function c2() { + function c3() { if (aZ === false) { aZ = ca(); } } - function de() { - var dJ = c5(); + function df() { + var dL = c6(); return cl( (g.userAgent || '') + (g.platform || '') + - X.JSON.stringify(dJ) + + X.JSON.stringify(dL) + new Date().getTime() + Math.random() ).slice(0, 16); } function aJ() { - var dJ = c5(); + var dL = c6(); return cl( - (g.userAgent || '') + (g.platform || '') + X.JSON.stringify(dJ) + (g.userAgent || '') + (g.platform || '') + X.JSON.stringify(dL) ).slice(0, 6); } function bq() { return Math.floor(new Date().getTime() / 1000); } function aS() { - var dK = bq(); - var dL = aJ(); - var dJ = String(dK) + dL; - return dJ; + var dM = bq(); + var dN = aJ(); + var dL = String(dM) + dN; + return dL; } - function ds(dL) { - dL = String(dL); - var dO = aJ(); - var dM = dO.length; - var dN = dL.substr(-1 * dM, dM); - var dK = parseInt(dL.substr(0, dL.length - dM), 10); - if (dK && dN && dN === dO) { - var dJ = bq(); + function du(dN) { + dN = String(dN); + var dQ = aJ(); + var dO = dQ.length; + var dP = dN.substr(-1 * dO, dO); + var dM = parseInt(dN.substr(0, dN.length - dO), 10); + if (dM && dP && dP === dQ) { + var dL = bq(); if (ba <= 0) { return true; } - if (dJ >= dK && dJ <= dK + ba) { + if (dL >= dM && dL <= dM + ba) { return true; } } return false; } - function dG(dJ) { - if (!da) { + function dI(dL) { + if (!db) { return ''; } - var dN = e(dJ, aD); - if (!dN) { + var dP = e(dL, aD); + if (!dP) { return ''; } - dN = String(dN); - var dL = new RegExp('^[a-zA-Z0-9]+$'); - if (dN.length === 32 && dL.test(dN)) { - var dK = dN.substr(16, 32); - if (ds(dK)) { - var dM = dN.substr(0, 16); - return dM; + dP = String(dP); + var dN = new RegExp('^[a-zA-Z0-9]+$'); + if (dP.length === 32 && dN.test(dP)) { + var dM = dP.substr(16, 32); + if (du(dM)) { + var dO = dP.substr(0, 16); + return dO; } } return ''; } - function db() { + function dc() { if (!b0) { - b0 = dG(bZ); + b0 = dI(bZ); } - var dL = new Date(), - dJ = Math.round(dL.getTime() / 1000), - dK = a2('id'), - dO = aL(dK), - dN, - dM; - if (dO) { - dN = dO.split('.'); - dN.unshift('0'); + var dN = new Date(), + dL = Math.round(dN.getTime() / 1000), + dM = a2('id'), + dQ = aL(dM), + dP, + dO; + if (dQ) { + dP = dQ.split('.'); + dP.unshift('0'); if (b0.length) { - dN[1] = b0; + dP[1] = b0; } - return dN; + return dP; } if (b0.length) { - dM = b0; + dO = b0; } else { if ('0' === ci()) { - dM = ''; + dO = ''; } else { - dM = de(); + dO = df(); } } - dN = ['1', dM, dJ]; - return dN; + dP = ['1', dO, dL]; + return dP; } function a9() { - var dM = db(), - dK = dM[0], - dL = dM[1], - dJ = dM[2]; - return { newVisitor: dK, uuid: dL, createTs: dJ }; + var dO = dc(), + dM = dO[0], + dN = dO[1], + dL = dO[2]; + return { newVisitor: dM, uuid: dN, createTs: dL }; } function aP() { - var dM = new Date(), - dK = dM.getTime(), - dN = a9().createTs; - var dJ = parseInt(dN, 10); - var dL = dJ * 1000 + c7 - dK; - return dL; + var dO = new Date(), + dM = dO.getTime(), + dP = a9().createTs; + var dL = parseInt(dP, 10); + var dN = dL * 1000 + c8 - dM; + return dN; } - function aV(dJ) { + function aV(dL) { if (!cj) { return; } - var dL = new Date(), - dK = Math.round(dL.getTime() / 1000); - if (!N(dJ)) { - dJ = a9(); + var dN = new Date(), + dM = Math.round(dN.getTime() / 1000); + if (!N(dL)) { + dL = a9(); } - var dM = dJ.uuid + '.' + dJ.createTs + '.'; - dE(a2('id'), dM, aP(), bC, dm, b5, aR); + var dO = dL.uuid + '.' + dL.createTs + '.'; + dG(a2('id'), dO, aP(), bC, dp, b5, aR); } function bX() { - var dJ = aL(a2('ref')); - if (dJ.length) { + var dL = aL(a2('ref')); + if (dL.length) { try { - dJ = X.JSON.parse(dJ); - if (aa(dJ)) { - return dJ; + dL = X.JSON.parse(dL); + if (aa(dL)) { + return dL; } - } catch (dK) {} + } catch (dM) {} } return ['', '', 0, '']; } - function bJ(dL) { - var dK = bv + 'testcookie_domain'; - var dJ = 'testvalue'; - dE(dK, dJ, 10000, null, dL, b5, aR); - if (aL(dK) === dJ) { - cc(dK, null, dL); + function bJ(dN) { + var dM = bv + 'testcookie_domain'; + var dL = 'testvalue'; + dG(dM, dL, 10000, null, dN, b5, aR); + if (aL(dM) === dL) { + cc(dM, null, dN); return true; } return false; } function aN() { - var dK = bx; + var dM = bx; bx = false; - var dJ, dL; - for (dJ = 0; dJ < bH.length; dJ++) { - dL = a2(bH[dJ]); - if (dL !== c9 && dL !== bo && 0 !== aL(dL)) { - cc(dL, bC, dm); + var dL, dN; + for (dL = 0; dL < bH.length; dL++) { + dN = a2(bH[dL]); + if (dN !== da && dN !== bo && 0 !== aL(dN)) { + cc(dN, bC, dp); } } - bx = dK; + bx = dM; } - function cg(dJ) { - cj = dJ; + function cg(dL) { + cj = dL; } - function dI(dN) { - if (!dN || !aa(dN)) { + function dK(dP) { + if (!dP || !aa(dP)) { return; } - var dM = []; - var dL; - for (dL in dN) { - if (Object.prototype.hasOwnProperty.call(dN, dL)) { - dM.push(dL); + var dO = []; + var dN; + for (dN in dP) { + if (Object.prototype.hasOwnProperty.call(dP, dN)) { + dO.push(dN); } } - var dO = {}; - dM.sort(); - var dJ = dM.length; - var dK; - for (dK = 0; dK < dJ; dK++) { - dO[dM[dK]] = dN[dM[dK]]; + var dQ = {}; + dO.sort(); + var dL = dO.length; + var dM; + for (dM = 0; dM < dL; dM++) { + dQ[dO[dM]] = dP[dO[dM]]; } - return dO; + return dQ; } function cs() { - dE(a2('ses'), '1', cE, bC, dm, b5, aR); + dG(a2('ses'), '1', cE, bC, dp, b5, aR); } function br() { - var dM = ''; - var dK = + var dO = ''; + var dM = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; - var dL = dK.length; - var dJ; - for (dJ = 0; dJ < 6; dJ++) { - dM += dK.charAt(Math.floor(Math.random() * dL)); + var dN = dM.length; + var dL; + for (dL = 0; dL < 6; dL++) { + dO += dM.charAt(Math.floor(Math.random() * dN)); } - return dM; + return dO; } - function aI(dK) { + function aI(dM) { if (cD !== '') { - dK += cD; + dM += cD; bs = true; - return dK; + return dM; } if (!h) { - return dK; + return dM; } - var dL = + var dN = typeof h.timing === 'object' && h.timing ? h.timing : undefined; - if (!dL) { - dL = + if (!dN) { + dN = typeof h.getEntriesByType === 'function' && h.getEntriesByType('navigation') ? h.getEntriesByType('navigation')[0] : undefined; } - if (!dL) { - return dK; + if (!dN) { + return dM; } - var dJ = ''; - if (dL.connectEnd && dL.fetchStart) { - if (dL.connectEnd < dL.fetchStart) { - return dK; + var dL = ''; + if (dN.connectEnd && dN.fetchStart) { + if (dN.connectEnd < dN.fetchStart) { + return dM; } - dJ += '&pf_net=' + Math.round(dL.connectEnd - dL.fetchStart); + dL += '&pf_net=' + Math.round(dN.connectEnd - dN.fetchStart); } - if (dL.responseStart && dL.requestStart) { - if (dL.responseStart < dL.requestStart) { - return dK; + if (dN.responseStart && dN.requestStart) { + if (dN.responseStart < dN.requestStart) { + return dM; } - dJ += '&pf_srv=' + Math.round(dL.responseStart - dL.requestStart); + dL += '&pf_srv=' + Math.round(dN.responseStart - dN.requestStart); } - if (dL.responseStart && dL.responseEnd) { - if (dL.responseEnd < dL.responseStart) { - return dK; + if (dN.responseStart && dN.responseEnd) { + if (dN.responseEnd < dN.responseStart) { + return dM; } - dJ += '&pf_tfr=' + Math.round(dL.responseEnd - dL.responseStart); + dL += '&pf_tfr=' + Math.round(dN.responseEnd - dN.responseStart); } - if (N(dL.domLoading)) { - if (dL.domInteractive && dL.domLoading) { - if (dL.domInteractive < dL.domLoading) { - return dK; + if (N(dN.domLoading)) { + if (dN.domInteractive && dN.domLoading) { + if (dN.domInteractive < dN.domLoading) { + return dM; } - dJ += '&pf_dm1=' + Math.round(dL.domInteractive - dL.domLoading); + dL += '&pf_dm1=' + Math.round(dN.domInteractive - dN.domLoading); } } else { - if (dL.domInteractive && dL.responseEnd) { - if (dL.domInteractive < dL.responseEnd) { - return dK; + if (dN.domInteractive && dN.responseEnd) { + if (dN.domInteractive < dN.responseEnd) { + return dM; } - dJ += '&pf_dm1=' + Math.round(dL.domInteractive - dL.responseEnd); + dL += '&pf_dm1=' + Math.round(dN.domInteractive - dN.responseEnd); } } - if (dL.domComplete && dL.domInteractive) { - if (dL.domComplete < dL.domInteractive) { - return dK; + if (dN.domComplete && dN.domInteractive) { + if (dN.domComplete < dN.domInteractive) { + return dM; } - dJ += '&pf_dm2=' + Math.round(dL.domComplete - dL.domInteractive); + dL += '&pf_dm2=' + Math.round(dN.domComplete - dN.domInteractive); } - if (dL.loadEventEnd && dL.loadEventStart) { - if (dL.loadEventEnd < dL.loadEventStart) { - return dK; + if (dN.loadEventEnd && dN.loadEventStart) { + if (dN.loadEventEnd < dN.loadEventStart) { + return dM; } - dJ += '&pf_onl=' + Math.round(dL.loadEventEnd - dL.loadEventStart); + dL += '&pf_onl=' + Math.round(dN.loadEventEnd - dN.loadEventStart); } - return dK + dJ; + return dM + dL; } - function cr(dJ) { + function cr(dL) { return ( - e(dJ, 'ignore_referrer') === '1' || e(dJ, 'ignore_referer') === '1' + e(dL, 'ignore_referrer') === '1' || e(dL, 'ignore_referer') === '1' ); } - function dz() { - var dT, - dM = new Date(), - dN = Math.round(dM.getTime() / 1000), - dY, - dL, - dO = 1024, - dV, - dP, - dK = a2('ses'), - dS = a2('ref'), - dR = aL(dK), - dJ = bX(), - dX = bf || bZ, - dU, - dQ, - dW = {}; - dU = dJ[0]; - dQ = dJ[1]; - dY = dJ[2]; - dL = dJ[3]; - if (!cr(dX) && !dR) { - if (!bI || !dU.length) { - for (dT in cH) { - if (Object.prototype.hasOwnProperty.call(cH, dT)) { - dU = e(dX, cH[dT]); - if (dU.length) { + function dB() { + var dV, + dO = new Date(), + dP = Math.round(dO.getTime() / 1000), + d0, + dN, + dQ = 1024, + dX, + dR, + dM = a2('ses'), + dU = a2('ref'), + dT = aL(dM), + dL = bX(), + dZ = bf || bZ, + dW, + dS, + dY = {}; + dW = dL[0]; + dS = dL[1]; + d0 = dL[2]; + dN = dL[3]; + if (!cr(dZ) && !dT) { + if ((!bI || !dW.length) && (dm || cY)) { + for (dV in cH) { + if (Object.prototype.hasOwnProperty.call(cH, dV)) { + dW = e(dZ, cH[dV]); + if (dW.length) { break; } } } - for (dT in bT) { - if (Object.prototype.hasOwnProperty.call(bT, dT)) { - dQ = e(dX, bT[dT]); - if (dQ.length) { + for (dV in bT) { + if (Object.prototype.hasOwnProperty.call(bT, dV)) { + dS = e(dZ, bT[dV]); + if (dS.length) { break; } } } } - dV = d(bA); - dP = dL.length ? d(dL) : ''; + dX = d(bA); + dR = dN.length ? d(dN) : ''; if ( - dV.length && - !a6(dV) && + dX.length && + !a6(dX) && !cJ(bA) && - (!bI || !dP.length || a6(dP) || cJ(dL)) + (!bI || !dR.length || a6(dR) || cJ(dN)) ) { - dL = bA; + dN = bA; } - if (dL.length || dU.length) { - dY = dN; - dJ = [dU, dQ, dY, cf(dL.slice(0, dO))]; - dE(dS, X.JSON.stringify(dJ), dv, bC, dm, b5, aR); + if (dN.length || dW.length) { + d0 = dP; + dL = [dW, dS, d0, cf(dN.slice(0, dQ))]; + dG(dU, X.JSON.stringify(dL), dx, bC, dp, b5, aR); } } - if (dU.length) { - dW._rcn = u(dU); + if (dW.length) { + dY._rcn = u(dW); } - if (dQ.length) { - dW._rck = u(dQ); + if (dS.length) { + dY._rck = u(dS); } - dW._refts = dY; - if (String(dL).length) { - dW._ref = u(cf(dL.slice(0, dO))); + dY._refts = d0; + if (String(dN).length) { + dY._ref = u(cf(dN.slice(0, dQ))); } - return dW; + return dY; } - function cL(dK, dW, dX) { - var dV, - dJ = new Date(), - dU = aZ, - dQ = a2('cvar'), - dZ = bf || bZ, - dL = cr(dZ); + function cL(dM, dY, dZ) { + var dX, + dL = new Date(), + dW = aZ, + dS = a2('cvar'), + d1 = bf || bZ, + dN = cr(d1); if (bx) { aN(); } - if (dd) { + if (de) { return ''; } - var dY = new RegExp('^file://', 'i'); - if (!cV && (X.location.protocol === 'file:' || dY.test(dZ))) { + var d0 = new RegExp('^file://', 'i'); + if (!cW && (X.location.protocol === 'file:' || d0.test(d1))) { return ''; } - c5(); - var dR = a9(); - var dO = K.characterSet || K.charset; - if (!dO || dO.toLowerCase() === 'utf-8') { - dO = null; + c6(); + var dT = a9(); + var dQ = K.characterSet || K.charset; + if (!dQ || dQ.toLowerCase() === 'utf-8') { + dQ = null; } - dK += + dM += '&idsite=' + cj + '&rec=1&r=' + String(Math.random()).slice(2, 8) + '&h=' + - dJ.getHours() + + dL.getHours() + '&m=' + - dJ.getMinutes() + + dL.getMinutes() + '&s=' + - dJ.getSeconds() + + dL.getSeconds() + '&url=' + - u(cf(dZ)) + - (bA.length && !cJ(bA) && !dL ? '&urlref=' + u(cf(bA)) : '') + + u(cf(d1)) + + (bA.length && !cJ(bA) && !dN ? '&urlref=' + u(cf(bA)) : '') + (ad(bL) ? '&uid=' + u(bL) : '') + '&_id=' + - dR.uuid + + dT.uuid + '&_idn=' + - dR.newVisitor + - (dO ? '&cs=' + u(dO) : '') + + dT.newVisitor + + (dQ ? '&cs=' + u(dQ) : '') + '&send_image=0'; - var dT = dz(); - for (dV in dT) { - if (Object.prototype.hasOwnProperty.call(dT, dV)) { - dK += '&' + dV + '=' + dT[dV]; + var dV = dB(); + for (dX in dV) { + if (Object.prototype.hasOwnProperty.call(dV, dX)) { + dM += '&' + dX + '=' + dV[dX]; } } - var d1 = []; - if (dW) { - for (dV in dW) { + var d3 = []; + if (dY) { + for (dX in dY) { if ( - Object.prototype.hasOwnProperty.call(dW, dV) && - /^dimension\d+$/.test(dV) + Object.prototype.hasOwnProperty.call(dY, dX) && + /^dimension\d+$/.test(dX) ) { - var dM = dV.replace('dimension', ''); - d1.push(parseInt(dM, 10)); - d1.push(String(dM)); - dK += '&' + dV + '=' + u(dW[dV]); - delete dW[dV]; + var dO = dX.replace('dimension', ''); + d3.push(parseInt(dO, 10)); + d3.push(String(dO)); + dM += '&' + dX + '=' + u(dY[dX]); + delete dY[dX]; } } } - if (dW && E(dW)) { - dW = null; + if (dY && E(dY)) { + dY = null; } - for (dV in cN) { - if (Object.prototype.hasOwnProperty.call(cN, dV)) { - dK += '&' + dV + '=' + u(cN[dV]); + for (dX in cN) { + if (Object.prototype.hasOwnProperty.call(cN, dX)) { + dM += '&' + dX + '=' + u(cN[dX]); } } - for (dV in bz) { - if (Object.prototype.hasOwnProperty.call(bz, dV)) { - var dP = -1 === Q(d1, dV); - if (dP) { - dK += '&dimension' + dV + '=' + u(bz[dV]); + for (dX in bz) { + if (Object.prototype.hasOwnProperty.call(bz, dX)) { + var dR = -1 === Q(d3, dX); + if (dR) { + dM += '&dimension' + dX + '=' + u(bz[dX]); } } } - if (dW) { - dK += '&data=' + u(X.JSON.stringify(dW)); + if (dY) { + dM += '&data=' + u(X.JSON.stringify(dY)); } else { if (aw) { - dK += '&data=' + u(X.JSON.stringify(aw)); + dM += '&data=' + u(X.JSON.stringify(aw)); } } - function dN(d2, d3) { - var d4 = X.JSON.stringify(d2); - if (d4.length > 2) { - return '&' + d3 + '=' + u(d4); + function dP(d4, d5) { + var d6 = X.JSON.stringify(d4); + if (d6.length > 2) { + return '&' + d5 + '=' + u(d6); } return ''; } - var d0 = dI(b9); - var dS = dI(cC); - dK += dN(d0, 'cvar'); - dK += dN(dS, 'e_cvar'); + var d2 = dK(b9); + var dU = dK(cC); + dM += dP(d2, 'cvar'); + dM += dP(dU, 'e_cvar'); if (aZ) { - dK += dN(aZ, '_cvar'); - for (dV in dU) { - if (Object.prototype.hasOwnProperty.call(dU, dV)) { - if (aZ[dV][0] === '' || aZ[dV][1] === '') { - delete aZ[dV]; + dM += dP(aZ, '_cvar'); + for (dX in dW) { + if (Object.prototype.hasOwnProperty.call(dW, dX)) { + if (aZ[dX][0] === '' || aZ[dX][1] === '') { + delete aZ[dX]; } } } if (b3) { - dE(dQ, X.JSON.stringify(aZ), cE, bC, dm, b5, aR); + dG(dS, X.JSON.stringify(aZ), cE, bC, dp, b5, aR); } } if (bd && bR && !bs) { - dK = aI(dK); + dM = aI(dM); bs = true; } if (aU) { - dK += '&pv_id=' + aU; + dM += '&pv_id=' + aU; } - aV(dR); + aV(dT); cs(); - dK += ah(dX, { tracker: bV, request: dK }); - if (dp.length) { - dK += '&' + dp; + dM += ah(dZ, { tracker: bV, request: dM }); + if (dr.length) { + dM += '&' + dr; } if (au()) { - dK += '&tracker_install_check=' + q; + dM += '&tracker_install_check=' + q; } if (D(cq)) { - dK = cq(dK); + dM = cq(dM); } - return dK; + return dM; } b4 = function bi() { - var dJ = new Date(); - dJ = dJ.getTime(); - if (!dn) { + var dL = new Date(); + dL = dL.getTime(); + if (!dq) { return false; } - if (dn + bg <= dJ) { + if (dq + bg <= dL) { bV.ping(); return true; } return false; }; - function bD(dM, dL, dQ, dN, dJ, dT) { - var dP = 'idgoal=0', - dK = new Date(), - dR = [], - dS, - dO = String(dM).length; - if (dO) { - dP += '&ec_id=' + u(dM); + function bD(dO, dN, dS, dP, dL, dV) { + var dR = 'idgoal=0', + dM = new Date(), + dT = [], + dU, + dQ = String(dO).length; + if (dQ) { + dR += '&ec_id=' + u(dO); } - dP += '&revenue=' + dL; - if (String(dQ).length) { - dP += '&ec_st=' + dQ; + dR += '&revenue=' + dN; + if (String(dS).length) { + dR += '&ec_st=' + dS; } - if (String(dN).length) { - dP += '&ec_tx=' + dN; + if (String(dP).length) { + dR += '&ec_tx=' + dP; } - if (String(dJ).length) { - dP += '&ec_sh=' + dJ; + if (String(dL).length) { + dR += '&ec_sh=' + dL; } - if (String(dT).length) { - dP += '&ec_dt=' + dT; + if (String(dV).length) { + dR += '&ec_dt=' + dV; } - if (dq) { - for (dS in dq) { - if (Object.prototype.hasOwnProperty.call(dq, dS)) { - if (!N(dq[dS][1])) { - dq[dS][1] = ''; + if (ds) { + for (dU in ds) { + if (Object.prototype.hasOwnProperty.call(ds, dU)) { + if (!N(ds[dU][1])) { + ds[dU][1] = ''; } - if (!N(dq[dS][2])) { - dq[dS][2] = ''; + if (!N(ds[dU][2])) { + ds[dU][2] = ''; } - if (!N(dq[dS][3]) || String(dq[dS][3]).length === 0) { - dq[dS][3] = 0; + if (!N(ds[dU][3]) || String(ds[dU][3]).length === 0) { + ds[dU][3] = 0; } - if (!N(dq[dS][4]) || String(dq[dS][4]).length === 0) { - dq[dS][4] = 1; + if (!N(ds[dU][4]) || String(ds[dU][4]).length === 0) { + ds[dU][4] = 1; } - dR.push(dq[dS]); + dT.push(ds[dU]); } } - dP += '&ec_items=' + u(X.JSON.stringify(dR)); + dR += '&ec_items=' + u(X.JSON.stringify(dT)); } - dP = cL(dP, aw, 'ecommerce'); - bS(dP, bW); - if (dO) { - dq = {}; + dR = cL(dR, aw, 'ecommerce'); + bS(dR, bW); + if (dQ) { + ds = {}; } } - function cb(dJ, dN, dM, dL, dK, dO) { - if (String(dJ).length && N(dN)) { - bD(dJ, dN, dM, dL, dK, dO); + function cb(dL, dP, dO, dN, dM, dQ) { + if (String(dL).length && N(dP)) { + bD(dL, dP, dO, dN, dM, dQ); } } - function bF(dJ) { - if (N(dJ)) { - bD('', dJ, '', '', '', ''); + function bF(dL) { + if (N(dL)) { + bD('', dL, '', '', '', ''); } } - function cd(dK, dM, dL) { + function cd(dM, dO, dN) { if (!bN) { aU = br(); } - var dJ = cL('action_name=' + u(aq(dK || bu)), dM, 'log'); + var dL = cL('action_name=' + u(aq(dM || bu)), dO, 'log'); if (bd && !bs) { - dJ = aI(dJ); + dL = aI(dL); } - bS(dJ, bW, dL); + bS(dL, bW, dN); } - function bb(dL, dK) { - var dM, - dJ = '(^| )(piwik[_-]' + dK + '|matomo[_-]' + dK; - if (dL) { - for (dM = 0; dM < dL.length; dM++) { - dJ += '|' + dL[dM]; + function bb(dN, dM) { + var dO, + dL = '(^| )(piwik[_-]' + dM + '|matomo[_-]' + dM; + if (dN) { + for (dO = 0; dO < dN.length; dO++) { + dL += '|' + dN[dO]; } } - dJ += ')( |$)'; - return new RegExp(dJ); + dL += ')( |$)'; + return new RegExp(dL); } - function a4(dJ) { - return aM && dJ && 0 === String(dJ).indexOf(aM); + function a4(dL) { + return aM && dL && 0 === String(dL).indexOf(aM); } - function cP(dN, dJ, dO, dK) { - if (a4(dJ)) { + function cP(dP, dL, dQ, dM) { + if (a4(dL)) { return 0; } - var dM = bb(bY, 'download'), - dL = bb(bj, 'link'), - dP = new RegExp('\\.(' + dw.join('|') + ')([?&#]|$)', 'i'); - if (dL.test(dN)) { + var dO = bb(bY, 'download'), + dN = bb(bj, 'link'), + dR = new RegExp('\\.(' + dy.join('|') + ')([?&#]|$)', 'i'); + if (dN.test(dP)) { return 'link'; } - if (dK || dM.test(dN) || dP.test(dJ)) { + if (dM || dO.test(dP) || dR.test(dL)) { return 'download'; } - if (dO) { + if (dQ) { return 0; } return 'link'; } - function aC(dK) { - var dJ; - dJ = dK.parentNode; - while (dJ !== null && N(dJ)) { - if (aj.isLinkElement(dK)) { + function aC(dM) { + var dL; + dL = dM.parentNode; + while (dL !== null && N(dL)) { + if (aj.isLinkElement(dM)) { break; } - dK = dJ; - dJ = dK.parentNode; + dM = dL; + dL = dM.parentNode; } - return dK; + return dM; } - function dC(dO) { - dO = aC(dO); - if (!aj.hasNodeAttribute(dO, 'href')) { + function dE(dQ) { + dQ = aC(dQ); + if (!aj.hasNodeAttribute(dQ, 'href')) { return; } - if (!N(dO.href)) { + if (!N(dQ.href)) { return; } - var dN = aj.getAttributeValueFromNode(dO, 'href'); - var dK = dO.pathname || cB(dO.href); - var dP = dO.hostname || d(dO.href); - var dQ = dP.toLowerCase(); - var dL = dO.href.replace(dP, dQ); - var dM = new RegExp( + var dP = aj.getAttributeValueFromNode(dQ, 'href'); + var dM = dQ.pathname || cB(dQ.href); + var dR = dQ.hostname || d(dQ.href); + var dS = dR.toLowerCase(); + var dN = dQ.href.replace(dR, dS); + var dO = new RegExp( '^(javascript|vbscript|jscript|mocha|livescript|ecmascript|mailto|tel):', 'i' ); - if (!dM.test(dL)) { - var dJ = cP( - dO.className, - dL, - aA(dQ, dK), - aj.hasNodeAttribute(dO, 'download') + if (!dO.test(dN)) { + var dL = cP( + dQ.className, + dN, + aA(dS, dM), + aj.hasNodeAttribute(dQ, 'download') ); - if (dJ) { - return { type: dJ, href: dL }; + if (dL) { + return { type: dL, href: dN }; } } } - function aY(dJ, dK, dL, dM) { - var dN = x.buildInteractionRequestParams(dJ, dK, dL, dM); - if (!dN) { + function aY(dL, dM, dN, dO) { + var dP = x.buildInteractionRequestParams(dL, dM, dN, dO); + if (!dP) { return; } - return cL(dN, null, 'contentInteraction'); + return cL(dP, null, 'contentInteraction'); } - function bm(dJ, dK) { - if (!dJ || !dK) { + function bm(dL, dM) { + if (!dL || !dM) { return false; } - var dL = x.findTargetNode(dJ); - if (x.shouldIgnoreInteraction(dL)) { + var dN = x.findTargetNode(dL); + if (x.shouldIgnoreInteraction(dN)) { return false; } - dL = x.findTargetNodeNoDefault(dJ); - if (dL && !Z(dL, dK)) { + dN = x.findTargetNodeNoDefault(dL); + if (dN && !Z(dN, dM)) { return false; } return true; } - function cO(dL, dK, dN) { - if (!dL) { + function cO(dN, dM, dP) { + if (!dN) { return; } - var dJ = x.findParentContentNode(dL); - if (!dJ) { + var dL = x.findParentContentNode(dN); + if (!dL) { return; } - if (!bm(dJ, dL)) { + if (!bm(dL, dN)) { return; } - var dM = x.buildContentBlock(dJ); - if (!dM) { + var dO = x.buildContentBlock(dL); + if (!dO) { return; } - if (!dM.target && dN) { - dM.target = dN; + if (!dO.target && dP) { + dO.target = dP; } return x.buildInteractionRequestParams( - dK, - dM.name, - dM.piece, - dM.target + dM, + dO.name, + dO.piece, + dO.target ); } - function a7(dK) { + function a7(dM) { if (!cp || !cp.length) { return false; } - var dJ, dL; - for (dJ = 0; dJ < cp.length; dJ++) { - dL = cp[dJ]; + var dL, dN; + for (dL = 0; dL < cp.length; dL++) { + dN = cp[dL]; if ( - dL && - dL.name === dK.name && - dL.piece === dK.piece && - dL.target === dK.target + dN && + dN.name === dM.name && + dN.piece === dM.piece && + dN.target === dM.target ) { return true; } } return false; } - function a8(dJ) { - return function (dN) { - if (!dJ) { + function a8(dL) { + return function (dP) { + if (!dL) { return; } - var dL = x.findParentContentNode(dJ); - var dK; - if (dN) { - dK = dN.target || dN.srcElement; + var dN = x.findParentContentNode(dL); + var dM; + if (dP) { + dM = dP.target || dP.srcElement; } - if (!dK) { - dK = dJ; + if (!dM) { + dM = dL; } - if (!bm(dL, dK)) { + if (!bm(dN, dM)) { return; } - if (!dL) { + if (!dN) { return false; } - var dO = x.findTargetNode(dL); - if (!dO || x.shouldIgnoreInteraction(dO)) { + var dQ = x.findTargetNode(dN); + if (!dQ || x.shouldIgnoreInteraction(dQ)) { return false; } - var dM = dC(dO); - if (dy && dM && dM.type) { - return dM.type; + var dO = dE(dQ); + if (dA && dO && dO.type) { + return dO.type; } - return bV.trackContentInteractionNode(dK, 'click'); + return bV.trackContentInteractionNode(dM, 'click'); }; } - function ce(dL) { - if (!dL || !dL.length) { + function ce(dN) { + if (!dN || !dN.length) { return; } - var dJ, dK; - for (dJ = 0; dJ < dL.length; dJ++) { - dK = x.findTargetNode(dL[dJ]); - if (dK && !dK.contentInteractionTrackingSetupDone) { - dK.contentInteractionTrackingSetupDone = true; - at(dK, 'click', a8(dK)); + var dL, dM; + for (dL = 0; dL < dN.length; dL++) { + dM = x.findTargetNode(dN[dL]); + if (dM && !dM.contentInteractionTrackingSetupDone) { + dM.contentInteractionTrackingSetupDone = true; + at(dM, 'click', a8(dM)); } } } - function bK(dL, dM) { - if (!dL || !dL.length) { + function bK(dN, dO) { + if (!dN || !dN.length) { return []; } - var dJ, dK; - for (dJ = 0; dJ < dL.length; dJ++) { - if (a7(dL[dJ])) { - dL.splice(dJ, 1); - dJ--; + var dL, dM; + for (dL = 0; dL < dN.length; dL++) { + if (a7(dN[dL])) { + dN.splice(dL, 1); + dL--; } else { - cp.push(dL[dJ]); + cp.push(dN[dL]); } } - if (!dL || !dL.length) { + if (!dN || !dN.length) { return []; } - ce(dM); - var dN = []; - for (dJ = 0; dJ < dL.length; dJ++) { - dK = cL( + ce(dO); + var dP = []; + for (dL = 0; dL < dN.length; dL++) { + dM = cL( x.buildImpressionRequestParams( - dL[dJ].name, - dL[dJ].piece, - dL[dJ].target + dN[dL].name, + dN[dL].piece, + dN[dL].target ), undefined, 'contentImpressions' ); - if (dK) { - dN.push(dK); + if (dM) { + dP.push(dM); } } - return dN; + return dP; } - function cW(dK) { - var dJ = x.collectContent(dK); - return bK(dJ, dK); + function cX(dM) { + var dL = x.collectContent(dM); + return bK(dL, dM); } - function bk(dK) { - if (!dK || !dK.length) { + function bk(dM) { + if (!dM || !dM.length) { return []; } - var dJ; - for (dJ = 0; dJ < dK.length; dJ++) { - if (!x.isNodeVisible(dK[dJ])) { - dK.splice(dJ, 1); - dJ--; + var dL; + for (dL = 0; dL < dM.length; dL++) { + if (!x.isNodeVisible(dM[dL])) { + dM.splice(dL, 1); + dL--; } } - if (!dK || !dK.length) { + if (!dM || !dM.length) { return []; } - return cW(dK); + return cX(dM); } - function aO(dL, dJ, dK) { - var dM = x.buildImpressionRequestParams(dL, dJ, dK); - return cL(dM, null, 'contentImpression'); + function aO(dN, dL, dM) { + var dO = x.buildImpressionRequestParams(dN, dL, dM); + return cL(dO, null, 'contentImpression'); } - function dB(dM, dK) { - if (!dM) { + function dD(dO, dM) { + if (!dO) { return; } - var dJ = x.findParentContentNode(dM); - var dL = x.buildContentBlock(dJ); - if (!dL) { + var dL = x.findParentContentNode(dO); + var dN = x.buildContentBlock(dL); + if (!dN) { return; } - if (!dK) { - dK = 'Unknown'; + if (!dM) { + dM = 'Unknown'; } - return aY(dK, dL.name, dL.piece, dL.target); + return aY(dM, dN.name, dN.piece, dN.target); } - function dc(dK, dM, dJ, dL) { + function dd(dM, dO, dL, dN) { return ( 'e_c=' + - u(dK) + - '&e_a=' + u(dM) + - (N(dJ) ? '&e_n=' + u(dJ) : '') + - (N(dL) ? '&e_v=' + u(dL) : '') + + '&e_a=' + + u(dO) + + (N(dL) ? '&e_n=' + u(dL) : '') + + (N(dN) ? '&e_v=' + u(dN) : '') + '&ca=1' ); } - function aB(dL, dN, dJ, dM, dP, dO) { - if (!ad(dL) || !ad(dN)) { + function aB(dN, dP, dL, dO, dR, dQ) { + if (!ad(dN) || !ad(dP)) { ap( 'Error while logging event: Parameters `category` and `action` must not be empty or filled with whitespaces' ); return false; } - var dK = cL(dc(dL, dN, dJ, dM), dP, 'event'); - bS(dK, bW, dO); + var dM = cL(dd(dN, dP, dL, dO), dR, 'event'); + bS(dM, bW, dQ); } - function cm(dJ, dM, dK, dN) { - var dL = cL( + function cm(dL, dO, dM, dP) { + var dN = cL( 'search=' + - u(dJ) + - (dM ? '&search_cat=' + u(dM) : '') + - (N(dK) ? '&search_count=' + dK : ''), - dN, + u(dL) + + (dO ? '&search_cat=' + u(dO) : '') + + (N(dM) ? '&search_count=' + dM : ''), + dP, 'sitesearch' ); - bS(dL, bW); + bS(dN, bW); } - function dg(dJ, dN, dM, dL) { - var dK = cL('idgoal=' + dJ + (dN ? '&revenue=' + dN : ''), dM, 'goal'); - bS(dK, bW, dL); + function dh(dL, dP, dO, dN) { + var dM = cL('idgoal=' + dL + (dP ? '&revenue=' + dP : ''), dO, 'goal'); + bS(dM, bW, dN); } - function dr(dM, dJ, dQ, dP, dL) { - var dO = dJ + '=' + u(cf(dM)); - var dK = cO(dL, 'click', dM); - if (dK) { - dO += '&' + dK; + function dt(dO, dL, dS, dR, dN) { + var dQ = dL + '=' + u(cf(dO)); + var dM = cO(dN, 'click', dO); + if (dM) { + dQ += '&' + dM; } - var dN = cL(dO, dQ, 'link'); - bS(dN, bW, dP); + var dP = cL(dQ, dS, 'link'); + bS(dP, bW, dR); } - function b7(dK, dJ) { - if (dK !== '') { - return dK + dJ.charAt(0).toUpperCase() + dJ.slice(1); + function b7(dM, dL) { + if (dM !== '') { + return dM + dL.charAt(0).toUpperCase() + dL.slice(1); } - return dJ; + return dL; } - function cw(dO) { - var dN, - dJ, - dM = ['', 'webkit', 'ms', 'moz'], - dL; + function cw(dQ) { + var dP, + dL, + dO = ['', 'webkit', 'ms', 'moz'], + dN; if (!bp) { - for (dJ = 0; dJ < dM.length; dJ++) { - dL = dM[dJ]; - if (Object.prototype.hasOwnProperty.call(K, b7(dL, 'hidden'))) { - if (K[b7(dL, 'visibilityState')] === 'prerender') { - dN = true; + for (dL = 0; dL < dO.length; dL++) { + dN = dO[dL]; + if (Object.prototype.hasOwnProperty.call(K, b7(dN, 'hidden'))) { + if (K[b7(dN, 'visibilityState')] === 'prerender') { + dP = true; } break; } } } - if (dN) { - at(K, dL + 'visibilitychange', function dK() { - K.removeEventListener(dL + 'visibilitychange', dK, false); - dO(); + if (dP) { + at(K, dN + 'visibilitychange', function dM() { + K.removeEventListener(dN + 'visibilitychange', dM, false); + dQ(); }); return; } - dO(); + dQ(); } function bE() { - var dK = bV.getVisitorId(); - var dJ = aS(); - return dK + dJ; + var dM = bV.getVisitorId(); + var dL = aS(); + return dM + dL; } - function cz(dJ) { - if (!dJ) { + function cz(dL) { + if (!dL) { return; } - if (!aj.hasNodeAttribute(dJ, 'href')) { + if (!aj.hasNodeAttribute(dL, 'href')) { return; } - var dK = aj.getAttributeValueFromNode(dJ, 'href'); - if (!dK || a4(dK)) { + var dM = aj.getAttributeValueFromNode(dL, 'href'); + if (!dM || a4(dM)) { return; } if (!bV.getVisitorId()) { return; } - dK = j(dK, aD); - var dL = bE(); - dK = J(dK, aD, dL); - aj.setAnyAttribute(dJ, 'href', dK); + dM = j(dM, aD); + var dN = bE(); + dM = J(dM, aD, dN); + aj.setAnyAttribute(dL, 'href', dM); } - function bw(dM) { - var dN = aj.getAttributeValueFromNode(dM, 'href'); - if (!dN) { + function bw(dO) { + var dP = aj.getAttributeValueFromNode(dO, 'href'); + if (!dP) { return false; } - dN = String(dN); - var dK = - dN.indexOf('//') === 0 || - dN.indexOf('http://') === 0 || - dN.indexOf('https://') === 0; - if (!dK) { + dP = String(dP); + var dM = + dP.indexOf('//') === 0 || + dP.indexOf('http://') === 0 || + dP.indexOf('https://') === 0; + if (!dM) { return false; } - var dJ = dM.pathname || cB(dM.href); - var dL = (dM.hostname || d(dM.href)).toLowerCase(); - if (aA(dL, dJ)) { - if (!c4(dh, P(dL))) { + var dL = dO.pathname || cB(dO.href); + var dN = (dO.hostname || d(dO.href)).toLowerCase(); + if (aA(dN, dL)) { + if (!c5(di, P(dN))) { return true; } return false; } return false; } - function c3(dJ) { - var dK = dC(dJ); - if (dK && dK.type) { - dK.href = p(dK.href); - dr(dK.href, dK.type, undefined, null, dJ); + function c4(dL) { + var dM = dE(dL); + if (dM && dM.type) { + dM.href = p(dM.href); + dt(dM.href, dM.type, undefined, null, dL); return; } - if (da) { - dJ = aC(dJ); - if (bw(dJ)) { - cz(dJ); + if (db) { + dL = aC(dL); + if (bw(dL)) { + cz(dL); } } } function cQ() { return K.all && !K.addEventListener; } - function di(dJ) { - var dL = dJ.which; - var dK = typeof dJ.button; - if (!dL && dK !== 'undefined') { + function dj(dL) { + var dN = dL.which; + var dM = typeof dL.button; + if (!dN && dM !== 'undefined') { if (cQ()) { - if (dJ.button & 1) { - dL = 1; + if (dL.button & 1) { + dN = 1; } else { - if (dJ.button & 2) { - dL = 3; + if (dL.button & 2) { + dN = 3; } else { - if (dJ.button & 4) { - dL = 2; + if (dL.button & 4) { + dN = 2; } } } } else { - if (dJ.button === 0 || dJ.button === '0') { - dL = 1; + if (dL.button === 0 || dL.button === '0') { + dN = 1; } else { - if (dJ.button & 1) { - dL = 2; + if (dL.button & 1) { + dN = 2; } else { - if (dJ.button & 2) { - dL = 3; + if (dL.button & 2) { + dN = 3; } } } } } - return dL; + return dN; } - function b6(dJ) { - switch (di(dJ)) { + function b6(dL) { + switch (dj(dL)) { case 1: return 'left'; case 2: @@ -3306,122 +3370,122 @@ if (typeof window.Matomo !== 'object') { return 'right'; } } - function bc(dJ) { - return dJ.target || dJ.srcElement; + function bc(dL) { + return dL.target || dL.srcElement; } - function dj(dJ) { - return dJ === 'A' || dJ === 'AREA'; + function dk(dL) { + return dL === 'A' || dL === 'AREA'; } - function aK(dJ) { - function dK(dM) { - var dN = bc(dM); - var dO = dN.nodeName; - var dL = bb(bM, 'ignore'); - while (!dj(dO) && dN && dN.parentNode) { - dN = dN.parentNode; - dO = dN.nodeName; + function aK(dL) { + function dM(dO) { + var dP = bc(dO); + var dQ = dP.nodeName; + var dN = bb(bM, 'ignore'); + while (!dk(dQ) && dP && dP.parentNode) { + dP = dP.parentNode; + dQ = dP.nodeName; } - if (dN && dj(dO) && !dL.test(dN.className)) { - return dN; + if (dP && dk(dQ) && !dN.test(dP.className)) { + return dP; } } - return function (dN) { - dN = dN || X.event; - var dO = dK(dN); - if (!dO) { + return function (dP) { + dP = dP || X.event; + var dQ = dM(dP); + if (!dQ) { return; } - var dM = b6(dN); - if (dN.type === 'click') { - var dL = false; - if (dJ && dM === 'middle') { - dL = true; + var dO = b6(dP); + if (dP.type === 'click') { + var dN = false; + if (dL && dO === 'middle') { + dN = true; } - if (dO && !dL) { - c3(dO); + if (dQ && !dN) { + c4(dQ); } } else { - if (dN.type === 'mousedown') { - if (dM === 'middle' && dO) { - a0 = dM; - bO = dO; + if (dP.type === 'mousedown') { + if (dO === 'middle' && dQ) { + a0 = dO; + bO = dQ; } else { a0 = bO = null; } } else { - if (dN.type === 'mouseup') { - if (dM === a0 && dO === bO) { - c3(dO); + if (dP.type === 'mouseup') { + if (dO === a0 && dQ === bO) { + c4(dQ); } a0 = bO = null; } else { - if (dN.type === 'contextmenu') { - c3(dO); + if (dP.type === 'contextmenu') { + c4(dQ); } } } } }; } - function az(dM, dL, dJ) { - var dK = typeof dL; - if (dK === 'undefined') { - dL = true; + function az(dO, dN, dL) { + var dM = typeof dN; + if (dM === 'undefined') { + dN = true; } - at(dM, 'click', aK(dL), dJ); - if (dL) { - at(dM, 'mouseup', aK(dL), dJ); - at(dM, 'mousedown', aK(dL), dJ); - at(dM, 'contextmenu', aK(dL), dJ); + at(dO, 'click', aK(dN), dL); + if (dN) { + at(dO, 'mouseup', aK(dN), dL); + at(dO, 'mousedown', aK(dN), dL); + at(dO, 'contextmenu', aK(dN), dL); } } - function a1(dK, dN, dO) { + function a1(dM, dP, dQ) { if (cu) { return true; } cu = true; - var dP = false; - var dM, dL; - function dJ() { - dP = true; + var dR = false; + var dO, dN; + function dL() { + dR = true; } n(function () { - function dQ(dS) { + function dS(dU) { setTimeout(function () { if (!cu) { return; } - dP = false; - dO.trackVisibleContentImpressions(); - dQ(dS); - }, dS); + dR = false; + dQ.trackVisibleContentImpressions(); + dS(dU); + }, dU); } - function dR(dS) { + function dT(dU) { setTimeout(function () { if (!cu) { return; } - if (dP) { - dP = false; - dO.trackVisibleContentImpressions(); + if (dR) { + dR = false; + dQ.trackVisibleContentImpressions(); } - dR(dS); - }, dS); + dT(dU); + }, dU); } - if (dK) { - dM = ['scroll', 'resize']; - for (dL = 0; dL < dM.length; dL++) { + if (dM) { + dO = ['scroll', 'resize']; + for (dN = 0; dN < dO.length; dN++) { if (K.addEventListener) { - K.addEventListener(dM[dL], dJ, false); + K.addEventListener(dO[dN], dL, false); } else { - X.attachEvent('on' + dM[dL], dJ); + X.attachEvent('on' + dO[dN], dL); } } - dR(100); + dT(100); } - if (dN && dN > 0) { - dN = parseInt(dN, 10); - dQ(dN); + if (dP && dP > 0) { + dP = parseInt(dP, 10); + dS(dP); } }); } @@ -3431,36 +3495,36 @@ if (typeof window.Matomo !== 'object') { timeout: null, interval: 2500, sendRequests: function () { - var dJ = this.requests; + var dL = this.requests; this.requests = []; - if (dJ.length === 1) { - bS(dJ[0], bW); + if (dL.length === 1) { + bS(dL[0], bW); } else { - dF(dJ, bW); + dH(dL, bW); } }, canQueue: function () { return !m && this.enabled; }, - pushMultiple: function (dK) { + pushMultiple: function (dM) { if (!this.canQueue()) { - dF(dK, bW); + dH(dM, bW); return; } - var dJ; - for (dJ = 0; dJ < dK.length; dJ++) { - this.push(dK[dJ]); + var dL; + for (dL = 0; dL < dM.length; dL++) { + this.push(dM[dL]); } }, - push: function (dJ) { - if (!dJ) { + push: function (dL) { + if (!dL) { return; } if (!this.canQueue()) { - bS(dJ, bW); + bS(dL, bW); return; } - bQ.requests.push(dJ); + bQ.requests.push(dL); if (this.timeout) { clearTimeout(this.timeout); this.timeout = null; @@ -3469,9 +3533,9 @@ if (typeof window.Matomo !== 'object') { bQ.timeout = null; bQ.sendRequests(); }, bQ.interval); - var dK = 'RequestQueue' + aF; - if (!Object.prototype.hasOwnProperty.call(b, dK)) { - b[dK] = { + var dM = 'RequestQueue' + aF; + if (!Object.prototype.hasOwnProperty.call(b, dM)) { + b[dM] = { unload: function () { if (bQ.timeout) { clearTimeout(bQ.timeout); @@ -3490,7 +3554,7 @@ if (typeof window.Matomo !== 'object') { if (!aL(a2('id'))) { aV(); } - return db(); + return dc(); }; this.getVisitorId = function () { return this.getVisitorInfo()[1]; @@ -3510,8 +3574,8 @@ if (typeof window.Matomo !== 'object') { this.getAttributionReferrerUrl = function () { return bX()[3]; }; - this.setTrackerUrl = function (dJ) { - aM = dJ; + this.setTrackerUrl = function (dL) { + aM = dL; }; this.getTrackerUrl = function () { return aM; @@ -3522,153 +3586,153 @@ if (typeof window.Matomo !== 'object') { this.getPiwikUrl = function () { return this.getMatomoUrl(); }; - this.addTracker = function (dL, dK) { - if (!N(dL) || null === dL) { - dL = this.getTrackerUrl(); + this.addTracker = function (dN, dM) { + if (!N(dN) || null === dN) { + dN = this.getTrackerUrl(); } - var dJ = new U(dL, dK); - M.push(dJ); + var dL = new U(dN, dM); + M.push(dL); v.trigger('TrackerAdded', [this]); - return dJ; + return dL; }; this.getSiteId = function () { return cj; }; - this.setSiteId = function (dJ) { - cg(dJ); + this.setSiteId = function (dL) { + cg(dL); }; this.resetUserId = function () { bL = ''; }; - this.setUserId = function (dJ) { - if (ad(dJ)) { - bL = dJ; + this.setUserId = function (dL) { + if (ad(dL)) { + bL = dL; } }; - this.setVisitorId = function (dK) { - var dJ = /[0-9A-Fa-f]{16}/g; - if (y(dK) && dJ.test(dK)) { - b0 = dK; + this.setVisitorId = function (dM) { + var dL = /[0-9A-Fa-f]{16}/g; + if (y(dM) && dL.test(dM)) { + b0 = dM; } else { - ap('Invalid visitorId set' + dK); + ap('Invalid visitorId set' + dM); } }; this.getUserId = function () { return bL; }; - this.setCustomData = function (dJ, dK) { - if (aa(dJ)) { - aw = dJ; + this.setCustomData = function (dL, dM) { + if (aa(dL)) { + aw = dL; } else { if (!aw) { aw = {}; } - aw[dJ] = dK; + aw[dL] = dM; } }; this.getCustomData = function () { return aw; }; - this.setCustomRequestProcessing = function (dJ) { - cq = dJ; + this.setCustomRequestProcessing = function (dL) { + cq = dL; }; - this.appendToTrackingUrl = function (dJ) { - dp = dJ; + this.appendToTrackingUrl = function (dL) { + dr = dL; }; - this.getRequest = function (dJ) { - return cL(dJ); + this.getRequest = function (dL) { + return cL(dL); }; - this.addPlugin = function (dJ, dK) { - b[dJ] = dK; + this.addPlugin = function (dL, dM) { + b[dL] = dM; }; - this.setCustomDimension = function (dJ, dK) { - dJ = parseInt(dJ, 10); - if (dJ > 0) { - if (!N(dK)) { - dK = ''; + this.setCustomDimension = function (dL, dM) { + dL = parseInt(dL, 10); + if (dL > 0) { + if (!N(dM)) { + dM = ''; } - if (!y(dK)) { - dK = String(dK); + if (!y(dM)) { + dM = String(dM); } - bz[dJ] = dK; + bz[dL] = dM; } }; - this.getCustomDimension = function (dJ) { - dJ = parseInt(dJ, 10); - if (dJ > 0 && Object.prototype.hasOwnProperty.call(bz, dJ)) { - return bz[dJ]; + this.getCustomDimension = function (dL) { + dL = parseInt(dL, 10); + if (dL > 0 && Object.prototype.hasOwnProperty.call(bz, dL)) { + return bz[dL]; } }; - this.deleteCustomDimension = function (dJ) { - dJ = parseInt(dJ, 10); - if (dJ > 0) { - delete bz[dJ]; + this.deleteCustomDimension = function (dL) { + dL = parseInt(dL, 10); + if (dL > 0) { + delete bz[dL]; } }; - this.setCustomVariable = function (dK, dJ, dN, dL) { - var dM; - if (!N(dL)) { - dL = 'visit'; + this.setCustomVariable = function (dM, dL, dP, dN) { + var dO; + if (!N(dN)) { + dN = 'visit'; } - if (!N(dJ)) { + if (!N(dL)) { return; } - if (!N(dN)) { - dN = ''; + if (!N(dP)) { + dP = ''; } - if (dK > 0) { - dJ = !y(dJ) ? String(dJ) : dJ; - dN = !y(dN) ? String(dN) : dN; - dM = [dJ.slice(0, bG), dN.slice(0, bG)]; - if (dL === 'visit' || dL === 2) { - c2(); - aZ[dK] = dM; + if (dM > 0) { + dL = !y(dL) ? String(dL) : dL; + dP = !y(dP) ? String(dP) : dP; + dO = [dL.slice(0, bG), dP.slice(0, bG)]; + if (dN === 'visit' || dN === 2) { + c3(); + aZ[dM] = dO; } else { - if (dL === 'page' || dL === 3) { - b9[dK] = dM; + if (dN === 'page' || dN === 3) { + b9[dM] = dO; } else { - if (dL === 'event') { - cC[dK] = dM; + if (dN === 'event') { + cC[dM] = dO; } } } } }; - this.getCustomVariable = function (dK, dL) { - var dJ; - if (!N(dL)) { - dL = 'visit'; + this.getCustomVariable = function (dM, dN) { + var dL; + if (!N(dN)) { + dN = 'visit'; } - if (dL === 'page' || dL === 3) { - dJ = b9[dK]; + if (dN === 'page' || dN === 3) { + dL = b9[dM]; } else { - if (dL === 'event') { - dJ = cC[dK]; + if (dN === 'event') { + dL = cC[dM]; } else { - if (dL === 'visit' || dL === 2) { - c2(); - dJ = aZ[dK]; + if (dN === 'visit' || dN === 2) { + c3(); + dL = aZ[dM]; } } } - if (!N(dJ) || (dJ && dJ[0] === '')) { + if (!N(dL) || (dL && dL[0] === '')) { return false; } - return dJ; + return dL; }; - this.deleteCustomVariable = function (dJ, dK) { - if (this.getCustomVariable(dJ, dK)) { - this.setCustomVariable(dJ, '', '', dK); + this.deleteCustomVariable = function (dL, dM) { + if (this.getCustomVariable(dL, dM)) { + this.setCustomVariable(dL, '', '', dM); } }; - this.deleteCustomVariables = function (dJ) { - if (dJ === 'page' || dJ === 3) { + this.deleteCustomVariables = function (dL) { + if (dL === 'page' || dL === 3) { b9 = {}; } else { - if (dJ === 'event') { + if (dL === 'event') { cC = {}; } else { - if (dJ === 'visit' || dJ === 2) { + if (dL === 'visit' || dL === 2) { aZ = {}; } } @@ -3677,113 +3741,113 @@ if (typeof window.Matomo !== 'object') { this.storeCustomVariablesInCookie = function () { b3 = true; }; - this.setLinkTrackingTimer = function (dJ) { - bW = dJ; + this.setLinkTrackingTimer = function (dL) { + bW = dL; }; this.getLinkTrackingTimer = function () { return bW; }; - this.setDownloadExtensions = function (dJ) { - if (y(dJ)) { - dJ = dJ.split('|'); + this.setDownloadExtensions = function (dL) { + if (y(dL)) { + dL = dL.split('|'); } - dw = dJ; + dy = dL; }; - this.addDownloadExtensions = function (dK) { - var dJ; - if (y(dK)) { - dK = dK.split('|'); + this.addDownloadExtensions = function (dM) { + var dL; + if (y(dM)) { + dM = dM.split('|'); } - for (dJ = 0; dJ < dK.length; dJ++) { - dw.push(dK[dJ]); + for (dL = 0; dL < dM.length; dL++) { + dy.push(dM[dL]); } }; - this.removeDownloadExtensions = function (dL) { - var dK, - dJ = []; - if (y(dL)) { - dL = dL.split('|'); + this.removeDownloadExtensions = function (dN) { + var dM, + dL = []; + if (y(dN)) { + dN = dN.split('|'); } - for (dK = 0; dK < dw.length; dK++) { - if (Q(dL, dw[dK]) === -1) { - dJ.push(dw[dK]); + for (dM = 0; dM < dy.length; dM++) { + if (Q(dN, dy[dM]) === -1) { + dL.push(dy[dM]); } } - dw = dJ; + dy = dL; }; - this.setDomains = function (dJ) { - aG = y(dJ) ? [dJ] : dJ; - var dN = false, - dL = 0, - dK; - for (dL; dL < aG.length; dL++) { - dK = String(aG[dL]); - if (c4(dh, P(dK))) { - dN = true; + this.setDomains = function (dL) { + aG = y(dL) ? [dL] : dL; + var dP = false, + dN = 0, + dM; + for (dN; dN < aG.length; dN++) { + dM = String(aG[dN]); + if (c5(di, P(dM))) { + dP = true; break; } - var dM = cB(dK); - if (dM && dM !== '/' && dM !== '/*') { - dN = true; + var dO = cB(dM); + if (dO && dO !== '/' && dO !== '/*') { + dP = true; break; } } - if (!dN) { - aG.push(dh); + if (!dP) { + aG.push(di); } }; - this.setExcludedReferrers = function (dJ) { - cS = y(dJ) ? [dJ] : dJ; + this.setExcludedReferrers = function (dL) { + cS = y(dL) ? [dL] : dL; }; this.enableCrossDomainLinking = function () { - da = true; + db = true; }; this.disableCrossDomainLinking = function () { - da = false; + db = false; }; this.isCrossDomainLinkingEnabled = function () { - return da; + return db; }; - this.setCrossDomainLinkingTimeout = function (dJ) { - ba = dJ; + this.setCrossDomainLinkingTimeout = function (dL) { + ba = dL; }; this.getCrossDomainLinkingUrlParameter = function () { return u(aD) + '=' + u(bE()); }; - this.setIgnoreClasses = function (dJ) { - bM = y(dJ) ? [dJ] : dJ; + this.setIgnoreClasses = function (dL) { + bM = y(dL) ? [dL] : dL; }; - this.setRequestMethod = function (dJ) { - if (dJ) { - dA = String(dJ).toUpperCase(); + this.setRequestMethod = function (dL) { + if (dL) { + dC = String(dL).toUpperCase(); } else { - dA = cx; + dC = cx; } - if (dA === 'GET') { + if (dC === 'GET') { this.disableAlwaysUseSendBeacon(); } }; - this.setRequestContentType = function (dJ) { - cR = dJ || aQ; + this.setRequestContentType = function (dL) { + cR = dL || aQ; }; - this.setGenerationTimeMs = function (dJ) { + this.setGenerationTimeMs = function (dL) { ap( 'setGenerationTimeMs is no longer supported since Matomo 4. The call will be ignored. The replacement is setPagePerformanceTiming.' ); }; - this.setPagePerformanceTiming = function (dN, dP, dO, dK, dQ, dL) { - var dM = { - pf_net: dN, - pf_srv: dP, - pf_tfr: dO, - pf_dm1: dK, - pf_dm2: dQ, - pf_onl: dL, + this.setPagePerformanceTiming = function (dP, dR, dQ, dM, dS, dN) { + var dO = { + pf_net: dP, + pf_srv: dR, + pf_tfr: dQ, + pf_dm1: dM, + pf_dm2: dS, + pf_onl: dN, }; try { - dM = R(dM, N); - dM = C(dM); - cD = l(dM); + dO = R(dO, N); + dO = C(dO); + cD = l(dO); if (cD === '') { ap( 'setPagePerformanceTiming() called without parameters. This function needs to be called with at least one performance parameter.' @@ -3792,137 +3856,137 @@ if (typeof window.Matomo !== 'object') { } bs = false; bR = true; - } catch (dJ) { - ap('setPagePerformanceTiming: ' + dJ.toString()); + } catch (dL) { + ap('setPagePerformanceTiming: ' + dL.toString()); } }; - this.setReferrerUrl = function (dJ) { - bA = dJ; + this.setReferrerUrl = function (dL) { + bA = dL; }; - this.setCustomUrl = function (dJ) { - bf = b8(bZ, dJ); + this.setCustomUrl = function (dL) { + bf = b8(bZ, dL); }; this.getCurrentUrl = function () { return bf || bZ; }; - this.setDocumentTitle = function (dJ) { - bu = dJ; + this.setDocumentTitle = function (dL) { + bu = dL; }; - this.setPageViewId = function (dJ) { - aU = dJ; + this.setPageViewId = function (dL) { + aU = dL; bN = true; }; this.getPageViewId = function () { return aU; }; - this.setAPIUrl = function (dJ) { - bU = dJ; + this.setAPIUrl = function (dL) { + bU = dL; }; - this.setDownloadClasses = function (dJ) { - bY = y(dJ) ? [dJ] : dJ; + this.setDownloadClasses = function (dL) { + bY = y(dL) ? [dL] : dL; }; - this.setLinkClasses = function (dJ) { - bj = y(dJ) ? [dJ] : dJ; + this.setLinkClasses = function (dL) { + bj = y(dL) ? [dL] : dL; }; - this.setCampaignNameKey = function (dJ) { - cH = y(dJ) ? [dJ] : dJ; + this.setCampaignNameKey = function (dL) { + cH = y(dL) ? [dL] : dL; }; - this.setCampaignKeywordKey = function (dJ) { - bT = y(dJ) ? [dJ] : dJ; + this.setCampaignKeywordKey = function (dL) { + bT = y(dL) ? [dL] : dL; }; - this.discardHashTag = function (dJ) { - b1 = dJ; + this.discardHashTag = function (dL) { + b1 = dL; }; - this.setCookieNamePrefix = function (dJ) { - bv = dJ; + this.setCookieNamePrefix = function (dL) { + bv = dL; if (aZ) { aZ = ca(); } }; - this.setCookieDomain = function (dJ) { - var dK = P(dJ); - if (!bx && !bJ(dK)) { - ap("Can't write cookie on domain " + dJ); + this.setCookieDomain = function (dL) { + var dM = P(dL); + if (!bx && !bJ(dM)) { + ap("Can't write cookie on domain " + dL); } else { - dm = dK; + dp = dM; bt(); } }; - this.setExcludedQueryParams = function (dJ) { - cy = y(dJ) ? [dJ] : dJ; + this.setExcludedQueryParams = function (dL) { + cy = y(dL) ? [dL] : dL; }; this.getCookieDomain = function () { - return dm; + return dp; }; this.hasCookies = function () { return '1' === ci(); }; - this.setSessionCookie = function (dL, dK, dJ) { - if (!dL) { + this.setSessionCookie = function (dN, dM, dL) { + if (!dN) { throw new Error('Missing cookie name'); } - if (!N(dJ)) { - dJ = cE; + if (!N(dL)) { + dL = cE; } - bH.push(dL); - dE(a2(dL), dK, dJ, bC, dm, b5, aR); + bH.push(dN); + dG(a2(dN), dM, dL, bC, dp, b5, aR); }; - this.getCookie = function (dK) { - var dJ = aL(a2(dK)); - if (dJ === 0) { + this.getCookie = function (dM) { + var dL = aL(a2(dM)); + if (dL === 0) { return null; } - return dJ; + return dL; }; - this.setCookiePath = function (dJ) { - bC = dJ; + this.setCookiePath = function (dL) { + bC = dL; bt(); }; this.getCookiePath = function () { return bC; }; - this.setVisitorCookieTimeout = function (dJ) { - c7 = dJ * 1000; + this.setVisitorCookieTimeout = function (dL) { + c8 = dL * 1000; }; - this.setSessionCookieTimeout = function (dJ) { - cE = dJ * 1000; + this.setSessionCookieTimeout = function (dL) { + cE = dL * 1000; }; this.getSessionCookieTimeout = function () { return cE; }; - this.setReferralCookieTimeout = function (dJ) { - dv = dJ * 1000; + this.setReferralCookieTimeout = function (dL) { + dx = dL * 1000; }; - this.setConversionAttributionFirstReferrer = function (dJ) { - bI = dJ; + this.setConversionAttributionFirstReferrer = function (dL) { + bI = dL; }; - this.setSecureCookie = function (dJ) { - if (dJ && location.protocol !== 'https:') { + this.setSecureCookie = function (dL) { + if (dL && location.protocol !== 'https:') { ap('Error in setSecureCookie: You cannot use `Secure` on http.'); return; } - b5 = dJ; + b5 = dL; }; - this.setCookieSameSite = function (dJ) { - dJ = String(dJ); - dJ = dJ.charAt(0).toUpperCase() + dJ.toLowerCase().slice(1); - if (dJ !== 'None' && dJ !== 'Lax' && dJ !== 'Strict') { + this.setCookieSameSite = function (dL) { + dL = String(dL); + dL = dL.charAt(0).toUpperCase() + dL.toLowerCase().slice(1); + if (dL !== 'None' && dL !== 'Lax' && dL !== 'Strict') { ap( 'Ignored value for sameSite. Please use either Lax, None, or Strict.' ); return; } - if (dJ === 'None') { + if (dL === 'None') { if (location.protocol === 'https:') { this.setSecureCookie(true); } else { ap( 'sameSite=None cannot be used on http, reverted to sameSite=Lax.' ); - dJ = 'Lax'; + dL = 'Lax'; } } - aR = dJ; + aR = dL; }; this.disableCookies = function () { bx = true; @@ -3934,15 +3998,15 @@ if (typeof window.Matomo !== 'object') { return !bx; }; this.setCookieConsentGiven = function () { - if (bx && !dd) { + if (bx && !de) { bx = false; - if (!dl) { + if (!dn) { this.enableBrowserFeatureDetection(); } if (cj && aE) { aV(); - var dJ = cL('ping=1', null, 'ping'); - bS(dJ, bW); + var dL = cL('ping=1', null, 'ping'); + bS(dL, bW); } } }; @@ -3954,73 +4018,76 @@ if (typeof window.Matomo !== 'object') { return true; }; this.getRememberedCookieConsent = function () { - return aL(c0); + return aL(c1); }; this.forgetCookieConsentGiven = function () { - cc(c0, bC, dm); + cc(c1, bC, dp); this.disableCookies(); }; - this.rememberCookieConsentGiven = function (dK) { - if (dK) { - dK = dK * 60 * 60 * 1000; + this.rememberCookieConsentGiven = function (dM) { + if (dM) { + dM = dM * 60 * 60 * 1000; } else { - dK = 30 * 365 * 24 * 60 * 60 * 1000; + dM = 30 * 365 * 24 * 60 * 60 * 1000; } this.setCookieConsentGiven(); - var dJ = new Date().getTime(); - dE(c0, dJ, dK, bC, dm, b5, aR); + var dL = new Date().getTime(); + dG(c1, dL, dM, bC, dp, b5, aR); }; this.deleteCookies = function () { aN(); }; - this.setDoNotTrack = function (dK) { - var dJ = g.doNotTrack || g.msDoNotTrack; - dd = dK && (dJ === 'yes' || dJ === '1'); - if (dd) { + this.setDoNotTrack = function (dM) { + var dL = g.doNotTrack || g.msDoNotTrack; + de = dM && (dL === 'yes' || dL === '1'); + if (de) { this.disableCookies(); } }; + this.disableCampaignParameters = function () { + dm = false; + }; this.alwaysUseSendBeacon = function () { - dk = true; + dl = true; }; this.disableAlwaysUseSendBeacon = function () { - dk = false; + dl = false; }; - this.addListener = function (dK, dJ) { - az(dK, dJ, false); + this.addListener = function (dM, dL) { + az(dM, dL, false); }; - this.enableLinkTracking = function (dK) { - if (dy) { + this.enableLinkTracking = function (dM) { + if (dA) { return; } - dy = true; - var dJ = this; + dA = true; + var dL = this; r(function () { ax = true; - var dL = K.body; - az(dL, dK, true); + var dN = K.body; + az(dN, dM, true); }); }; this.enableJSErrorTracking = function () { - if (df) { + if (dg) { return; } - df = true; - var dJ = X.onerror; - X.onerror = function (dO, dM, dL, dN, dK) { + dg = true; + var dL = X.onerror; + X.onerror = function (dQ, dO, dN, dP, dM) { cw(function () { - var dP = 'JavaScript Errors'; - var dQ = dM + ':' + dL; - if (dN) { - dQ += ':' + dN; + var dR = 'JavaScript Errors'; + var dS = dO + ':' + dN; + if (dP) { + dS += ':' + dP; } - if (Q(cM, dP + dQ + dO) === -1) { - cM.push(dP + dQ + dO); - aB(dP, dQ, dO); + if (Q(cM, dR + dS + dQ) === -1) { + cM.push(dR + dS + dQ); + aB(dR, dS, dQ); } }); - if (dJ) { - return dJ(dO, dM, dL, dN, dK); + if (dL) { + return dL(dQ, dO, dN, dP, dM); } return false; }; @@ -4028,11 +4095,11 @@ if (typeof window.Matomo !== 'object') { this.disablePerformanceTracking = function () { bd = false; }; - this.enableHeartBeatTimer = function (dJ) { - dJ = Math.max(dJ || 15, 5); - bg = dJ * 1000; - if (dn !== null) { - dH(); + this.enableHeartBeatTimer = function (dL) { + dL = Math.max(dL || 15, 5); + bg = dL * 1000; + if (dq !== null) { + dJ(); } }; this.disableHeartBeatTimer = function () { @@ -4057,30 +4124,30 @@ if (typeof window.Matomo !== 'object') { X.top.location = X.location; } }; - this.redirectFile = function (dJ) { + this.redirectFile = function (dL) { if (X.location.protocol === 'file:') { - X.location = dJ; + X.location = dL; } }; - this.setCountPreRendered = function (dJ) { - bp = dJ; + this.setCountPreRendered = function (dL) { + bp = dL; }; - this.trackGoal = function (dJ, dM, dL, dK) { + this.trackGoal = function (dL, dO, dN, dM) { cw(function () { - dg(dJ, dM, dL, dK); + dh(dL, dO, dN, dM); }); }; - this.trackLink = function (dK, dJ, dM, dL) { + this.trackLink = function (dM, dL, dO, dN) { cw(function () { - dr(dK, dJ, dM, dL); + dt(dM, dL, dO, dN); }); }; this.getNumTrackedPageViews = function () { return cK; }; - this.trackPageView = function (dJ, dL, dK) { + this.trackPageView = function (dL, dN, dM) { cp = []; - c8 = []; + c9 = []; cM = []; if (S(cj)) { cw(function () { @@ -4089,20 +4156,20 @@ if (typeof window.Matomo !== 'object') { } else { cw(function () { cK++; - cd(dJ, dL, dK); + cd(dL, dN, dM); }); } }; this.disableBrowserFeatureDetection = function () { - dl = false; - dx = {}; + dn = false; + dz = {}; if (av()) { ay(); } }; this.enableBrowserFeatureDetection = function () { - dl = true; - c5(); + dn = true; + c6(); }; this.trackAllContentImpressions = function () { if (S(cj)) { @@ -4110,176 +4177,176 @@ if (typeof window.Matomo !== 'object') { } cw(function () { r(function () { - var dJ = x.findContentNodes(); - var dK = cW(dJ); - bQ.pushMultiple(dK); + var dL = x.findContentNodes(); + var dM = cX(dL); + bQ.pushMultiple(dM); }); }); }; - this.trackVisibleContentImpressions = function (dJ, dK) { + this.trackVisibleContentImpressions = function (dL, dM) { if (S(cj)) { return; } - if (!N(dJ)) { - dJ = true; + if (!N(dL)) { + dL = true; } - if (!N(dK)) { - dK = 750; + if (!N(dM)) { + dM = 750; } - a1(dJ, dK, this); + a1(dL, dM, this); cw(function () { n(function () { - var dL = x.findContentNodes(); - var dM = bk(dL); - bQ.pushMultiple(dM); + var dN = x.findContentNodes(); + var dO = bk(dN); + bQ.pushMultiple(dO); }); }); }; - this.trackContentImpression = function (dL, dJ, dK) { + this.trackContentImpression = function (dN, dL, dM) { if (S(cj)) { return; } + dN = a(dN); dL = a(dL); - dJ = a(dJ); - dK = a(dK); - if (!dL) { + dM = a(dM); + if (!dN) { return; } - dJ = dJ || 'Unknown'; + dL = dL || 'Unknown'; cw(function () { - var dM = aO(dL, dJ, dK); - bQ.push(dM); + var dO = aO(dN, dL, dM); + bQ.push(dO); }); }; - this.trackContentImpressionsWithinNode = function (dJ) { - if (S(cj) || !dJ) { + this.trackContentImpressionsWithinNode = function (dL) { + if (S(cj) || !dL) { return; } cw(function () { if (cu) { n(function () { - var dK = x.findContentNodesWithinNode(dJ); - var dL = bk(dK); - bQ.pushMultiple(dL); + var dM = x.findContentNodesWithinNode(dL); + var dN = bk(dM); + bQ.pushMultiple(dN); }); } else { r(function () { - var dK = x.findContentNodesWithinNode(dJ); - var dL = cW(dK); - bQ.pushMultiple(dL); + var dM = x.findContentNodesWithinNode(dL); + var dN = cX(dM); + bQ.pushMultiple(dN); }); } }); }; - this.trackContentInteraction = function (dL, dM, dJ, dK) { + this.trackContentInteraction = function (dN, dO, dL, dM) { if (S(cj)) { return; } + dN = a(dN); + dO = a(dO); dL = a(dL); dM = a(dM); - dJ = a(dJ); - dK = a(dK); - if (!dL || !dM) { + if (!dN || !dO) { return; } - dJ = dJ || 'Unknown'; + dL = dL || 'Unknown'; cw(function () { - var dN = aY(dL, dM, dJ, dK); - if (dN) { - bQ.push(dN); + var dP = aY(dN, dO, dL, dM); + if (dP) { + bQ.push(dP); } }); }; - this.trackContentInteractionNode = function (dL, dK) { - if (S(cj) || !dL) { + this.trackContentInteractionNode = function (dN, dM) { + if (S(cj) || !dN) { return; } - var dJ = null; + var dL = null; cw(function () { - dJ = dB(dL, dK); - if (dJ) { - bQ.push(dJ); + dL = dD(dN, dM); + if (dL) { + bQ.push(dL); } }); - return dJ; + return dL; }; this.logAllContentBlocksOnPage = function () { - var dL = x.findContentNodes(); - var dJ = x.collectContent(dL); - var dK = typeof console; - if (dK !== 'undefined' && console && console.log) { - console.log(dJ); + var dN = x.findContentNodes(); + var dL = x.collectContent(dN); + var dM = typeof console; + if (dM !== 'undefined' && console && console.log) { + console.log(dL); } }; - this.trackEvent = function (dK, dM, dJ, dL, dO, dN) { + this.trackEvent = function (dM, dO, dL, dN, dQ, dP) { cw(function () { - aB(dK, dM, dJ, dL, dO, dN); + aB(dM, dO, dL, dN, dQ, dP); }); }; - this.trackSiteSearch = function (dJ, dL, dK, dM) { + this.trackSiteSearch = function (dL, dN, dM, dO) { cp = []; cw(function () { - cm(dJ, dL, dK, dM); + cm(dL, dN, dM, dO); }); }; - this.setEcommerceView = function (dN, dJ, dL, dK) { + this.setEcommerceView = function (dP, dL, dN, dM) { cN = {}; - if (ad(dL)) { - dL = String(dL); + if (ad(dN)) { + dN = String(dN); } - if (!N(dL) || dL === null || dL === false || !dL.length) { - dL = ''; + if (!N(dN) || dN === null || dN === false || !dN.length) { + dN = ''; } else { - if (dL instanceof Array) { - dL = X.JSON.stringify(dL); + if (dN instanceof Array) { + dN = X.JSON.stringify(dN); } } - var dM = '_pkc'; - cN[dM] = dL; - if (N(dK) && dK !== null && dK !== false && String(dK).length) { - dM = '_pkp'; - cN[dM] = dK; + var dO = '_pkc'; + cN[dO] = dN; + if (N(dM) && dM !== null && dM !== false && String(dM).length) { + dO = '_pkp'; + cN[dO] = dM; } - if (!ad(dN) && !ad(dJ)) { + if (!ad(dP) && !ad(dL)) { return; } - if (ad(dN)) { - dM = '_pks'; - cN[dM] = dN; + if (ad(dP)) { + dO = '_pks'; + cN[dO] = dP; } - if (!ad(dJ)) { - dJ = ''; + if (!ad(dL)) { + dL = ''; } - dM = '_pkn'; - cN[dM] = dJ; + dO = '_pkn'; + cN[dO] = dL; }; this.getEcommerceItems = function () { - return JSON.parse(JSON.stringify(dq)); + return JSON.parse(JSON.stringify(ds)); }; - this.addEcommerceItem = function (dN, dJ, dL, dK, dM) { - if (ad(dN)) { - dq[dN] = [String(dN), dJ, dL, dK, dM]; + this.addEcommerceItem = function (dP, dL, dN, dM, dO) { + if (ad(dP)) { + ds[dP] = [String(dP), dL, dN, dM, dO]; } }; - this.removeEcommerceItem = function (dJ) { - if (ad(dJ)) { - dJ = String(dJ); - delete dq[dJ]; + this.removeEcommerceItem = function (dL) { + if (ad(dL)) { + dL = String(dL); + delete ds[dL]; } }; this.clearEcommerceCart = function () { - dq = {}; + ds = {}; }; - this.trackEcommerceOrder = function (dJ, dN, dM, dL, dK, dO) { - cb(dJ, dN, dM, dL, dK, dO); + this.trackEcommerceOrder = function (dL, dP, dO, dN, dM, dQ) { + cb(dL, dP, dO, dN, dM, dQ); }; - this.trackEcommerceCartUpdate = function (dJ) { - bF(dJ); + this.trackEcommerceCartUpdate = function (dL) { + bF(dL); }; - this.trackRequest = function (dK, dM, dL, dJ) { + this.trackRequest = function (dM, dO, dN, dL) { cw(function () { - var dN = cL(dK, dM, dJ); - bS(dN, bW, dL); + var dP = cL(dM, dO, dL); + bS(dP, bW, dN); }); }; this.ping = function () { @@ -4288,39 +4355,39 @@ if (typeof window.Matomo !== 'object') { this.disableQueueRequest = function () { bQ.enabled = false; }; - this.setRequestQueueInterval = function (dJ) { - if (dJ < 1000) { + this.setRequestQueueInterval = function (dL) { + if (dL < 1000) { throw new Error('Request queue interval needs to be at least 1000ms'); } - bQ.interval = dJ; + bQ.interval = dL; }; - this.queueRequest = function (dK, dJ) { + this.queueRequest = function (dM, dL) { cw(function () { - var dL = dJ ? dK : cL(dK); - bQ.push(dL); + var dN = dL ? dM : cL(dM); + bQ.push(dN); }); }; this.isConsentRequired = function () { - return cX; + return cY; }; this.getRememberedConsent = function () { - var dJ = aL(bo); - if (aL(c9)) { - if (dJ) { - cc(bo, bC, dm); + var dL = aL(bo); + if (aL(da)) { + if (dL) { + cc(bo, bC, dp); } return null; } - if (!dJ || dJ === 0) { + if (!dL || dL === 0) { return null; } - return dJ; + return dL; }; this.hasRememberedConsent = function () { return !!this.getRememberedConsent(); }; this.requireConsent = function () { - cX = true; + cY = true; bP = this.hasRememberedConsent(); if (!bP) { bx = true; @@ -4334,47 +4401,47 @@ if (typeof window.Matomo !== 'object') { }, }; }; - this.setConsentGiven = function (dK) { + this.setConsentGiven = function (dM) { bP = true; - if (!dl) { + if (!dn) { this.enableBrowserFeatureDetection(); } - cc(c9, bC, dm); - var dL, dJ; - for (dL = 0; dL < c8.length; dL++) { - dJ = typeof c8[dL][0]; - if (dJ === 'string') { - bS(c8[dL][0], bW, c8[dL][1]); + cc(da, bC, dp); + var dN, dL; + for (dN = 0; dN < c9.length; dN++) { + dL = typeof c9[dN][0]; + if (dL === 'string') { + bS(c9[dN][0], bW, c9[dN][1]); } else { - if (dJ === 'object') { - dF(c8[dL][0], bW); + if (dL === 'object') { + dH(c9[dN][0], bW); } } } - c8 = []; - if (!N(dK) || dK) { + c9 = []; + if (!N(dM) || dM) { this.setCookieConsentGiven(); } }; - this.rememberConsentGiven = function (dL) { - if (dL) { - dL = dL * 60 * 60 * 1000; + this.rememberConsentGiven = function (dN) { + if (dN) { + dN = dN * 60 * 60 * 1000; } else { - dL = 30 * 365 * 24 * 60 * 60 * 1000; + dN = 30 * 365 * 24 * 60 * 60 * 1000; } - var dJ = true; - this.setConsentGiven(dJ); - var dK = new Date().getTime(); - dE(bo, dK, dL, bC, dm, b5, aR); + var dL = true; + this.setConsentGiven(dL); + var dM = new Date().getTime(); + dG(bo, dM, dN, bC, dp, b5, aR); }; - this.forgetConsentGiven = function (dJ) { - if (dJ) { - dJ = dJ * 60 * 60 * 1000; + this.forgetConsentGiven = function (dL) { + if (dL) { + dL = dL * 60 * 60 * 1000; } else { - dJ = 30 * 365 * 24 * 60 * 60 * 1000; + dL = 30 * 365 * 24 * 60 * 60 * 1000; } - cc(bo, bC, dm); - dE(c9, new Date().getTime(), dJ, bC, dm, b5, aR); + cc(bo, bC, dp); + dG(da, new Date().getTime(), dL, bC, dp, b5, aR); this.forgetCookieConsentGiven(); this.requireConsent(); }; @@ -4386,7 +4453,7 @@ if (typeof window.Matomo !== 'object') { this.setConsentGiven(false); }; this.enableFileTracking = function () { - cV = true; + cW = true; }; n(function () { setTimeout(function () { @@ -4402,7 +4469,7 @@ if (typeof window.Matomo !== 'object') { } if (!aE) { aV(); - dz(); + dB(); } }, }); @@ -4446,6 +4513,7 @@ if (typeof window.Matomo !== 'object') { 'forgetCookieConsentGiven', 'requireCookieConsent', 'disableBrowserFeatureDetection', + 'disableCampaignParameters', 'disableCookies', 'setTrackerUrl', 'setAPIUrl', diff --git a/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts b/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts index 7240df8b..e3df4e4f 100644 --- a/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts +++ b/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts @@ -64,7 +64,6 @@ import regression from 'regression'; import { TextToSpeechService } from '../../../services/textToSpeech/text-to-speech.service'; import { SanremoYOUDevice } from '../../../classes/preparationDevice/sanremo/sanremoYOUDevice'; import { SanremoYOUMode } from '../../../enums/preparationDevice/sanremo/sanremoYOUMode'; -import { BookooScale } from '../../../classes/devices/bokooScale'; declare var Plotly; @@ -878,6 +877,21 @@ export class BrewBrewingGraphComponent implements OnInit { this.getChartConfig() ); + setTimeout(() => { + //Do this after the plot. + if ( + this.brewComponent?.brewBrewingPreparationDeviceEl?.preparationDeviceConnected() + ) { + if ( + this.brewComponent?.brewBrewingPreparationDeviceEl?.hasTargetWeightActive() + ) { + this.drawTargetWeight( + this.brewComponent?.brewBrewingPreparationDeviceEl?.getTargetWeight() + ); + } + } + }, 50); + this.lastChartRenderingInstance = -1; if (this.brewComponent.maximizeFlowGraphIsShown) { //After we don't know how long all scale events take, dispatch the resize event, after the flow component will then grab on and stretch the canva. @@ -1174,6 +1188,52 @@ export class BrewBrewingGraphComponent implements OnInit { return layout; } + public drawTargetWeight(_targetWeight: number) { + if ( + this.brewComponent?.brewBrewingPreparationDeviceEl?.getPreparationDeviceType() === + PreparationDeviceType.SANREMO_YOU || + true + ) { + if (!('shapes' in this.lastChartLayout)) { + this.lastChartLayout['shapes'] = []; + } + let didWeAlreadyHaveAshape: boolean = false; + for (const [index, shape] of this.lastChartLayout['shapes'].entries()) { + if (shape['customId'] === 'targetWeightLine') { + if (_targetWeight > 0) { + //We have already one shape. + shape['y0'] = _targetWeight; + shape['y1'] = _targetWeight; + } else { + this.lastChartLayout['shapes'].splice(index, 1); + } + didWeAlreadyHaveAshape = true; + + break; + } + } + if (didWeAlreadyHaveAshape === false && _targetWeight > 0) { + this.lastChartLayout['shapes'].push({ + type: 'line', + x0: 0, // Anfang der x-Achse (in Bezug auf die Daten) + x1: 1, // Ende der x-Achse (1 = 100% der Breite) + y0: _targetWeight, // Y-Position der Linie (auf der Datenachse) + y1: _targetWeight, // gleiche Y-Position, um eine horizontale Linie zu erzeugen + xref: 'paper', // Verweise auf das gesamte Diagramm (nicht nur den Datenbereich) + yref: 'y', // Y-Achse bezieht sich auf den Datenwert + line: { + color: 'rgb(193, 160, 80)', + width: 2, + dash: 'dot', // Stil der Linie (optional) + }, + customId: 'targetWeightLine', + }); + } + + Plotly.relayout(this.profileDiv.nativeElement, this.lastChartLayout); + } + } + private getChartConfig() { if (this.isDetail === true) { const config = { @@ -1634,6 +1694,16 @@ export class BrewBrewingGraphComponent implements OnInit { // If users rests, we reset also drip time, else the script would not recognize it. this.data.coffee_first_drip_time = 0; this.data.coffee_first_drip_time_milliseconds = 0; + + const deviceType = + this.brewComponent?.brewBrewingPreparationDeviceEl?.getDataPreparationDeviceType(); + if (deviceType === PreparationDeviceType.XENIA) { + this.stopFetchingDataFromSanremoYOU(); + } else if (deviceType === PreparationDeviceType.METICULOUS) { + this.stopFetchingDataFromMeticulous(); + } else if (deviceType === PreparationDeviceType.SANREMO_YOU) { + this.stopFetchingDataFromSanremoYOU(); + } } if (scale || pressureDevice || temperatureDevice) { @@ -1800,10 +1870,12 @@ export class BrewBrewingGraphComponent implements OnInit { this.brewComponent?.brewBrewingPreparationDeviceEl?.getPreparationDeviceType() === PreparationDeviceType.SANREMO_YOU && _event !== 'sanremo_you' && + _event !== 'shot_ended' && this.machineStopScriptWasTriggered === false && this.data.preparationDeviceBrew.params.selectedMode !== SanremoYOUMode.LISTENING ) { + //User pressed the pause button, or the stopAtWeight is bigger then 0 this.machineStopScriptWasTriggered = true; this.uiLog.log(`Sanremo YOU - Pause button pressed, stop shot`); const prepDeviceCall: SanremoYOUDevice = this.brewComponent @@ -2950,7 +3022,7 @@ export class BrewBrewingGraphComponent implements OnInit { 'We could not stop at weight: ' + _msg, false ); - this.uiLog.log('We could not start script at weight: ' + _msg); + this.uiLog.log('We could not stop script at weight: ' + _msg); }); // This will be just called once, we stopped the shot and now we check if we directly shall stop or not @@ -3058,7 +3130,8 @@ export class BrewBrewingGraphComponent implements OnInit { scale ); if (this.machineStopScriptWasTriggered === false) { - if (thresholdHit) { + //Don't stop the machine when the target weight is 0 + if (thresholdHit && targetWeight > 0) { this.machineStopScriptWasTriggered = true; this.triggerStopShotOnSanremoYOU(_val.actual); } @@ -3699,7 +3772,7 @@ export class BrewBrewingGraphComponent implements OnInit { if (!isMeticulous) { // We have found a written weight which is above 5 grams at least this.__setScaleWeight(weight, false, false); - this.brewComponent.timer.pauseTimer(); + this.brewComponent.timer.pauseTimer('shot_ended'); this.changeDetectorRef.markForCheck(); this.brewComponent.timer.checkChanges(); this.checkChanges(); diff --git a/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.html b/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.html index 716cb30d..5f07d67a 100644 --- a/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.html +++ b/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.html @@ -84,7 +84,7 @@ {{"PREPARATION_DEVICE.TYPE_SANREMO_YOU.TITLE" | translate }} - {{"PREPARATION_DEVICE.TYPE_SANREMO_YOU.MODE_LISTENING" | translate}} @@ -105,9 +105,16 @@ - + + + + {{"PREPARATION_DEVICE.TYPE_SANREMO_YOU.NO_PROFILE_TARGET_WEIGHT_INFORMATION" | translate}} + {{"PREPARATION_DEVICE.TYPE_SANREMO_YOU.NO_MANUAL_TARGET_WEIGHT_INFORMATION" | translate}} + + diff --git a/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.ts b/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.ts index cb340e75..9e8f8d08 100644 --- a/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.ts +++ b/src/components/brews/brew-brewing-preparation-device/brew-brewing-preparation-device.component.ts @@ -45,6 +45,7 @@ import { SanremoYOUParams, } from '../../../classes/preparationDevice/sanremo/sanremoYOUDevice'; import { SanremoYOUMode } from '../../../enums/preparationDevice/sanremo/sanremoYOUMode'; + @Component({ selector: 'brew-brewing-preparation-device', templateUrl: './brew-brewing-preparation-device.component.html', @@ -93,6 +94,36 @@ export class BrewBrewingPreparationDeviceComponent implements OnInit { } } + public hasTargetWeightActive() { + return this.getTargetWeight() > 0; + } + + public getTargetWeight(): number { + const connectedType = this.getDataPreparationDeviceType(); + let targetWeight = 0; + if (connectedType === PreparationDeviceType.XENIA) { + targetWeight = + this.data.preparationDeviceBrew?.params.scriptAtWeightReachedNumber; + } else if (connectedType === PreparationDeviceType.SANREMO_YOU) { + targetWeight = this.data.preparationDeviceBrew?.params.stopAtWeight; + } + return targetWeight; + } + + public sanremoSelectedModeChange() { + const selectedMode: SanremoYOUMode = + this.data.preparationDeviceBrew?.params.selectedMode; + if (selectedMode === SanremoYOUMode.LISTENING) { + ( + this.data.preparationDeviceBrew?.params as SanremoYOUParams + ).stopAtWeight = 0; + this.drawTargetWeight(0); + } + } + + public drawTargetWeight(_weight: number) { + this.brewComponent.brewBrewingGraphEl?.drawTargetWeight(_weight); + } public hasAPreparationDeviceSet() { return ( this.preparation?.connectedPreparationDevice.type !== From 1c9b71c000482910fe07581435254e74fb749af9 Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Sat, 7 Sep 2024 23:02:56 +0200 Subject: [PATCH 44/55] #769 fixing scroll for beans --- src/app/beans/beans.page.ts | 5 ++++- src/app/settings/settings.page.html | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/app/beans/beans.page.ts b/src/app/beans/beans.page.ts index 3e70c849..63bff187 100644 --- a/src/app/beans/beans.page.ts +++ b/src/app/beans/beans.page.ts @@ -149,7 +149,10 @@ export class BeansPage implements OnDestroy { this.frozenBeansCollapsed = !this.frozenBeansCollapsed; } this.__saveCollapseFilter(); - this.research(); + + this.__initializeBeans(); + this.changeDetectorRef.detectChanges(); + this.retriggerScroll(); } private async __saveCollapseFilter() { this.settings.bean_collapsed.OPEN = this.openBeansCollapsed; diff --git a/src/app/settings/settings.page.html b/src/app/settings/settings.page.html index 39465cb3..7a10c894 100644 --- a/src/app/settings/settings.page.html +++ b/src/app/settings/settings.page.html @@ -687,8 +687,8 @@

{{"SMART_SCALE_ESPRESSO_STOP_ON_NO_WEIGHT_CHANGE" | translate}}

{{"SMART_SCALE_ESPRESSO_STOP_ON_NO_WEIGHT_CHANGE_MIN_FLOW_DESCRIPTION" | translate}}

- - +
{{settings.bluetooth_scale_espresso_stop_on_no_weight_change_min_flow | number : '.0-2'}} g/s
From 81d6b784dff538e4057ab9de3a5c895b9c8fe22b Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Sun, 8 Sep 2024 20:54:25 +0200 Subject: [PATCH 45/55] Lokalise update --- src/assets/i18n/de.json | 74 +++++++++++++++++++++++++++++++- src/assets/i18n/en.json | 94 ++++++++++++++++++++--------------------- src/assets/i18n/es.json | 76 +++++++++++++++++++++++++++++++-- src/assets/i18n/fr.json | 80 ++++++++++++++++++++++++++++++++--- src/assets/i18n/id.json | 74 +++++++++++++++++++++++++++++++- src/assets/i18n/it.json | 74 +++++++++++++++++++++++++++++++- src/assets/i18n/nl.json | 74 +++++++++++++++++++++++++++++++- src/assets/i18n/pl.json | 74 +++++++++++++++++++++++++++++++- src/assets/i18n/tr.json | 74 +++++++++++++++++++++++++++++++- src/assets/i18n/zh.json | 74 +++++++++++++++++++++++++++++++- 10 files changed, 699 insertions(+), 69 deletions(-) diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json index d63be163..77f5fe05 100644 --- a/src/assets/i18n/de.json +++ b/src/assets/i18n/de.json @@ -1030,7 +1030,8 @@ "TYPE": { "NONE": "Keins", "XENIA": "Xenia", - "METICULOUS": "Meticulous" + "METICULOUS": "Meticulous", + "SANREMO_YOU": "Sanremo YOU" }, "URL": "Url", "CHOOSE_DEVICE": "Gerät auswählen", @@ -1063,6 +1064,19 @@ "CHOOSE_PROFILE": "Profil wählen", "SHOT_STARTED": "Shot gestartet", "SHOT_ENDED": "Shot beendet" + }, + "TYPE_SANREMO_YOU": { + "TITLE": "Sanremo YOU Maschine", + "STOP_AT_WEIGHT": "Stopp bei Gewicht", + "SELECT_MODE": "Modus auswählen", + "MODE_LISTENING": "Hören", + "MODE_CONTROL": "Steuern", + "MANUAL_CONTROLLING": "", + "PROFILE_P1_CONTROLLING": "", + "PROFILE_P2_CONTROLLING": "", + "PROFILE_P3_CONTROLLING": "", + "NO_PROFILE_TARGET_WEIGHT_INFORMATION": "", + "NO_MANUAL_TARGET_WEIGHT_INFORMATION": "" } }, "DEVICE_CONNECTION": "Geräteverbindung", @@ -1319,5 +1333,61 @@ "BEAN_DATA_FROZEN_NOTE": "Notizen zum Einfrieren", "IGNORE_NEGATIVE_VALUES_DESCRIPTION": "Wenn diese Option aktiviert ist, wird ein Diagramm erstellt, das eine Sekunde zurückliegt", "IGNORE_ANOMALY_VALUES_DESCRIPTION": "Wenn diese Option aktiviert ist, wird ein Diagramm erstellt, das eine Sekunde zurückliegt", - "SAVE_LOGFILES_TO_NOTES_FROM_MACHINE": "Maschinenprotokolle speichern, wenn die Brühung gespeichert wird" + "SAVE_LOGFILES_TO_NOTES_FROM_MACHINE": "Maschinenprotokolle speichern, wenn die Brühung gespeichert wird", + "IMPORT_SHOT_FROM_METICULOUS": "Bezug importieren", + "BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "Alle gefundenen Einträge in der Excel-Liste wurden in neue Bohnen umgewandelt, genieße eine gute Brühung!", + "IMPORT_UNSUCCESSFULLY": "Import nicht erfolgreich", + "BEAN_LIST": "Bohnenliste", + "IMPORT_ROASTED_BEANS_EXCEL": "Geröstete Bohnen über Excel importieren", + "IMPORT_GREEN_BEANS_EXCEL": "Grüne Bohnen über Excel importieren", + "BEANS_IMPORTED_UNSUCCESSFULLY_WRONG_EXCELFILE": "Es sieht so aus, als ob die ausgewählte Excel-Datei beschädigt ist, falsche Daten enthält, falsch strukturiert ist oder falsch ausgewählt wurde. Aus diesem Grund konnten keine Bohnen importiert werden.", + "OK": "Ok", + "BEAN_SORT_BEAN_AGE": "Bohnenalter", + "GREEN_BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "Alle in der Excel-Liste gefundenen Einträge wurden in neue grüne Bohnen umgewandelt, happy Roasting!", + "PREPARATION_TYPE_SANREMO_YOU": "Sanremo YOU", + "PREPARATION_TYPE_XENIA": "Xenia", + "BEAN_POPUP_YOU_DONT_SEE_EVERYTHING_DESCRIPTION": "Du fügst eine Bohne hinzu, die Sorteninformationen enthält, aber diese Parameter sind nicht aktiviert. Möchtest du sie jetzt aktivieren?", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS": "Definieren der Achsen für den Druckgraph", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_DESCRIPTION": "Festlegen der Anfangs- und Endgröße der Achsen für den Druck", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_RANGE": "Druckachsen", + "PAGE_SETTINGS_LANGUAGE_DUTCH": "Holländisch", + "DOWNLOAD_IMPORT_EXCEL_TEMPLATES": "Importvorlagen herunterladen", + "SHOW_GRAPH": "Graph anzeigen", + "UPDATE_TEXT_TITLE_TITLE": { + "7.5.0": { + "TITLE": "Version 7.5.0: Das ist neu", + "DESCRIPTION": [ + " Neue Sprache <\/b>", + "Unterstützung von Niederländisch - Danke an Ygg", + "", + "Brühungen<\/b>", + "Das Einklappen der Brühliste ist nun möglich", + "Anzeigen, ob eine gefrorene Bohne verwendet wurde", + "Bohnenbild jetzt in der Auswahl sichtbar", + "Lesen und importieren von alten Brühungen der Meticulous", + "Brühlisten-Einträge können jetzt gewischt werden, wenn sie ein Diagramm haben", + "", + "Bohnen<\/b>", + "Das Einklappen der Bohnenliste ist jetzt möglich", + "Bohnen können jetzt nach Bohnenalter sortiert werden", + "Sortierungen werden beim Archivieren einer Bean nicht mehr zurückgesetzt", + "", + "Rohkaffee<\/b>", + "Rohkaffee sind jetzt importierbar - siehe Einstellungen", + "", + "Einstellungen<\/b>", + "Einstellen der Startachse für den Druck", + "Importieren von Rohbohnen und geröstetem Kaffee", + "", + " Erkennung von Datenbeschädigungen <\/b>", + "Beanconqueror prüft nun, ob eine Datenbeschädigung aufgetreten ist und zeigt Ihnen in diesem Fall ein Popup an, um das Einspielen einer Sicherungskopie zu ermöglichen.", + "", + "Sonstiges<\/b>", + "Buxfix zur Sortierung von Zubereitungsmethoden", + "Korrektur der Berechnung von gefrorenen Bohnen - Behebung von Rundungsproblemen", + "Einige technische Änderungen im Code", + "Kleine Optimierungen" + ] + } + } } \ No newline at end of file diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index f38dd487..7a183f5b 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -1066,17 +1066,17 @@ "SHOT_ENDED": "Shot ended" }, "TYPE_SANREMO_YOU": { - "TITLE": "Sanremo YOU Machine", - "STOP_AT_WEIGHT": "Stop at weight", - "SELECT_MODE": "Select mode", - "MODE_LISTENING": "Listening", - "MODE_CONTROL": "Control", - "MANUAL_CONTROLLING": "Manual Control", - "PROFILE_P1_CONTROLLING": "Profile P1 Control", - "PROFILE_P2_CONTROLLING": "Profile P2 Control", - "PROFILE_P3_CONTROLLING": "Profile P3 Control", - "NO_PROFILE_TARGET_WEIGHT_INFORMATION": "You did not define a target weight, the chosen profile will be fully executed", - "NO_MANUAL_TARGET_WEIGHT_INFORMATION": "You did not define a target weight, please stop extraction manually" + "TITLE": "Sanremo YOU Machine", + "STOP_AT_WEIGHT": "Stop at weight", + "SELECT_MODE": "Select mode", + "MODE_LISTENING": "Listening", + "MODE_CONTROL": "Control", + "MANUAL_CONTROLLING": "Manual Control", + "PROFILE_P1_CONTROLLING": "Profile P1 Control", + "PROFILE_P2_CONTROLLING": "Profile P2 Control", + "PROFILE_P3_CONTROLLING": "Profile P3 Control", + "NO_PROFILE_TARGET_WEIGHT_INFORMATION": "You did not define a target weight, the chosen profile will be fully executed", + "NO_MANUAL_TARGET_WEIGHT_INFORMATION": "You did not define a target weight, please stop extraction manually" } }, "DEVICE_CONNECTION": "Device connection", @@ -1354,40 +1354,40 @@ "DOWNLOAD_IMPORT_EXCEL_TEMPLATES": "Download import templates", "SHOW_GRAPH": "Show graph", "UPDATE_TEXT_TITLE_TITLE": { - "7.5.0": { - "TITLE": "Version 7.5.0: What's new", - "DESCRIPTION": [ - "New language", - "Dutch support - Thanks to Ygg", - "", - "Brews", - "Collapsing a brew-list is now possible", - "Displaying if a frozen bean was used", - "Bean image now visible on selection", - "Read and import a history shot from the Meticulous", - "Brew list entries are now swipe able when they have graph", - "", - "Beans", - "Collapsing the bean list is now possible", - "Beans are now sortable by bean age", - "Sorting are not reset anymore when archiving a bean", - "", - "Green beans", - "Green beans are now importable - See Settings", - "", - "Settings", - "Set the starting axis for the pressure", - "Import green beans or roasted beans", - "", - "Data corruption detection", - "Beanconqueror now checks if somehow a data corruption occured, and if yes, it shows you a popup to make it possible to import a backup", - "", - "Other", - "Fixing sorting of preparation tools", - "Fixing frozen bean calculation to fix rounding issues", - "Some technical changes in the code", - "Small tweaks" - ] - } + "7.5.0": { + "TITLE": "Version 7.5.0: What's new", + "DESCRIPTION": [ + "New language<\/b>", + "Dutch support - Thanks to Ygg", + "", + "Brews<\/b>", + "Collapsing a brew-list is now possible", + "Displaying if a frozen bean was used", + "Bean image now visible on selection", + "Read and import a history shot from the Meticulous", + "Brew list entries are now swipe able when they have graph", + "", + "Beans<\/b>", + "Collapsing the bean list is now possible", + "Beans are now sortable by bean age", + "Sorting are not reset anymore when archiving a bean", + "", + "Green beans<\/b>", + "Green beans are now importable - See Settings", + "", + "Settings<\/b>", + "Set the starting axis for the pressure", + "Import green beans or roasted beans", + "", + "Data corruption detection<\/b>", + "Beanconqueror now checks if somehow a data corruption occured, and if yes, it shows you a popup to make it possible to import a backup", + "", + "Other<\/b>", + "Fixing sorting of preparation tools", + "Fixing frozen bean calculation to fix rounding issues", + "Some technical changes in the code", + "Small tweaks" + ] + } } -} +} \ No newline at end of file diff --git a/src/assets/i18n/es.json b/src/assets/i18n/es.json index 38d1a467..f6d2501e 100644 --- a/src/assets/i18n/es.json +++ b/src/assets/i18n/es.json @@ -1030,7 +1030,8 @@ "TYPE": { "NONE": "Ninguno", "XENIA": "Xenia", - "METICULOUS": "Meticulous" + "METICULOUS": "Meticulous", + "SANREMO_YOU": "" }, "URL": "URL", "CHOOSE_DEVICE": "Seleccionar dispositivo", @@ -1063,6 +1064,19 @@ "CHOOSE_PROFILE": "Elegir perfil", "SHOT_STARTED": "Preparación iniciada", "SHOT_ENDED": "Preparación finalizada" + }, + "TYPE_SANREMO_YOU": { + "TITLE": "", + "STOP_AT_WEIGHT": "", + "SELECT_MODE": "", + "MODE_LISTENING": "", + "MODE_CONTROL": "", + "MANUAL_CONTROLLING": "", + "PROFILE_P1_CONTROLLING": "", + "PROFILE_P2_CONTROLLING": "", + "PROFILE_P3_CONTROLLING": "", + "NO_PROFILE_TARGET_WEIGHT_INFORMATION": "", + "NO_MANUAL_TARGET_WEIGHT_INFORMATION": "" } }, "DEVICE_CONNECTION": "Conexión del dispositivo", @@ -1228,7 +1242,7 @@ "PAGE_SETTINGS_BREW_TIMER_START_DELAY_ACTIVE_DESCRIPTION": "Elige el tiempo de retraso que quieres que se muestre al iniciar una preparación", "STARTING_IN": "Iniciando en... {{time}}", "IOS_DATABASE_ISSUE_TITLE": "¡ATENCIÓN! SE HA PERDIDO LA CONEXIÓN CON LA BASE DE DATOS", - "IOS_DATABASE_ISSUE_DESCRIPTION": "Lamentamos comunicarte que la conexión con la base de datos se ha perdido. Este problema es debido a un fallo no resuelto en el sistema operativo por parte de Apple, que va más allá del control de Beanconqueror. Para evitar que ser pierdan datos, cierra completamente la aplicación y ábrela de nuevo.", + "IOS_DATABASE_ISSUE_DESCRIPTION": "Hemos perdido la conexión a la base de datos. Esto es debido a un fallo no resuelto en iOS por parte de Apple, que escapa al control de Beanconqueror. Para evitar que se pierdan datos, cierra completamente la aplicación y ábrela de nuevo.", "RELOAD_APP": "Recargar aplicación ahora", "WATER_TYPE_ADD_CUSTOM": "Agua personalizada", "WATER_TYPE_THIRD_WAVE_WATER_CLASSIC_LIGHT_ROAST_PROFILE": "Third Wave Water - Classic Light Roast Profile", @@ -1319,5 +1333,61 @@ "BEAN_DATA_FROZEN_NOTE": "Información de congelado", "IGNORE_NEGATIVE_VALUES_DESCRIPTION": "Activar esto añadirá un segundo de retardo en los gráficos", "IGNORE_ANOMALY_VALUES_DESCRIPTION": "Activar esto añadirá un segundo de retardo en los gráficos", - "SAVE_LOGFILES_TO_NOTES_FROM_MACHINE": "Guardar los registros de la máquina con la preparación" + "SAVE_LOGFILES_TO_NOTES_FROM_MACHINE": "Guardar los registros de la máquina con la preparación", + "IMPORT_SHOT_FROM_METICULOUS": "Importar tiro de espresso", + "BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "Se han importado con éxito los cafés. ¡A disfrutarlos!", + "IMPORT_UNSUCCESSFULLY": "Importación fallida", + "BEAN_LIST": "Lista de cafés", + "IMPORT_ROASTED_BEANS_EXCEL": "Importar cafés tostados desde Excel", + "IMPORT_GREEN_BEANS_EXCEL": "Importar cafés verdes desde Excel", + "BEANS_IMPORTED_UNSUCCESSFULLY_WRONG_EXCELFILE": "El archivo Excel seleccionado tiene datos erróneos o no tiene el formato correcto. No se han podido importar los datos.", + "OK": "OK", + "BEAN_SORT_BEAN_AGE": "Antigüedad", + "GREEN_BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "Se han importado con éxito los cafés verdes. ¡A tostar!", + "PREPARATION_TYPE_SANREMO_YOU": "", + "PREPARATION_TYPE_XENIA": "Xenia", + "BEAN_POPUP_YOU_DONT_SEE_EVERYTHING_DESCRIPTION": "Vas a añadir un café con datos que están actualmente ocultos. ¿Quieres que cambiemos los ajustes para que se muestren?", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS": "Definir ejes para la gráfica de presión", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_DESCRIPTION": "Rango de valores para el eje de presión", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_RANGE": "Ejes de presión", + "PAGE_SETTINGS_LANGUAGE_DUTCH": "Neerlandés", + "DOWNLOAD_IMPORT_EXCEL_TEMPLATES": "Descargar plantilla Excel para importar datos", + "SHOW_GRAPH": "Mostrar gráfica", + "UPDATE_TEXT_TITLE_TITLE": { + "7.5.0": { + "TITLE": "Versión 7.5.0: Novedades", + "DESCRIPTION": [ + "Nuevo idioma<\/b>", + "Añadida traducción al Neerlandés - Gracias a Ygg", + "", + "Preparaciones<\/b>", + "Ahora se puede contraer la lista de preparaciones.", + "Ahora se muestra si se ha usado un café congelado.", + "El selector de cafés muestra ahora la foto de cada uno.", + "Añadido soporte para importar espressos preparados en Meticulous.", + "Si las preparaciones tienen gráficas asociadas, ahora puedes deslizar para moverte entre ellas.", + "", + "Cafés<\/b>", + "Ahora se puede contraer la lista de cafés.", + "Añadida opción para ordenar por antigüedad del café.", + "El ordenado ya no se pierde al archivar un café.", + "", + "Cafés verdes<\/b>", + "Añadido soporte para cafés verdes (sin tostar).", + "", + "Configuración<\/b>", + "Ahora se puede ajustar el rango del eje de presión en las gráficas.", + "Ahora puedes añadir cafés verdes (sin tostar) o tostados.", + "", + "Detección de datos corruptos<\/b>", + "Beanconqueror ahora verifica si los datos guardados se han corrompido. En ese caso, se intentará restaurar una copia de seguridad.", + "", + "Otros<\/b>", + "Solucionados errores al ordenar herramientas de preparación.", + "Solucionados errores numéricos de redondeo al congelar cafés.", + "Cambios técnicos en el código.", + "Pequeñas mejoras." + ] + } + } } \ No newline at end of file diff --git a/src/assets/i18n/fr.json b/src/assets/i18n/fr.json index 17a5ccbe..1d7c7d78 100644 --- a/src/assets/i18n/fr.json +++ b/src/assets/i18n/fr.json @@ -153,7 +153,7 @@ "BREW_DATA_ATTACHMENTS": "Fichiers jointes \/ Photos", "BREW_DATA_RATING": "Evaluation", "BREW_DATA_CALCULATED_COFFEE_BREW_TIME": "Temps d'infusion du café", - "BREW_DATA_TDS": "Total des solides dissous (TDS)", + "BREW_DATA_TDS": "Solides dissous totaux (SDT)", "BREW_DATA_CALCULATED_EXTRACTION_YIELD": "Rendement d’extraction %", "BREW_INFORMATION_BREW_RATIO": "Ratio d’infusion", "BREW_INFORMATION_BEAN_AGE": "Âge des grains", @@ -1030,7 +1030,8 @@ "TYPE": { "NONE": "Aucun", "XENIA": "Xenia", - "METICULOUS": "Meticulous" + "METICULOUS": "Meticulous", + "SANREMO_YOU": "Sanremo YOU" }, "URL": "Url", "CHOOSE_DEVICE": "Choisir l'appareil", @@ -1063,6 +1064,19 @@ "CHOOSE_PROFILE": "Choisir un profil", "SHOT_STARTED": "L'infusion a commencé", "SHOT_ENDED": "Infusion terminée" + }, + "TYPE_SANREMO_YOU": { + "TITLE": "Machine San Remo YOU", + "STOP_AT_WEIGHT": "Arrêt au poids", + "SELECT_MODE": "Sélectionner le mode", + "MODE_LISTENING": "Écouter", + "MODE_CONTROL": "Contrôle", + "MANUAL_CONTROLLING": "", + "PROFILE_P1_CONTROLLING": "", + "PROFILE_P2_CONTROLLING": "", + "PROFILE_P3_CONTROLLING": "", + "NO_PROFILE_TARGET_WEIGHT_INFORMATION": "", + "NO_MANUAL_TARGET_WEIGHT_INFORMATION": "" } }, "DEVICE_CONNECTION": "Connexion de l'appareil", @@ -1148,7 +1162,7 @@ }, "SHOT": { "UPLOAD_SUCCESSFULLY": "Infusion téléchargé sur Visualizer.", - "UPLOAD_UNSUCCESSFULLY": "L'infusion n'a pas pu être téléchargée sur Visualizer." + "UPLOAD_UNSUCCESSFULLY": "La préparation n'a pas pu être téléchargée sur Visualizer." }, "URL": "URL du serveur", "USERNAME": "Nom d'utilisateur", @@ -1223,7 +1237,7 @@ "PAGE_SETTINGS_LANGUAGE_POLISH": "Polonais", "BREW_CANT_START_BECAUSE_TIMER_NOT_RESETTED_GENERAL_DESCRIPTION": "Tu dois d'abord réinitialiser ton minuteur avant de pouvoir commencer", "SMART_SCALE_FIRST_DRIP_THRESHOLD": "Seuil de la première goutte", - "SMART_SCALE_FIRST_DRIP_THRESHOLD_TOOLTIP": "À quel poids de balance la première goutte doit-elle être comptée ? Valeur par défaut : >= 0,1 g", + "SMART_SCALE_FIRST_DRIP_THRESHOLD_TOOLTIP": "À quel poids au niveau de la balance la première goutte doit-elle être comptée ? Valeur par défaut : >= 0,1 g", "PAGE_SETTINGS_BREW_TIMER_START_DELAY_ACTIVE": "Délai de démarrage de l’infusion", "PAGE_SETTINGS_BREW_TIMER_START_DELAY_ACTIVE_DESCRIPTION": "Définir un délai pour l'affichage de l'essoreuse de chargement jusqu'à ce que l'infusion commence", "STARTING_IN": "Partir à {{time}}", @@ -1319,5 +1333,61 @@ "BEAN_DATA_FROZEN_NOTE": "Notes sur geler", "IGNORE_NEGATIVE_VALUES_DESCRIPTION": "En activant cette fonction, le graphique sera décalé d'une seconde.", "IGNORE_ANOMALY_VALUES_DESCRIPTION": "En activant cette fonction, le graphique sera décalé d'une seconde.", - "SAVE_LOGFILES_TO_NOTES_FROM_MACHINE": "Enregistrer les journaux de la machine lors de l’enregistrement d’une infusion" + "SAVE_LOGFILES_TO_NOTES_FROM_MACHINE": "Enregistrer les journaux de la machine lors de l’enregistrement d’une infusion", + "IMPORT_SHOT_FROM_METICULOUS": "Importer une infusion", + "BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "Toutes les entrées trouvées dans la liste Excel ont été transformées en nouveaux grains, savoure une infusion délicieuse!", + "IMPORT_UNSUCCESSFULLY": "L'importation n'a pas abouti", + "BEAN_LIST": "Liste des grains", + "IMPORT_ROASTED_BEANS_EXCEL": "Importer des grains torréfiés via Excel", + "IMPORT_GREEN_BEANS_EXCEL": "Importer des grains verts via Excel", + "BEANS_IMPORTED_UNSUCCESSFULLY_WRONG_EXCELFILE": "Il semble que le fichier Excel choisi soit corrompu, qu’il contienne des données incorrectes, qu’il soit mal structuré ou qu’il ait été mal choisi. Pour cette raison, aucun grain ne pouvait être importé.", + "OK": "Ok", + "BEAN_SORT_BEAN_AGE": "Âge des grains", + "GREEN_BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "Toutes les entrées trouvées dans la liste excel ont été transformées en nouveaux grains verts, happy roasting !", + "PREPARATION_TYPE_SANREMO_YOU": "Sanremo YOU", + "PREPARATION_TYPE_XENIA": "Xenia", + "BEAN_POPUP_YOU_DONT_SEE_EVERYTHING_DESCRIPTION": "Tu ajoutes un grain qui contient des informations de variété, mais ces paramètres ne sont pas activés. Veux-tu les activer maintenant ?", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS": "Définir les axes du graphique de pression", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_DESCRIPTION": "Définir la taille initiale et finale des axes de pression", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_RANGE": "Axes de pression", + "PAGE_SETTINGS_LANGUAGE_DUTCH": "Néerlandais", + "DOWNLOAD_IMPORT_EXCEL_TEMPLATES": "Télécharger les modèles d'importation", + "SHOW_GRAPH": "Afficher le graphique", + "UPDATE_TEXT_TITLE_TITLE": { + "7.5.0": { + "TITLE": "Version 7.5.0: What's new?", + "DESCRIPTION": [ + " Nouvelle langue <\/b>", + "Support néerlandais - Merci à Ygg", + "", + "Infusions<\/b>", + "Il est possible de réduire une liste des infusions", + "Affichage si un grain congelé a été utilisé", + "L'image du grain est désormais visible lors de la sélection", + "Lisez et importez une infusion historique du Meticulous", + "Les entrées de la liste des infusions peuvent être balayées lorsqu’elles ont un graphique.", + "", + "Grains<\/b>", + "Il est possible de réduire la liste des grains", + "Les grains peuvent maintenant être triés par âge", + "Les tris ne sont plus réinitialisés lors de l'archivage d'un grain", + "", + "Grains verts<\/b>", + "Les haricots verts sont désormais importables - Voir les Paramètres", + "", + "Paramètres<\/b>", + "Définir l'axe de départ pour la pression", + "Importer des grains verts ou des grains torréfiés", + "", + "Détection de la corruption des données<\/b>", + "Beanconqueror vérifie maintenant si une corruption de données s'est produite, et si c'est le cas, il affiche une fenêtre contextuelle pour permettre l'importation d'une sauvegarde", + "", + " Autre <\/b>", + "Correction du tri des outils de préparation", + "Correction du calcul des grains congelés pour résoudre les problèmes d'arrondi", + "Quelques changements techniques dans le code", + "Petits ajustements" + ] + } + } } \ No newline at end of file diff --git a/src/assets/i18n/id.json b/src/assets/i18n/id.json index 50594012..b92d43ac 100644 --- a/src/assets/i18n/id.json +++ b/src/assets/i18n/id.json @@ -1030,7 +1030,8 @@ "TYPE": { "NONE": "None", "XENIA": "Xenia", - "METICULOUS": "Meticulous" + "METICULOUS": "Meticulous", + "SANREMO_YOU": "" }, "URL": "Url", "CHOOSE_DEVICE": "Choose device", @@ -1063,6 +1064,19 @@ "CHOOSE_PROFILE": "Pilih profil", "SHOT_STARTED": "Shot dimulai", "SHOT_ENDED": "Shot selesai" + }, + "TYPE_SANREMO_YOU": { + "TITLE": "", + "STOP_AT_WEIGHT": "", + "SELECT_MODE": "", + "MODE_LISTENING": "", + "MODE_CONTROL": "", + "MANUAL_CONTROLLING": "", + "PROFILE_P1_CONTROLLING": "", + "PROFILE_P2_CONTROLLING": "", + "PROFILE_P3_CONTROLLING": "", + "NO_PROFILE_TARGET_WEIGHT_INFORMATION": "", + "NO_MANUAL_TARGET_WEIGHT_INFORMATION": "" } }, "DEVICE_CONNECTION": "Device connection", @@ -1319,5 +1333,61 @@ "BEAN_DATA_FROZEN_NOTE": "Notes beku", "IGNORE_NEGATIVE_VALUES_DESCRIPTION": "Mengaktifkannya akan menghasilkan grafik yang tertinggal satu detik", "IGNORE_ANOMALY_VALUES_DESCRIPTION": "Mengaktifkannya akan menghasilkan grafik yang tertinggal satu detik", - "SAVE_LOGFILES_TO_NOTES_FROM_MACHINE": "Simpan log mesin saat menyimpan seduhan" + "SAVE_LOGFILES_TO_NOTES_FROM_MACHINE": "Simpan log mesin saat menyimpan seduhan", + "IMPORT_SHOT_FROM_METICULOUS": "", + "BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "", + "IMPORT_UNSUCCESSFULLY": "", + "BEAN_LIST": "", + "IMPORT_ROASTED_BEANS_EXCEL": "", + "IMPORT_GREEN_BEANS_EXCEL": "", + "BEANS_IMPORTED_UNSUCCESSFULLY_WRONG_EXCELFILE": "", + "OK": "", + "BEAN_SORT_BEAN_AGE": "", + "GREEN_BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "", + "PREPARATION_TYPE_SANREMO_YOU": "", + "PREPARATION_TYPE_XENIA": "Xenia", + "BEAN_POPUP_YOU_DONT_SEE_EVERYTHING_DESCRIPTION": "", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS": "", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_DESCRIPTION": "", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_RANGE": "", + "PAGE_SETTINGS_LANGUAGE_DUTCH": "", + "DOWNLOAD_IMPORT_EXCEL_TEMPLATES": "", + "SHOW_GRAPH": "", + "UPDATE_TEXT_TITLE_TITLE": { + "7.5.0": { + "TITLE": "", + "DESCRIPTION": [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ] + } + } } \ No newline at end of file diff --git a/src/assets/i18n/it.json b/src/assets/i18n/it.json index 522b9323..4251787f 100644 --- a/src/assets/i18n/it.json +++ b/src/assets/i18n/it.json @@ -1030,7 +1030,8 @@ "TYPE": { "NONE": "Nessuno", "XENIA": "Xenia", - "METICULOUS": "Meticulous" + "METICULOUS": "Meticulous", + "SANREMO_YOU": "" }, "URL": "URL", "CHOOSE_DEVICE": "Scegli dispositivo", @@ -1063,6 +1064,19 @@ "CHOOSE_PROFILE": "Scegli profilo", "SHOT_STARTED": "Estrazione iniziata", "SHOT_ENDED": "Estrazione terminata" + }, + "TYPE_SANREMO_YOU": { + "TITLE": "", + "STOP_AT_WEIGHT": "", + "SELECT_MODE": "", + "MODE_LISTENING": "", + "MODE_CONTROL": "", + "MANUAL_CONTROLLING": "", + "PROFILE_P1_CONTROLLING": "", + "PROFILE_P2_CONTROLLING": "", + "PROFILE_P3_CONTROLLING": "", + "NO_PROFILE_TARGET_WEIGHT_INFORMATION": "", + "NO_MANUAL_TARGET_WEIGHT_INFORMATION": "" } }, "DEVICE_CONNECTION": "Connessione dispositivi", @@ -1319,5 +1333,61 @@ "BEAN_DATA_FROZEN_NOTE": "Note sul congelato", "IGNORE_NEGATIVE_VALUES_DESCRIPTION": "Attivando questa opzione, il grafico avrà un secondo di ritardo", "IGNORE_ANOMALY_VALUES_DESCRIPTION": "Attivando questa opzione, il grafico avrà un secondo di ritardo", - "SAVE_LOGFILES_TO_NOTES_FROM_MACHINE": "Salva i log della macchina al salvataggio della preparazione" + "SAVE_LOGFILES_TO_NOTES_FROM_MACHINE": "Salva i log della macchina al salvataggio della preparazione", + "IMPORT_SHOT_FROM_METICULOUS": "", + "BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "", + "IMPORT_UNSUCCESSFULLY": "", + "BEAN_LIST": "", + "IMPORT_ROASTED_BEANS_EXCEL": "", + "IMPORT_GREEN_BEANS_EXCEL": "", + "BEANS_IMPORTED_UNSUCCESSFULLY_WRONG_EXCELFILE": "", + "OK": "", + "BEAN_SORT_BEAN_AGE": "", + "GREEN_BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "", + "PREPARATION_TYPE_SANREMO_YOU": "", + "PREPARATION_TYPE_XENIA": "", + "BEAN_POPUP_YOU_DONT_SEE_EVERYTHING_DESCRIPTION": "", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS": "", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_DESCRIPTION": "", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_RANGE": "", + "PAGE_SETTINGS_LANGUAGE_DUTCH": "", + "DOWNLOAD_IMPORT_EXCEL_TEMPLATES": "", + "SHOW_GRAPH": "", + "UPDATE_TEXT_TITLE_TITLE": { + "7.5.0": { + "TITLE": "", + "DESCRIPTION": [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ] + } + } } \ No newline at end of file diff --git a/src/assets/i18n/nl.json b/src/assets/i18n/nl.json index 31e2cd3e..8b67f4fa 100644 --- a/src/assets/i18n/nl.json +++ b/src/assets/i18n/nl.json @@ -1030,7 +1030,8 @@ "TYPE": { "NONE": "Geen", "XENIA": "Xenia", - "METICULOUS": "Meticulous" + "METICULOUS": "Meticulous", + "SANREMO_YOU": "" }, "URL": "URL-adres", "CHOOSE_DEVICE": "Kies apparaat", @@ -1063,6 +1064,19 @@ "CHOOSE_PROFILE": "Profiel kiezen", "SHOT_STARTED": "Brouwen gestart", "SHOT_ENDED": "Brouwen gestopt" + }, + "TYPE_SANREMO_YOU": { + "TITLE": "", + "STOP_AT_WEIGHT": "", + "SELECT_MODE": "", + "MODE_LISTENING": "", + "MODE_CONTROL": "", + "MANUAL_CONTROLLING": "", + "PROFILE_P1_CONTROLLING": "", + "PROFILE_P2_CONTROLLING": "", + "PROFILE_P3_CONTROLLING": "", + "NO_PROFILE_TARGET_WEIGHT_INFORMATION": "", + "NO_MANUAL_TARGET_WEIGHT_INFORMATION": "" } }, "DEVICE_CONNECTION": "Apparaatverbinding", @@ -1319,5 +1333,61 @@ "BEAN_DATA_FROZEN_NOTE": "Bevroren notities", "IGNORE_NEGATIVE_VALUES_DESCRIPTION": "Als je dit activeert, loopt de grafiek één seconde achter", "IGNORE_ANOMALY_VALUES_DESCRIPTION": "Als je dit activeert, loopt de grafiek één seconde achter", - "SAVE_LOGFILES_TO_NOTES_FROM_MACHINE": "Bewaar de machine logs bij het opslaan van een brouwsel" + "SAVE_LOGFILES_TO_NOTES_FROM_MACHINE": "Bewaar de machine logs bij het opslaan van een brouwsel", + "IMPORT_SHOT_FROM_METICULOUS": "Importeer een shot", + "BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "Alle gevonden items in de Excel-lijst zijn omgezet in nieuwe bonen. Geniet van een smakelijk kopje koffie!", + "IMPORT_UNSUCCESSFULLY": "Importeren is niet gelukt", + "BEAN_LIST": "Bonenlijst", + "IMPORT_ROASTED_BEANS_EXCEL": "Gebrande bonen importeren via Excel", + "IMPORT_GREEN_BEANS_EXCEL": "Groene bonen importeren via excel", + "BEANS_IMPORTED_UNSUCCESSFULLY_WRONG_EXCELFILE": "Het lijkt erop dat het gekozen excel-bestand corrupt is, verkeerde gegevens bevat, verkeerd is gestructureerd of verkeerd is gekozen. Hierdoor konden er geen beans worden geïmporteerd.", + "OK": "OK", + "BEAN_SORT_BEAN_AGE": "Bonen leeftijd", + "GREEN_BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "Alle gevonden items in de Excel-lijst zijn omgezet in nieuwe groene bonen, success met koffie roosteren!", + "PREPARATION_TYPE_SANREMO_YOU": "", + "PREPARATION_TYPE_XENIA": "Xenia", + "BEAN_POPUP_YOU_DONT_SEE_EVERYTHING_DESCRIPTION": "Je voegt een bean toe die variëteitsinformatie bevat, maar die parameters zijn niet geactiveerd. Wil je ze nu activeren?", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS": "Definieer de assen voor de drukgrafiek", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_DESCRIPTION": "Stel de begin- en eindgrootte van de assen voor druk in", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_RANGE": "Druk assen", + "PAGE_SETTINGS_LANGUAGE_DUTCH": "Nederlands", + "DOWNLOAD_IMPORT_EXCEL_TEMPLATES": "Download importsjablonen", + "SHOW_GRAPH": "Grafiek weergeven", + "UPDATE_TEXT_TITLE_TITLE": { + "7.5.0": { + "TITLE": "Versie 7.5.0: Wat is er nieuw", + "DESCRIPTION": [ + "Nieuwe taal<\/b>", + "Nederlandse ondersteuning - Met dank aan Ygg", + "", + " Brouwsels <\/b>", + "Het is nu mogelijk om een brouwlijst samen te vouwen", + "Weergeven of er een bevroren boon is gebruikt", + "Bonen afbeelding nu zichtbaar bij selectie", + "Lees en importeer een geschiedenisshot van de Meticulous", + "De vermeldingen in de brouwlijst zijn nu veegbaar als ze een grafiek hebben", + "", + " Bonen <\/b>", + "Het is nu mogelijk om de bonenlijst in te klappen", + "Bonen kunnen nu worden gesorteerd op boonleeftijd", + "Sortering wordt niet meer gereset bij het archiveren van een boon", + "", + " Groene bonen <\/b>", + "Groene bonen zijn nu importeerbaar - Zie Instellingen", + "", + " Instellingen <\/b>", + "Stel de start-as voor de druk in", + "Importeer groene bonen of geroosterde bonen", + "", + "Detectie van gegevenscorruptie<\/b>", + "Beanconqueror controleert nu of er op de een of andere manier een datacorruptie is opgetreden, en als dat zo is, toont het een pop-up waarmee u een back-up kunt importeren", + "", + "Anders<\/b>", + "Het oplossen van de sortering van bereidingsgereedschappen", + "Berekening van bevroren bonen om afrondingsproblemen op te lossen", + "Enkele technische wijzigingen in de code", + "Kleine aanpassingen" + ] + } + } } \ No newline at end of file diff --git a/src/assets/i18n/pl.json b/src/assets/i18n/pl.json index 23e94e5e..fe84cc33 100644 --- a/src/assets/i18n/pl.json +++ b/src/assets/i18n/pl.json @@ -1030,7 +1030,8 @@ "TYPE": { "NONE": "Nic", "XENIA": "Xenia", - "METICULOUS": "Meticulous" + "METICULOUS": "Meticulous", + "SANREMO_YOU": "" }, "URL": "Adres URL", "CHOOSE_DEVICE": "Wybierz urządzenie", @@ -1063,6 +1064,19 @@ "CHOOSE_PROFILE": "Wybierz profil", "SHOT_STARTED": "Strzał rozpoczęty", "SHOT_ENDED": "Strzał zakończony" + }, + "TYPE_SANREMO_YOU": { + "TITLE": "", + "STOP_AT_WEIGHT": "", + "SELECT_MODE": "", + "MODE_LISTENING": "", + "MODE_CONTROL": "", + "MANUAL_CONTROLLING": "", + "PROFILE_P1_CONTROLLING": "", + "PROFILE_P2_CONTROLLING": "", + "PROFILE_P3_CONTROLLING": "", + "NO_PROFILE_TARGET_WEIGHT_INFORMATION": "", + "NO_MANUAL_TARGET_WEIGHT_INFORMATION": "" } }, "DEVICE_CONNECTION": "Połączenie urządzenia", @@ -1319,5 +1333,61 @@ "BEAN_DATA_FROZEN_NOTE": "Zamrożone notatki", "IGNORE_NEGATIVE_VALUES_DESCRIPTION": "Aktywacja tej opcji spowoduje, że wykres będzie opóźniony o jedną sekundę.", "IGNORE_ANOMALY_VALUES_DESCRIPTION": "Aktywacja tej opcji spowoduje, że wykres będzie opóźniony o jedną sekundę.", - "SAVE_LOGFILES_TO_NOTES_FROM_MACHINE": "Zapisywanie dzienników urządzenia podczas zapisywania parzenia" + "SAVE_LOGFILES_TO_NOTES_FROM_MACHINE": "Zapisywanie dzienników urządzenia podczas zapisywania parzenia", + "IMPORT_SHOT_FROM_METICULOUS": "", + "BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "", + "IMPORT_UNSUCCESSFULLY": "", + "BEAN_LIST": "", + "IMPORT_ROASTED_BEANS_EXCEL": "", + "IMPORT_GREEN_BEANS_EXCEL": "", + "BEANS_IMPORTED_UNSUCCESSFULLY_WRONG_EXCELFILE": "", + "OK": "", + "BEAN_SORT_BEAN_AGE": "", + "GREEN_BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "", + "PREPARATION_TYPE_SANREMO_YOU": "", + "PREPARATION_TYPE_XENIA": "", + "BEAN_POPUP_YOU_DONT_SEE_EVERYTHING_DESCRIPTION": "", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS": "", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_DESCRIPTION": "", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_RANGE": "", + "PAGE_SETTINGS_LANGUAGE_DUTCH": "", + "DOWNLOAD_IMPORT_EXCEL_TEMPLATES": "", + "SHOW_GRAPH": "", + "UPDATE_TEXT_TITLE_TITLE": { + "7.5.0": { + "TITLE": "", + "DESCRIPTION": [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ] + } + } } \ No newline at end of file diff --git a/src/assets/i18n/tr.json b/src/assets/i18n/tr.json index f2e49174..a1b46f71 100644 --- a/src/assets/i18n/tr.json +++ b/src/assets/i18n/tr.json @@ -1030,7 +1030,8 @@ "TYPE": { "NONE": "Hiçbiri", "XENIA": "Xenia", - "METICULOUS": "Meticulous" + "METICULOUS": "Meticulous", + "SANREMO_YOU": "" }, "URL": "Url", "CHOOSE_DEVICE": "Cihaz seç", @@ -1063,6 +1064,19 @@ "CHOOSE_PROFILE": "Profil seçin", "SHOT_STARTED": "Akış başladı", "SHOT_ENDED": "Akış sona erdi" + }, + "TYPE_SANREMO_YOU": { + "TITLE": "", + "STOP_AT_WEIGHT": "", + "SELECT_MODE": "", + "MODE_LISTENING": "", + "MODE_CONTROL": "", + "MANUAL_CONTROLLING": "", + "PROFILE_P1_CONTROLLING": "", + "PROFILE_P2_CONTROLLING": "", + "PROFILE_P3_CONTROLLING": "", + "NO_PROFILE_TARGET_WEIGHT_INFORMATION": "", + "NO_MANUAL_TARGET_WEIGHT_INFORMATION": "" } }, "DEVICE_CONNECTION": "Cihaz bağlantısı", @@ -1319,5 +1333,61 @@ "BEAN_DATA_FROZEN_NOTE": "Dondurulmuş notlar", "IGNORE_NEGATIVE_VALUES_DESCRIPTION": "Bunu etkinleştirdiğinizde bir saniye geride olacak bir grafik ortaya çıkar", "IGNORE_ANOMALY_VALUES_DESCRIPTION": "Bunu etkinleştirdiğinizde bir saniye geride olacak bir grafik ortaya çıkar", - "SAVE_LOGFILES_TO_NOTES_FROM_MACHINE": "Bir demlemeyi kaydederken makine günlüklerini kaydedin" + "SAVE_LOGFILES_TO_NOTES_FROM_MACHINE": "Bir demlemeyi kaydederken makine günlüklerini kaydedin", + "IMPORT_SHOT_FROM_METICULOUS": "", + "BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "", + "IMPORT_UNSUCCESSFULLY": "", + "BEAN_LIST": "", + "IMPORT_ROASTED_BEANS_EXCEL": "", + "IMPORT_GREEN_BEANS_EXCEL": "", + "BEANS_IMPORTED_UNSUCCESSFULLY_WRONG_EXCELFILE": "", + "OK": "", + "BEAN_SORT_BEAN_AGE": "", + "GREEN_BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "", + "PREPARATION_TYPE_SANREMO_YOU": "", + "PREPARATION_TYPE_XENIA": "Xenia", + "BEAN_POPUP_YOU_DONT_SEE_EVERYTHING_DESCRIPTION": "", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS": "", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_DESCRIPTION": "", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_RANGE": "", + "PAGE_SETTINGS_LANGUAGE_DUTCH": "", + "DOWNLOAD_IMPORT_EXCEL_TEMPLATES": "", + "SHOW_GRAPH": "", + "UPDATE_TEXT_TITLE_TITLE": { + "7.5.0": { + "TITLE": "", + "DESCRIPTION": [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ] + } + } } \ No newline at end of file diff --git a/src/assets/i18n/zh.json b/src/assets/i18n/zh.json index bca45dc7..70ec6b44 100644 --- a/src/assets/i18n/zh.json +++ b/src/assets/i18n/zh.json @@ -1030,7 +1030,8 @@ "TYPE": { "NONE": "None", "XENIA": "Xenia", - "METICULOUS": "Meticulous" + "METICULOUS": "Meticulous", + "SANREMO_YOU": "" }, "URL": "Url", "CHOOSE_DEVICE": "选择设备", @@ -1063,6 +1064,19 @@ "CHOOSE_PROFILE": "选择配置文件", "SHOT_STARTED": "萃取开始", "SHOT_ENDED": "萃取结束" + }, + "TYPE_SANREMO_YOU": { + "TITLE": "", + "STOP_AT_WEIGHT": "", + "SELECT_MODE": "", + "MODE_LISTENING": "", + "MODE_CONTROL": "", + "MANUAL_CONTROLLING": "", + "PROFILE_P1_CONTROLLING": "", + "PROFILE_P2_CONTROLLING": "", + "PROFILE_P3_CONTROLLING": "", + "NO_PROFILE_TARGET_WEIGHT_INFORMATION": "", + "NO_MANUAL_TARGET_WEIGHT_INFORMATION": "" } }, "DEVICE_CONNECTION": "设备连接", @@ -1319,5 +1333,61 @@ "BEAN_DATA_FROZEN_NOTE": "冰冻风味叙述", "IGNORE_NEGATIVE_VALUES_DESCRIPTION": "激活此功能后,图表将落后一秒", "IGNORE_ANOMALY_VALUES_DESCRIPTION": "激活此功能后,图表将落后一秒", - "SAVE_LOGFILES_TO_NOTES_FROM_MACHINE": "保存冲泡时保存机器日志" + "SAVE_LOGFILES_TO_NOTES_FROM_MACHINE": "保存冲泡时保存机器日志", + "IMPORT_SHOT_FROM_METICULOUS": "", + "BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "", + "IMPORT_UNSUCCESSFULLY": "", + "BEAN_LIST": "", + "IMPORT_ROASTED_BEANS_EXCEL": "", + "IMPORT_GREEN_BEANS_EXCEL": "", + "BEANS_IMPORTED_UNSUCCESSFULLY_WRONG_EXCELFILE": "", + "OK": "", + "BEAN_SORT_BEAN_AGE": "", + "GREEN_BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "", + "PREPARATION_TYPE_SANREMO_YOU": "", + "PREPARATION_TYPE_XENIA": "Xenia", + "BEAN_POPUP_YOU_DONT_SEE_EVERYTHING_DESCRIPTION": "", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS": "", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_DESCRIPTION": "", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_RANGE": "", + "PAGE_SETTINGS_LANGUAGE_DUTCH": "", + "DOWNLOAD_IMPORT_EXCEL_TEMPLATES": "", + "SHOW_GRAPH": "", + "UPDATE_TEXT_TITLE_TITLE": { + "7.5.0": { + "TITLE": "", + "DESCRIPTION": [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ] + } + } } \ No newline at end of file From e54913ef687ea397be2019a376f34b49a537a79f Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Sun, 8 Sep 2024 21:31:42 +0200 Subject: [PATCH 46/55] Adding xenia machine image --- .../beanconqueror-preparation-xenia.svg | 22 +++++++++++++++++++ src/classes/preparation/preparation.ts | 2 +- 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 src/assets/custom-ion-icons/beanconqueror-preparation-xenia.svg diff --git a/src/assets/custom-ion-icons/beanconqueror-preparation-xenia.svg b/src/assets/custom-ion-icons/beanconqueror-preparation-xenia.svg new file mode 100644 index 00000000..352ab078 --- /dev/null +++ b/src/assets/custom-ion-icons/beanconqueror-preparation-xenia.svg @@ -0,0 +1,22 @@ + + + + diff --git a/src/classes/preparation/preparation.ts b/src/classes/preparation/preparation.ts index 4e0aa9b1..3394961b 100755 --- a/src/classes/preparation/preparation.ts +++ b/src/classes/preparation/preparation.ts @@ -257,7 +257,7 @@ export class Preparation implements IPreparation { case PREPARATION_TYPES.SANREMO_YOU: return 'beanconqueror-preparation-sanremo-you'; case PREPARATION_TYPES.XENIA: - return 'beanconqueror-preparation-portafilter'; + return 'beanconqueror-preparation-xenia'; default: return 'beanconqueror-preparation-custom'; } From a66a996472a74faa185bd3699488a08184ae358c Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Sun, 8 Sep 2024 22:24:24 +0200 Subject: [PATCH 47/55] #790 - Adding KG price --- .../bean-information.component.html | 2 +- .../bean-information.component.ts | 33 +++++++++++++++++-- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/components/bean-information/bean-information.component.html b/src/components/bean-information/bean-information.component.html index 8b4a5cd3..f723322a 100644 --- a/src/components/bean-information/bean-information.component.html +++ b/src/components/bean-information/bean-information.component.html @@ -162,7 +162,7 @@ {{'BEAN_DATA_COST' | translate}}
- {{bean?.cost}} + {{bean?.cost}} ({{calculateCostPerKG()}}{{getCurrencySymbol()}} / kg)
{{'BEAN_DATA_CUPPING_POINTS' | translate}}
diff --git a/src/components/bean-information/bean-information.component.ts b/src/components/bean-information/bean-information.component.ts index 3f3617cd..e31dbffd 100644 --- a/src/components/bean-information/bean-information.component.ts +++ b/src/components/bean-information/bean-information.component.ts @@ -36,11 +36,11 @@ import { BeanMapper } from '../../mapper/bean/beanMapper'; import { ServerCommunicationService } from '../../services/serverCommunication/server-communication.service'; import { UIHelper } from '../../services/uiHelper'; import { TranslateService } from '@ngx-translate/core'; -import BREW_TRACKING from '../../data/tracking/brewTracking'; import * as htmlToImage from 'html-to-image'; import { UIBrewHelper } from '../../services/uiBrewHelper'; import moment from 'moment/moment'; import { BEAN_FREEZING_STORAGE_ENUM } from '../../enums/beans/beanFreezingStorage'; +import { CurrencyService } from '../../services/currencyService/currency.service'; @Component({ selector: 'bean-information', @@ -81,7 +81,8 @@ export class BeanInformationComponent implements OnInit { private readonly translate: TranslateService, private readonly platform: Platform, private readonly uiBrewHelper: UIBrewHelper, - private actionSheetCtrl: ActionSheetController + private actionSheetCtrl: ActionSheetController, + private readonly currencyService: CurrencyService ) { this.settings = this.uiSettingsStorage.getSettings(); } @@ -542,4 +543,32 @@ export class BeanInformationComponent implements OnInit { } return false; } + + public showCostPerKG(): boolean { + if ( + this.bean.weight && + this.bean.weight > 0 && + this.bean.weight !== 1000 && + this.bean.cost && + this.bean.cost > 0 + ) { + return true; + } + return false; + } + public getCurrencySymbol() { + return this.currencyService.getActualCurrencySymbol(); + } + + public calculateCostPerKG() { + const beanWeight = this.bean.weight; + const beanCost = this.bean.cost; + + const costPerGramm = this.uiHelper.toFixedIfNecessary( + beanCost / beanWeight, + 2 + ); + const kgCost = costPerGramm * 1000; + return kgCost; + } } From 12e25d6cfd63a3f9f9897e6846d4aeda43547b6d Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Mon, 9 Sep 2024 19:42:27 +0200 Subject: [PATCH 48/55] Setting the bluetooth scale stay connected and wake_lock to true when connecting a device --- .../preparation-connected-device.component.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/app/preparation/preparation-connected-device/preparation-connected-device.component.ts b/src/app/preparation/preparation-connected-device/preparation-connected-device.component.ts index a4b55d94..32c86679 100644 --- a/src/app/preparation/preparation-connected-device/preparation-connected-device.component.ts +++ b/src/app/preparation/preparation-connected-device/preparation-connected-device.component.ts @@ -163,6 +163,8 @@ export class PreparationConnectedDeviceComponent { */ const settings: Settings = this.uiSettingsStorage.getSettings(); settings.bluetooth_scale_espresso_stop_on_no_weight_change = true; + settings.bluetooth_scale_stay_connected = true; + settings.wake_lock = true; await this.uiSettingsStorage.update(settings); } await this.uiPreparationStorage.update(this.data); From e90c54b23365d0976572aad5771d73d3ab40c2b1 Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Mon, 9 Sep 2024 20:37:57 +0200 Subject: [PATCH 49/55] Fixing currency --- src/components/bean-information/bean-information.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/bean-information/bean-information.component.html b/src/components/bean-information/bean-information.component.html index f723322a..4ebadc64 100644 --- a/src/components/bean-information/bean-information.component.html +++ b/src/components/bean-information/bean-information.component.html @@ -162,7 +162,7 @@
{{'BEAN_DATA_COST' | translate}}
- {{bean?.cost}} ({{calculateCostPerKG()}}{{getCurrencySymbol()}} / kg) + {{bean?.cost}} ({{calculateCostPerKG()}} {{getCurrencySymbol()}}/kg)
{{'BEAN_DATA_CUPPING_POINTS' | translate}}
From 13999d45ad9c3f2c2a9afbd78ba44b648ceadf3f Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Fri, 13 Sep 2024 21:16:25 +0200 Subject: [PATCH 50/55] We raised to 10 grams... just to make sure... --- .../brews/brew-brewing-graph/brew-brewing-graph.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts b/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts index e3df4e4f..6f0fbf62 100644 --- a/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts +++ b/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts @@ -2837,9 +2837,9 @@ export class BrewBrewingGraphComponent implements OnInit { * We try to match also turbo-shots which are like 7-8 grams. * Scales with just 3 values per second would be like 7 / 3 values per second = 2.33g increase each tick. * So we won't get jump from like 1 to 10 gram, then to like 40 grams - * Update 26.08.24 - We change from 5 to 7, because we had one shot where the value jumped from 0 to 5,5 and we didn't track anymore + * Update 26.08.24 - We change from 5 to 10, because we had one shot where the value jumped from 0 to 5,5 and we didn't track anymore */ - const plausibleEspressoWeightIncreaseBound: number = 7; + const plausibleEspressoWeightIncreaseBound: number = 10; risingFactorOK = entryBeforeVal + plausibleEspressoWeightIncreaseBound >= weight; From 2cb81a273e82acbfcccc8ba212d1572e400a8ca8 Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Sat, 14 Sep 2024 16:50:10 +0200 Subject: [PATCH 51/55] Lokalise update --- src/assets/i18n/id.json | 84 ++++++++++++++++++------------------ src/assets/i18n/it.json | 86 ++++++++++++++++++------------------ src/assets/i18n/pl.json | 86 ++++++++++++++++++------------------ src/assets/i18n/tr.json | 84 ++++++++++++++++++------------------ src/assets/i18n/zh.json | 96 ++++++++++++++++++++--------------------- 5 files changed, 218 insertions(+), 218 deletions(-) diff --git a/src/assets/i18n/id.json b/src/assets/i18n/id.json index b92d43ac..f9538a84 100644 --- a/src/assets/i18n/id.json +++ b/src/assets/i18n/id.json @@ -1334,59 +1334,59 @@ "IGNORE_NEGATIVE_VALUES_DESCRIPTION": "Mengaktifkannya akan menghasilkan grafik yang tertinggal satu detik", "IGNORE_ANOMALY_VALUES_DESCRIPTION": "Mengaktifkannya akan menghasilkan grafik yang tertinggal satu detik", "SAVE_LOGFILES_TO_NOTES_FROM_MACHINE": "Simpan log mesin saat menyimpan seduhan", - "IMPORT_SHOT_FROM_METICULOUS": "", - "BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "", - "IMPORT_UNSUCCESSFULLY": "", - "BEAN_LIST": "", - "IMPORT_ROASTED_BEANS_EXCEL": "", - "IMPORT_GREEN_BEANS_EXCEL": "", - "BEANS_IMPORTED_UNSUCCESSFULLY_WRONG_EXCELFILE": "", - "OK": "", - "BEAN_SORT_BEAN_AGE": "", - "GREEN_BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "", + "IMPORT_SHOT_FROM_METICULOUS": "Mengimpor seduhan", + "BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "Semua entri yang ditemukan dalam daftar Excel, telah diubah menjadi entri biji baru, selamat menikmati!", + "IMPORT_UNSUCCESSFULLY": "Impor tidak berhasil", + "BEAN_LIST": "Daftar kopi", + "IMPORT_ROASTED_BEANS_EXCEL": "Impor kopi menggunakan Excel", + "IMPORT_GREEN_BEANS_EXCEL": "Impor green beans menggunakan Excel", + "BEANS_IMPORTED_UNSUCCESSFULLY_WRONG_EXCELFILE": "Tampaknya file excel yang dipilih rusak, memiliki data yang salah, terstruktur salah, atau dipilih secara salah. Oleh karena itu, tidak ada data biji kopi yang dapat diimpor.", + "OK": "Ok", + "BEAN_SORT_BEAN_AGE": "Umur biji", + "GREEN_BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "Semua entri yang ditemukan dalam daftar excel, telah diubah menjadi entrti green beans baru, selamat meroasting!", "PREPARATION_TYPE_SANREMO_YOU": "", "PREPARATION_TYPE_XENIA": "Xenia", - "BEAN_POPUP_YOU_DONT_SEE_EVERYTHING_DESCRIPTION": "", - "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS": "", - "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_DESCRIPTION": "", - "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_RANGE": "", - "PAGE_SETTINGS_LANGUAGE_DUTCH": "", - "DOWNLOAD_IMPORT_EXCEL_TEMPLATES": "", - "SHOW_GRAPH": "", + "BEAN_POPUP_YOU_DONT_SEE_EVERYTHING_DESCRIPTION": "Anda menambahkan kopi yang berisi informasi varietas, tetapi parameter tersebut tidak diaktifkan. Apakah Anda ingin mengaktifkannya sekarang?", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS": "Tentukan sumbu untuk grafik tekanan", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_DESCRIPTION": "Atur ukuran awal dan akhir sumbu untuk tekanan", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_RANGE": "Sumbu untuk tekanan", + "PAGE_SETTINGS_LANGUAGE_DUTCH": "Belanda", + "DOWNLOAD_IMPORT_EXCEL_TEMPLATES": "Unduh templat impor", + "SHOW_GRAPH": "Tampilkan grafik", "UPDATE_TEXT_TITLE_TITLE": { "7.5.0": { - "TITLE": "", + "TITLE": "Versi 7.5.0: Apa yang baru?", "DESCRIPTION": [ + "Bahasa baru<\/b>", + "Dukungan bahasa Belanda - Terima kasih kepada Ygg", "", + "Seduhan<\/b>", + "Sekarang dimungkinkan untuk menutup daftar seduhan", + "Menampilkan jika biji kopi beku digunakan", + "Foto biji kopi sekarang terlihat pada pilihan", + "Membaca dan mengimpor riwayat penyeduhan dari Meticulous", + "Entri daftar brew sekarang dapat digeser ketika memiliki grafik", "", + "Beans<\/b>", + "Sekarang dimungkinkan untuk menutup daftar kopi", + "Kopi sekarang dapat diurutkan berdasarkan umur", + "Penyortiran tidak ter-reset lagi saat mengarsipkan kacang", "", + "Green beans<\/b>", + "Green bean sekarang dapat diimpor - Lihat Pengaturan", "", + "Pengaturan<\/b>", + "Atur sumbu awal untuk tekanan", + "Impor green beans atau kopi matang", "", + "Kerusakan data terdeteksi<\/b>", + "Beanconqueror sekarang memeriksa apakah ada kerusakan data yang terjadi, dan jika ya, ini akan menampilkan popup untuk memungkinkan impor cadangan", "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "" + "Lainnya<\/b>", + "Memperbaiki penyortiran alat seduh", + "Memperbaiki perhitungan (masalah pembulatan) biji kopi beku", + "Beberapa perubahan kecil dalam kode", + "Perubahan kecil" ] } } diff --git a/src/assets/i18n/it.json b/src/assets/i18n/it.json index 4251787f..2fba94e4 100644 --- a/src/assets/i18n/it.json +++ b/src/assets/i18n/it.json @@ -1334,59 +1334,59 @@ "IGNORE_NEGATIVE_VALUES_DESCRIPTION": "Attivando questa opzione, il grafico avrà un secondo di ritardo", "IGNORE_ANOMALY_VALUES_DESCRIPTION": "Attivando questa opzione, il grafico avrà un secondo di ritardo", "SAVE_LOGFILES_TO_NOTES_FROM_MACHINE": "Salva i log della macchina al salvataggio della preparazione", - "IMPORT_SHOT_FROM_METICULOUS": "", - "BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "", - "IMPORT_UNSUCCESSFULLY": "", - "BEAN_LIST": "", - "IMPORT_ROASTED_BEANS_EXCEL": "", - "IMPORT_GREEN_BEANS_EXCEL": "", - "BEANS_IMPORTED_UNSUCCESSFULLY_WRONG_EXCELFILE": "", - "OK": "", - "BEAN_SORT_BEAN_AGE": "", - "GREEN_BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "", + "IMPORT_SHOT_FROM_METICULOUS": "Importa un'estrazione", + "BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "Tutte le voci dell'excel, sono state importate, buona preparazione!", + "IMPORT_UNSUCCESSFULLY": "Importazione fallita", + "BEAN_LIST": "Lista caffè", + "IMPORT_ROASTED_BEANS_EXCEL": "Importa caffè tostato tramite Excel ", + "IMPORT_GREEN_BEANS_EXCEL": "Importa verde tramite Excel", + "BEANS_IMPORTED_UNSUCCESSFULLY_WRONG_EXCELFILE": "Sembra che il file excel scelto sia corrotto, abbia dati sbagliati, sia strutturato male o non sia quello giusto. Non è stato possibile importare alcun caffè.", + "OK": "Ok", + "BEAN_SORT_BEAN_AGE": "Giorni dalla tostatura", + "GREEN_BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "Tutte le voci dell'excel, sono state importate, buona tostatura!", "PREPARATION_TYPE_SANREMO_YOU": "", - "PREPARATION_TYPE_XENIA": "", - "BEAN_POPUP_YOU_DONT_SEE_EVERYTHING_DESCRIPTION": "", - "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS": "", - "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_DESCRIPTION": "", - "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_RANGE": "", - "PAGE_SETTINGS_LANGUAGE_DUTCH": "", - "DOWNLOAD_IMPORT_EXCEL_TEMPLATES": "", - "SHOW_GRAPH": "", + "PREPARATION_TYPE_XENIA": "Xenia", + "BEAN_POPUP_YOU_DONT_SEE_EVERYTHING_DESCRIPTION": "Si sta aggiungendo un caffè che contiene informazioni sulla varietà, ma questi parametri non sono attivi. Vuoi attivarla?", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS": "Imposta gli assi per il grafico della pressione", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_DESCRIPTION": "Imposta la scala degli assi per l'asse della pressione", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_RANGE": "Asse pressione", + "PAGE_SETTINGS_LANGUAGE_DUTCH": "Nederlands", + "DOWNLOAD_IMPORT_EXCEL_TEMPLATES": "Scarica i template di importazione", + "SHOW_GRAPH": "Mostra grafico", "UPDATE_TEXT_TITLE_TITLE": { "7.5.0": { - "TITLE": "", + "TITLE": "Versione 7.5.0: Novità", "DESCRIPTION": [ + "Nuova lingua<\/b>", + "Supporto dell'olandese - Grazie a Ygg", "", + "Preparazioni<\/b>", + "Ora è possibile comprimere la lista delle preparazioni", + "È possibile mostrare se è stato usato un caffè congelato", + "L'immagine del caffè è visibile alla selezione", + "È possibile leggere e importare la cronologia delle preparazioni di una Meticulous", + "È possibile fare swipe sugli elementi della lista preparazioni per mostrare il grafico, se presente", "", + "Caffè<\/b>", + "Ora è possibile comprimere la lista dei caffè", + "È possibile ordinare i caffè per età", + "L'ordinamento in lista non viene più ripristinato quando si archivia un caffè", "", + "Verde<\/b>", + "È ora possibile importare il verde in Impostazioni", "", + "Impostazioni<\/b>", + "Imposta l'asse di partenza per la pressione", + "Importa verde o caffè tostati", "", + "Rilevamento della corruzione dei dati<\/b>", + "Beanconqueror ora controlla se in qualche modo si è verificato una corruzione dei dati mostrando un popup per importare di un backup", "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "" + "Altro<\/b>", + "Correzione dell'ordinamento degli strumenti di preparazione", + "Correzione del calcolo sui caffè congelati (problemi di arrotondamento)", + "Piccole migliorie nel codice", + "Migliorie minori" ] } } diff --git a/src/assets/i18n/pl.json b/src/assets/i18n/pl.json index fe84cc33..72041c94 100644 --- a/src/assets/i18n/pl.json +++ b/src/assets/i18n/pl.json @@ -1334,59 +1334,59 @@ "IGNORE_NEGATIVE_VALUES_DESCRIPTION": "Aktywacja tej opcji spowoduje, że wykres będzie opóźniony o jedną sekundę.", "IGNORE_ANOMALY_VALUES_DESCRIPTION": "Aktywacja tej opcji spowoduje, że wykres będzie opóźniony o jedną sekundę.", "SAVE_LOGFILES_TO_NOTES_FROM_MACHINE": "Zapisywanie dzienników urządzenia podczas zapisywania parzenia", - "IMPORT_SHOT_FROM_METICULOUS": "", - "BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "", - "IMPORT_UNSUCCESSFULLY": "", - "BEAN_LIST": "", - "IMPORT_ROASTED_BEANS_EXCEL": "", - "IMPORT_GREEN_BEANS_EXCEL": "", - "BEANS_IMPORTED_UNSUCCESSFULLY_WRONG_EXCELFILE": "", - "OK": "", - "BEAN_SORT_BEAN_AGE": "", - "GREEN_BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "", + "IMPORT_SHOT_FROM_METICULOUS": "Importuj strzał", + "BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "Wszystkie znalezione wpisy na liście w pliku Excel zostały przekształcone w nowe ziarna. Ciesz się smacznym naparem!", + "IMPORT_UNSUCCESSFULLY": "Import nie powiódł się", + "BEAN_LIST": "Lista ziaren", + "IMPORT_ROASTED_BEANS_EXCEL": "Importuj wyrażone ziarna z pliku Excel", + "IMPORT_GREEN_BEANS_EXCEL": "Importuj zielone ziarna z pliku Excel", + "BEANS_IMPORTED_UNSUCCESSFULLY_WRONG_EXCELFILE": "Wygląda na to, że wybrany plik Excel jest uszkodzony, ma złe dane, jest źle ustrukturyzowany lub został źle wybrany. Z tego powodu nie można było zaimportować żadnych ziaren.", + "OK": "Ok", + "BEAN_SORT_BEAN_AGE": "Wiek ziarna", + "GREEN_BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "Wszystkie znalezione pozycje na liście excel, zostały przekształcone w nowe zielone ziarna, mają świetne pieczenie!", "PREPARATION_TYPE_SANREMO_YOU": "", - "PREPARATION_TYPE_XENIA": "", - "BEAN_POPUP_YOU_DONT_SEE_EVERYTHING_DESCRIPTION": "", - "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS": "", - "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_DESCRIPTION": "", - "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_RANGE": "", - "PAGE_SETTINGS_LANGUAGE_DUTCH": "", - "DOWNLOAD_IMPORT_EXCEL_TEMPLATES": "", - "SHOW_GRAPH": "", + "PREPARATION_TYPE_XENIA": "Xenia", + "BEAN_POPUP_YOU_DONT_SEE_EVERYTHING_DESCRIPTION": "Dodajesz ziarno, które zawiera informacje o odmianie, ale te parametry nie są aktywowane. Czy chcesz je teraz aktywować?", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS": "Zdefiniuj osie wykresu ciśnienia", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_DESCRIPTION": "Ustaw początkowy i końcowy rozmiar osi dla ciśnienia", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_RANGE": "Osie ciśnienia", + "PAGE_SETTINGS_LANGUAGE_DUTCH": "Holenderski", + "DOWNLOAD_IMPORT_EXCEL_TEMPLATES": "Pobierz szablony importu", + "SHOW_GRAPH": "Pokaż wykres", "UPDATE_TEXT_TITLE_TITLE": { "7.5.0": { - "TITLE": "", + "TITLE": "Wersja 7.5.0: Co nowego", "DESCRIPTION": [ + "Nowy język<\/b>", + "Wsparcie języka holenderskiego - podziękowania dla Ygg", "", + "Napary<\/b>", + "Zwinięcie listy naparów jest teraz możliwe", + "Wyświetlanie, czy użyto zamrożonego ziarna", + "Obraz ziarna jest teraz widoczny po zaznaczeniu.", + "Odczytywanie i importowanie ujęcia historii z Meticulous", + "Wpisy na liście na parów można teraz przesuwać, jeśli mają wykres", "", + "Ziarna<\/b>", + "Zwijanie listy fasoli jest teraz możliwe", + "Ziarna można teraz sortować według ich wieku", + "Sortowanie nie jest już resetowane podczas archiwizowania ziarna", "", + "Zielone ziarna<\/b>", + "Zielone ziarna można teraz importować - patrz Ustawienia", "", + "Ustawienia<\/b>", + "Ustaw oś początkową dla ciśnienia", + "Importuj zielone ziarna lub wypalone", "", + "Wykrywanie uszkodzeń danych<\/b>", + "Beanconqueror sprawdza teraz, czy w jakiś sposób nie doszło do uszkodzenia danych i jeśli tak, wyświetla okno podręczne umożliwiające zaimportowanie kopii zapasowej", "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "" + "Inne<\/b>", + "Naprawianie sortowania narzędzi przygotowawczych", + "Naprawa obliczeń zamrożonej fasoli w celu usunięcia problemów z zaokrąglaniem", + "Kilka zmian technicznych w kodzie", + "Drobne poprawki" ] } } diff --git a/src/assets/i18n/tr.json b/src/assets/i18n/tr.json index a1b46f71..8ae50306 100644 --- a/src/assets/i18n/tr.json +++ b/src/assets/i18n/tr.json @@ -1334,59 +1334,59 @@ "IGNORE_NEGATIVE_VALUES_DESCRIPTION": "Bunu etkinleştirdiğinizde bir saniye geride olacak bir grafik ortaya çıkar", "IGNORE_ANOMALY_VALUES_DESCRIPTION": "Bunu etkinleştirdiğinizde bir saniye geride olacak bir grafik ortaya çıkar", "SAVE_LOGFILES_TO_NOTES_FROM_MACHINE": "Bir demlemeyi kaydederken makine günlüklerini kaydedin", - "IMPORT_SHOT_FROM_METICULOUS": "", - "BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "", - "IMPORT_UNSUCCESSFULLY": "", - "BEAN_LIST": "", - "IMPORT_ROASTED_BEANS_EXCEL": "", - "IMPORT_GREEN_BEANS_EXCEL": "", - "BEANS_IMPORTED_UNSUCCESSFULLY_WRONG_EXCELFILE": "", - "OK": "", - "BEAN_SORT_BEAN_AGE": "", - "GREEN_BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "", + "IMPORT_SHOT_FROM_METICULOUS": "Çekimi içe aktar", + "BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "Excel listesinde bulunan tüm girişler yeni çekirdeklere dönüştürüldü, lezzetli bir demlemenin tadını çıkarın!", + "IMPORT_UNSUCCESSFULLY": "İçe aktarma başarısız oldu", + "BEAN_LIST": "Çekirdek listesi", + "IMPORT_ROASTED_BEANS_EXCEL": "Kavrulmuş çekirdekleri excel ile içe aktarın", + "IMPORT_GREEN_BEANS_EXCEL": "Yeşil çekirdekleri excel ile içe aktarın", + "BEANS_IMPORTED_UNSUCCESSFULLY_WRONG_EXCELFILE": "Seçilen excel dosyasının bozuk, yanlış veri içerdiği, yanlış yapılandırılmış olduğu veya yanlış seçildiği anlaşılıyor. Bu nedenle hiçbir çekirdek içe aktarılamadı.", + "OK": "Tamam", + "BEAN_SORT_BEAN_AGE": "Çekirdek yaşı", + "GREEN_BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "Excel listesinde bulunan tüm girdiler yeni yeşil çekirdeklere dönüştürüldü, harika kavrulmuşlar!", "PREPARATION_TYPE_SANREMO_YOU": "", "PREPARATION_TYPE_XENIA": "Xenia", - "BEAN_POPUP_YOU_DONT_SEE_EVERYTHING_DESCRIPTION": "", - "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS": "", - "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_DESCRIPTION": "", - "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_RANGE": "", - "PAGE_SETTINGS_LANGUAGE_DUTCH": "", - "DOWNLOAD_IMPORT_EXCEL_TEMPLATES": "", - "SHOW_GRAPH": "", + "BEAN_POPUP_YOU_DONT_SEE_EVERYTHING_DESCRIPTION": "İçinde çeşitlilik bilgisi olan bir çekirdek ekliyorsunuz, ancak bu parametreler etkinleştirilmemiş. Bunları şimdi etkinleştirmek ister misiniz?", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS": "Basınç grafiği için eksenleri tanımlayın", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_DESCRIPTION": "Basınç için eksenlerin başlangıç ve bitiş boyutunu ayarlayın", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_RANGE": "Basınç eksenleri", + "PAGE_SETTINGS_LANGUAGE_DUTCH": "Hollandaca", + "DOWNLOAD_IMPORT_EXCEL_TEMPLATES": "İçe aktarma şablonlarını indirin", + "SHOW_GRAPH": "Grafiği göster", "UPDATE_TEXT_TITLE_TITLE": { "7.5.0": { - "TITLE": "", + "TITLE": "Sürüm 7.5.0: Yenilikler", "DESCRIPTION": [ + "Yeni dil<\/b>", + "Hollandaca desteği için - Ygg'ye teşekkürler", "", + "Demlemeler<\/b>", + "Bir demleme listesini daraltmak artık mümkün", + "Dondurulmuş çekirdeğin kullanılıp kullanılmadığının görüntülenmesi", + "Çekirdek görüntüsü artık seçimde görünür", + "Meticulous'tan bir geçmiş görüntüsünü okuyun ve içe aktarın", + "Demleme listesi girişleri artık grafikleri olduğunda kaydırılabilir", "", + "Çekirdekler<\/b>", + "Çekirdek listesini daraltmak artık mümkün", + "Çekirdekler artık çekirdek yaşına göre sıralanabiliyor", + "Bir çekirdeği arşivlerken sıralama artık sıfırlanmıyor", "", + "Yeşil çekirdek<\/b>", + "Yeşil çekirdek artık içe aktarılabilir - Ayarlara Bakın", "", + " Ayarlar <\/b>", + "Basınç için başlangıç eksenini ayarlayın", + "Yeşil çekirdek veya kavrulmuş çekirdek içe aktarın", "", + "Veri bozulması tespiti<\/b>", + "Beanconqueror şimdi bir şekilde bir veri bozulması olup olmadığını kontrol ediyor ve evet ise, bir yedeği içe aktarmayı mümkün kılmak için size bir açılır pencere gösteriyor", "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "" + "Diğerleri<\/b>", + "Hazırlık araçlarının sıralamasının düzeltilmesi", + "Yuvarlama sorunlarını gidermek için dondurulmuş çekirdek hesaplamasının düzeltilmesi", + "Koddaki bazı teknik değişiklikler", + "Küçük değişiklikler" ] } } diff --git a/src/assets/i18n/zh.json b/src/assets/i18n/zh.json index 70ec6b44..dd248ec4 100644 --- a/src/assets/i18n/zh.json +++ b/src/assets/i18n/zh.json @@ -1334,59 +1334,59 @@ "IGNORE_NEGATIVE_VALUES_DESCRIPTION": "激活此功能后,图表将落后一秒", "IGNORE_ANOMALY_VALUES_DESCRIPTION": "激活此功能后,图表将落后一秒", "SAVE_LOGFILES_TO_NOTES_FROM_MACHINE": "保存冲泡时保存机器日志", - "IMPORT_SHOT_FROM_METICULOUS": "", - "BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "", - "IMPORT_UNSUCCESSFULLY": "", - "BEAN_LIST": "", - "IMPORT_ROASTED_BEANS_EXCEL": "", - "IMPORT_GREEN_BEANS_EXCEL": "", - "BEANS_IMPORTED_UNSUCCESSFULLY_WRONG_EXCELFILE": "", - "OK": "", - "BEAN_SORT_BEAN_AGE": "", - "GREEN_BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "", + "IMPORT_SHOT_FROM_METICULOUS": "导入一个冲泡记录", + "BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "所有在excel列表中找到的条目,都被转换成新的豆子,享受美味的冲泡!", + "IMPORT_UNSUCCESSFULLY": "导入不成功", + "BEAN_LIST": "豆子列表", + "IMPORT_ROASTED_BEANS_EXCEL": "通过excel导入已经烘焙好的豆子", + "IMPORT_GREEN_BEANS_EXCEL": "通过excel导入生豆", + "BEANS_IMPORTED_UNSUCCESSFULLY_WRONG_EXCELFILE": "看起来所选择的excel文件是损坏的,有错误的数据,错误的结构,或者是错误的选择。因此,没有豆子可以导入。", + "OK": "可以", + "BEAN_SORT_BEAN_AGE": "豆龄", + "GREEN_BEANS_IMPORTED_SUCCESSFULLY_DESCRIPTION": "所有在excel列表中找到的条目,已经转化为新的生豆,祝你烘焙成功!", "PREPARATION_TYPE_SANREMO_YOU": "", "PREPARATION_TYPE_XENIA": "Xenia", - "BEAN_POPUP_YOU_DONT_SEE_EVERYTHING_DESCRIPTION": "", - "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS": "", - "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_DESCRIPTION": "", - "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_RANGE": "", - "PAGE_SETTINGS_LANGUAGE_DUTCH": "", - "DOWNLOAD_IMPORT_EXCEL_TEMPLATES": "", - "SHOW_GRAPH": "", + "BEAN_POPUP_YOU_DONT_SEE_EVERYTHING_DESCRIPTION": "您正在添加一个包含各种信息的豆子,但是这些参数没有被激活。你想现在激活它们吗?", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS": "定义压力图表的坐标轴", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_DESCRIPTION": "设置坐标轴的起始大小和末端的压力", + "PAGE_SETTINGS_TAB_BLUETOOTH_PRESSURE_GRAPHS_AXIS_RANGE": "压力坐标轴", + "PAGE_SETTINGS_LANGUAGE_DUTCH": "荷兰语", + "DOWNLOAD_IMPORT_EXCEL_TEMPLATES": "下载导入模板", + "SHOW_GRAPH": "显示图表", "UPDATE_TEXT_TITLE_TITLE": { "7.5.0": { - "TITLE": "", + "TITLE": "版本7.5.0:更新了什么", "DESCRIPTION": [ - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "" + "新的语言<\/b>", + "荷兰语的支持-感谢Ygg", + "空的", + "冲泡<\/b>", + "现在可以折叠冲泡列表", + "显示是否使用了冷冻豆子", + "豆子的图像现在在选择时可见", + "读取并导入从“Meticulous”冲泡历史", + "当冲煮列表有图表时,它们的条目可以滑动", + "空的", + "豆子<\/b>", + "现在可以折叠豆子列表了", + "豆子现在是按豆龄分类的", + "归档豆子时不再重置排序", + "空的", + "生豆<\/b>", + "生豆现在是可导入的-见设置", + "空的", + "设置<\/b>", + "设置压力的起始轴", + "导入生豆或者烘焙过的豆子", + "空的", + "数据损坏检测<\/b>", + "Beanconqueror现在会检查是否发生了数据损坏,如果是,它会向您显示一个弹出窗口,以便导入备份", + "空的", + "其他<\/b>", + "修复准备工具的排序", + "修复冷冻豆子的计算以解决舍入问题", + "代码中的一些技术修改", + "小调整" ] } } From 978ed4658e39d701b0fb6379aa0bac9124316c9a Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Sat, 14 Sep 2024 17:14:55 +0200 Subject: [PATCH 52/55] Fixing styleLightContent for Android --- src/app/app.component.ts | 5 +++++ .../preparationDevice/sanremo/sanremoYOUDevice.ts | 13 ------------- .../brew-brewing-graph.component.ts | 3 +-- 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index fbf8c586..7fa3df09 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -329,6 +329,11 @@ export class AppComponent implements AfterViewInit { // #7 this.statusBar.show(); this.statusBar.styleDefault(); + if (this.platform.is('android')) { + try { + this.statusBar.styleLightContent(); + } catch (ex) {} + } this.keyboard.hideFormAccessoryBar(false); if (environment.production === true) { diff --git a/src/classes/preparationDevice/sanremo/sanremoYOUDevice.ts b/src/classes/preparationDevice/sanremo/sanremoYOUDevice.ts index 0cb02307..06209222 100644 --- a/src/classes/preparationDevice/sanremo/sanremoYOUDevice.ts +++ b/src/classes/preparationDevice/sanremo/sanremoYOUDevice.ts @@ -176,19 +176,6 @@ export class SanremoYOUDevice extends PreparationDevice { urlAdding = '/api/action/p3'; } - if (_mode !== SanremoYOUMode.MANUAL_CONTROLLING) { - await new Promise((resolveIntern) => { - this.fetchRuntimeData(() => { - resolveIntern(undefined); - }); - }); - if (this.statusPhase === 0) { - //Machine has already stoped, skipp. - reject(undefined); - return; - } - } - cordova.plugin.http.sendRequest( this.getPreparation().connectedPreparationDevice.url + urlAdding, options, diff --git a/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts b/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts index 6f0fbf62..ad9faba4 100644 --- a/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts +++ b/src/components/brews/brew-brewing-graph/brew-brewing-graph.component.ts @@ -1191,8 +1191,7 @@ export class BrewBrewingGraphComponent implements OnInit { public drawTargetWeight(_targetWeight: number) { if ( this.brewComponent?.brewBrewingPreparationDeviceEl?.getPreparationDeviceType() === - PreparationDeviceType.SANREMO_YOU || - true + PreparationDeviceType.SANREMO_YOU ) { if (!('shapes' in this.lastChartLayout)) { this.lastChartLayout['shapes'] = []; From 21543cb356adfca1319d1df2786341598d74d22b Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Sat, 14 Sep 2024 17:16:29 +0200 Subject: [PATCH 53/55] Latest Version 7.5.0 --- config.xml | 2 +- src/classes/version/iVersion.ts | 2 +- src/services/uiUpdate.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config.xml b/config.xml index cbc79bf9..0fa6ab0b 100644 --- a/config.xml +++ b/config.xml @@ -1,5 +1,5 @@ - + Beanconqueror Lars Saalbach diff --git a/src/classes/version/iVersion.ts b/src/classes/version/iVersion.ts index 79b29aa4..0611f9c7 100755 --- a/src/classes/version/iVersion.ts +++ b/src/classes/version/iVersion.ts @@ -60,7 +60,7 @@ export class Version implements IVersion { * We dont set this to a variable, else it would be stored in DB and wrongly overwritten */ private getUpdatedVersions() { - return ['7.4.0']; + return ['7.50']; } private versionCompare(_actualAppVersion, _updateVersion) { diff --git a/src/services/uiUpdate.ts b/src/services/uiUpdate.ts index 60af4c65..c91619a2 100755 --- a/src/services/uiUpdate.ts +++ b/src/services/uiUpdate.ts @@ -614,7 +614,7 @@ export class UIUpdate { versionCode = await this.appVersion.getVersionNumber(); } else { // Hardcored for testing - versionCode = '7.4.0'; + versionCode = '7.5.0'; } const version: Version = this.uiVersionStorage.getVersion(); const displayingVersions = From bd42c5f470649d4e4f0c4f38843517408bc23a08 Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Sat, 14 Sep 2024 17:18:56 +0200 Subject: [PATCH 54/55] typo --- src/classes/version/iVersion.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/classes/version/iVersion.ts b/src/classes/version/iVersion.ts index 0611f9c7..7acefb50 100755 --- a/src/classes/version/iVersion.ts +++ b/src/classes/version/iVersion.ts @@ -60,7 +60,7 @@ export class Version implements IVersion { * We dont set this to a variable, else it would be stored in DB and wrongly overwritten */ private getUpdatedVersions() { - return ['7.50']; + return ['7.5.0']; } private versionCompare(_actualAppVersion, _updateVersion) { From 33abcc6cd3c5746576a274f64251ef36587c913c Mon Sep 17 00:00:00 2001 From: Lars Saalbach Date: Sat, 14 Sep 2024 20:55:37 +0200 Subject: [PATCH 55/55] typo --- src/assets/i18n/de.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json index 77f5fe05..5ec30d24 100644 --- a/src/assets/i18n/de.json +++ b/src/assets/i18n/de.json @@ -1357,7 +1357,7 @@ "7.5.0": { "TITLE": "Version 7.5.0: Das ist neu", "DESCRIPTION": [ - " Neue Sprache <\/b>", + "Neue Sprache<\/b>", "Unterstützung von Niederländisch - Danke an Ygg", "", "Brühungen<\/b>", @@ -1390,4 +1390,4 @@ ] } } -} \ No newline at end of file +}