Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/app/brew/brew-flow/brew-flow.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,8 @@ export class BrewFlowComponent implements OnDestroy, OnInit {
this.ngZone.runOutsideAngular(() => {
if (this.temperatureDetail?.nativeElement) {
const temperatureEl = this.temperatureDetail.nativeElement;
temperatureEl.textContent = _val.temperature;
const temperatureFormatted = _val.temperature.toFixed(2);
temperatureEl.textContent = temperatureFormatted;
}
});
}
Expand Down
9 changes: 7 additions & 2 deletions src/app/settings/settings.page.html
Original file line number Diff line number Diff line change
Expand Up @@ -1678,15 +1678,20 @@ <h2>{{ "TEMPERATURE.LOG" | translate }}</h2>
</div>
</ion-checkbox>
</ion-item>

<ion-item>
<ion-checkbox label-placement='start' (ngModelChange)="saveSettings();" [(ngModel)]="settings.temperature_show_before_timer">
<div class='ion-label' style="white-space: normal;">
<h2>{{"TEMPERATURE.SHOW_BEFORE_TIMER" | translate}}</h2>
</div>
</ion-checkbox>
</ion-item>
</ion-list>

<ion-label>
<ion-icon style="top: 3px; position: relative;" name="information-circle-outline"></ion-icon>
<p style="display: inline;">{{ "TEMPERATURE.INFORMATION_DESCRIPTION" | translate }}</p>
</ion-label>
</ion-card-content>

</ion-card>
}
@case ('bluetoothrefractometer') {
Expand Down
2 changes: 2 additions & 0 deletions src/app/settings/settings.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ import { UIUpdate } from '../../services/uiUpdate';
import { UiVersionStorage } from '../../services/uiVersionStorage';
import { UIWaterStorage } from '../../services/uiWaterStorage';
import { VisualizerService } from '../../services/visualizerService/visualizer-service.service';
import { TemperatureSource } from 'src/classes/devices/temperatureBluetoothDevice';

@Component({
selector: 'settings',
Expand Down Expand Up @@ -385,6 +386,7 @@ export class SettingsPage {
if (disconnected) {
this.settings.temperature_id = '';
this.settings.temperature_type = null;
this.settings.temperature_graph_source = TemperatureSource.WATER_PROBE;
}
} else if (_type === BluetoothTypes.TDS) {
this.eventQueue.dispatch(
Expand Down
3 changes: 2 additions & 1 deletion src/assets/i18n/ar.json
Original file line number Diff line number Diff line change
Expand Up @@ -1535,7 +1535,8 @@
},
"LOG": "",
"THRESHOLD_ACTIVE": "",
"THRESHOLD_TEMP": ""
"THRESHOLD_TEMP": "",
"SHOW_BEFORE_TIMER": ""
},
"POPOVER_BLUETOOTH_ACTION_RECONNECT_TEMPERATURE_DEVICE": "",
"PRESSURE_DEVICE_JUST_VISIBLE_ON_ESPRESSO": "",
Expand Down
3 changes: 2 additions & 1 deletion src/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1187,7 +1187,8 @@
},
"LOG": "Activate log files for temperature device",
"THRESHOLD_ACTIVE": "Start timer with predefined temperature",
"THRESHOLD_TEMP": "Threshold temperature"
"THRESHOLD_TEMP": "Threshold temperature",
"SHOW_BEFORE_TIMER": "Show temperature before timer starts"
},
"POPOVER_BLUETOOTH_ACTION_RECONNECT_TEMPERATURE_DEVICE": "Reconnect temperature device",
"PRESSURE_DEVICE_JUST_VISIBLE_ON_ESPRESSO": "Pressure device is only usable with the preparation style 'Espresso'",
Expand Down
4 changes: 4 additions & 0 deletions src/classes/brew/brewFlow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ export class BrewFlow {
public temperatureFlow: Array<IBrewTemperatureFlow>;
public waterDispensed: Array<IBrewWaterDispensedFlow>;
public waterDispensedFlowSecond: Array<IBrewWaterDispensedFlow>;
public basketTemperatureFlow: Array<IBrewTemperatureFlow>;
public targetTemperatureFlow: Array<IBrewTemperatureFlow>;
public brewbyweight: Array<IBrewByWeight>;
public customMetrics: { [key: string]: Array<IBrewCustomMetric> };
public customAxes: Array<IBrewCustomAxis>;
Expand All @@ -22,6 +24,8 @@ export class BrewFlow {
this.temperatureFlow = [];
this.waterDispensed = [];
this.waterDispensedFlowSecond = [];
this.basketTemperatureFlow = [];
this.targetTemperatureFlow = [];
this.brewbyweight = [];
this.customMetrics = {};
this.customAxes = [];
Expand Down
113 changes: 101 additions & 12 deletions src/classes/devices/argosThermometer.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,44 @@
import { PeripheralData } from './ble.types';
import { Logger } from './common/logger';
import { TemperatureDevice } from './temperatureBluetoothDevice';
import {
TemperatureDevice,
TemperatureSource,
} from './temperatureBluetoothDevice';

declare var ble: any;

export class ArgosThermometer extends TemperatureDevice {
public static DEVICE_NAME = 'ARGOS';

// Should Argos be a seperate preperationDevice, so we can track all these?
// - setpoint would be handy to set on the shot as it doesn't change
// - group head & boiler could be tracked in graphs
// - how would these line up to Visualizer fields of target temp goal, temp basket, temp mix etc
public static TEMPERATURE_SERVICE_UUID =
'6a521c59-55b5-4384-85c0-6534e63fb09e';
public static TEMPERATURE_CHAR_UUID = '6a521c62-55b5-4384-85c0-6534e63fb09e';
public static TEMPERATURE_SETPOINT_CHAR_UUID =
'6a521c60-55b5-4384-85c0-6534e63fb09e';
public static TEMPERATURE_GROUPHEAD_CHAR_UUID =
'6a521c62-55b5-4384-85c0-6534e63fb09e';
public static TEMPERATURE_BOILER_CURRENT_CHAR_UUID =
'6a521c61-55b5-4384-85c0-6534e63fb09e';
public static TEMPERATURE_BOILER_TARGET_CHAR_UUID =
'6a521c66-55b5-4384-85c0-6534e63fb09e';

private logger: Logger;
private state: {
setPoint: number;
boilerTarget: number;
};

constructor(data: PeripheralData) {
super(data);
super(data, TemperatureSource.SET_POINT);
this.connect();
this.logger = new Logger('ArgosTemperatureSensor');
this.state = {
setPoint: 0,
boilerTarget: 0,
};
}

public static test(device: any): boolean {
Expand All @@ -37,40 +60,106 @@ export class ArgosThermometer extends TemperatureDevice {
}

private attachNotification() {
this.attachArgosNotification(
ArgosThermometer.TEMPERATURE_SETPOINT_CHAR_UUID,
);
this.attachArgosNotification(
ArgosThermometer.TEMPERATURE_BOILER_CURRENT_CHAR_UUID,
);
this.attachArgosNotification(
ArgosThermometer.TEMPERATURE_BOILER_TARGET_CHAR_UUID,
);
}

private attachArgosNotification(characteristic: string) {
ble.startNotification(
this.device_id,
ArgosThermometer.TEMPERATURE_SERVICE_UUID,
ArgosThermometer.TEMPERATURE_CHAR_UUID,
characteristic,

async (_data: any) => {
const rawData = _data; //new Uint8Array(_data.slice(0, -1));
this.parseStatusUpdate(rawData);
this.parseStatusUpdate(_data, characteristic);
},

(_data: any) => {},
);
}

private parseStatusUpdate(temperatureRawStatus: any) {
public getDefaultTempeatureSource() {
return TemperatureSource.BASKET_PROBE;
}

private parseStatusUpdate(
temperatureRawStatus: ArrayBuffer,
characteristic: string,
) {
this.logger.log(
'temperatureRawStatus received is: ' + temperatureRawStatus,
);

const temperatureDataview = new DataView(temperatureRawStatus);
const temperature = temperatureDataview.getFloat64(0, true);

const formatNumber = new Intl.NumberFormat(undefined, {
minimumIntegerDigits: 2,
}).format;

const setPoint =
((temperatureRawStatus.getUint16(5, true) / 127) * 5) / 9 - 32; // Convert from F to C
const data = formatNumber(setPoint);
const data = Number(formatNumber(temperature));

this.setTemperature(Number(data), temperatureRawStatus);
// set temperature on the correct source
switch (characteristic) {
case ArgosThermometer.TEMPERATURE_SETPOINT_CHAR_UUID:
this.state.setPoint = data;
this.setTemperature(
data,
temperatureDataview,
TemperatureSource.SET_POINT,
);
break;
case ArgosThermometer.TEMPERATURE_BOILER_CURRENT_CHAR_UUID:
if (this.state.setPoint != 0 && this.state.boilerTarget != 0) {
// calculate boiler error and approx water temp
const boilerError = data - this.state.boilerTarget;
const approxWaterTemp = Math.min(
this.state.setPoint + 0.5 * boilerError,
data,
);
this.setTemperature(
Number(formatNumber(approxWaterTemp)),
temperatureDataview,
TemperatureSource.BASKET_PROBE,
);
}
// send raw boiler temp
this.setTemperature(
data,
temperatureDataview,
TemperatureSource.WATER_PROBE,
);
break;
case ArgosThermometer.TEMPERATURE_BOILER_TARGET_CHAR_UUID:
this.state.boilerTarget = data;
break;
}
}

private deattachNotification() {
this.detachArgosNotification(
ArgosThermometer.TEMPERATURE_SETPOINT_CHAR_UUID,
);
this.detachArgosNotification(
ArgosThermometer.TEMPERATURE_BOILER_CURRENT_CHAR_UUID,
);
this.detachArgosNotification(
ArgosThermometer.TEMPERATURE_BOILER_TARGET_CHAR_UUID,
);
}

private detachArgosNotification(characteristic: string) {
ble.stopNotification(
this.device_id,
ArgosThermometer.TEMPERATURE_SERVICE_UUID,
ArgosThermometer.TEMPERATURE_CHAR_UUID,
characteristic,
(e: any) => {},
(e: any) => {},
);
Expand Down
71 changes: 51 additions & 20 deletions src/classes/devices/temperatureBluetoothDevice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,16 @@ import { to128bitUUID } from './common/util';

declare var ble: any;

export enum TemperatureSource {
SET_POINT = 'SetPoint', // set point or goal for the shot
WATER_PROBE = 'WaterProbe', // last measured water temp before group (mix)
BASKET_PROBE = 'BasketProbe', // measured temp at group/basket, may or may not be in water path
}

export interface Temperature {
actual: number;
old: number;
source: TemperatureSource;
}

export interface TemperatureChangeEvent extends Temperature {
Expand All @@ -26,32 +33,49 @@ export abstract class TemperatureDevice {
public batteryLevel: number;
public temperatureChange: EventEmitter<TemperatureChangeEvent> =
new EventEmitter();
protected temperature: Temperature;
protected temperatures = new Map<TemperatureSource, number>();
protected temperatureParentLogger: Logger;

private defaultTemperatureSource;
private lastTemperatureSetTime: number = 0;

protected constructor(data: PeripheralData) {
protected constructor(
data: PeripheralData,
defaultTemperatureSource: TemperatureSource = TemperatureSource.WATER_PROBE,
) {
this.device_id = data.id;
try {
this.device_name = data.name;
} catch (ex) {}
this.temperature = {
actual: 0,
old: 0,
};
this.temperatureParentLogger = new Logger();
this.defaultTemperatureSource = defaultTemperatureSource;
}

public abstract connect(): void;
public abstract disconnect(): void;

public getTemperature() {
return this.temperature.actual;
public getDefaultTempeatureSource() {
return TemperatureSource.WATER_PROBE;
}

public getTemperature(
_source: TemperatureSource = this.defaultTemperatureSource,
) {
return this.dataFor(_source).actual;
}

public getOldTemperature() {
return this.temperature.old;
public getOldTemperature(
_source: TemperatureSource = this.defaultTemperatureSource,
) {
return this.dataFor(_source).old;
}

private dataFor(_source: TemperatureSource) {
return (this.temperatures[_source] ??= {
actual: 0,
old: 0,
source: _source,
lastSetTime: 0,
});
}

/**
Expand All @@ -78,35 +102,42 @@ export abstract class TemperatureDevice {
);
});
}

protected setTemperature(_newTemperature: number, _rawData: any) {
if (Date.now() - this.lastTemperatureSetTime < UPDATE_EVERY_MS) {
protected setTemperature(
_newTemperature: number,
_rawData: any,
_source: TemperatureSource = this.defaultTemperatureSource,
) {
const temperatureData = this.dataFor(_source);
if (Date.now() - temperatureData.lastSetTime < UPDATE_EVERY_MS) {
return;
}
this.lastTemperatureSetTime = Date.now();
temperatureData.lastSetTime = Date.now();

this.temperatureParentLogger.log(
'Bluetooth Temperature Device - New temperature recieved ' +
_newTemperature +
'- source ' +
_source +
' - raw data ' +
JSON.stringify(_rawData),
);

this.temperature.actual = _newTemperature;
temperatureData.actual = _newTemperature;
const actualDate = new Date();
try {
this.temperatureParentLogger.log(
'Bluetooth Pressure Device - Are subscriptions existing? ' +
'Bluetooth Temperature Device - Are subscriptions existing? ' +
this.temperatureChange?.observers?.length,
);
} catch (ex) {}
this.temperatureChange.emit({
actual: this.temperature.actual,
old: this.temperature.old,
actual: temperatureData.actual,
old: temperatureData.old,
date: actualDate,
source: temperatureData.source,
});

this.temperature.old = _newTemperature;
temperatureData.old = _newTemperature;
}
}

Expand Down
Loading