parent
aca1aeead4
commit
3eaf721ffd
3 changed files with 65 additions and 3 deletions
frontend/src
|
@ -55,7 +55,7 @@
|
||||||
{{ device.name }}
|
{{ device.name }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p class="battery" :style="{ color: batteryColor }" v-if="point.battery">
|
<p class="battery" :style="{ color: batteryColor ?? 'initial' }" v-if="point.battery">
|
||||||
<font-awesome-icon :icon="batteryIconClass" />
|
<font-awesome-icon :icon="batteryIconClass" />
|
||||||
<span>{{ point.battery }}%</span>
|
<span>{{ point.battery }}%</span>
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -21,6 +21,13 @@
|
||||||
:title="(showMetrics.speed ? 'Hide' : 'Show') + ' speed'">
|
:title="(showMetrics.speed ? 'Hide' : 'Show') + ' speed'">
|
||||||
<font-awesome-icon icon="tachometer-alt" />
|
<font-awesome-icon icon="tachometer-alt" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<button @click="toggleMetric('battery')"
|
||||||
|
:class="{ selected: showMetrics.battery }"
|
||||||
|
:title="(showMetrics.battery ? 'Hide' : 'Show') + ' battery level'"
|
||||||
|
v-if="hasBatteryInfo">
|
||||||
|
<font-awesome-icon icon="battery-full" />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="page-button-container">
|
<div class="page-button-container">
|
||||||
|
@ -128,12 +135,25 @@ export default {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
hasBatteryInfo(): boolean {
|
||||||
|
return this.points.some((point: GPSPoint) => point.battery != null)
|
||||||
|
},
|
||||||
|
|
||||||
|
battery(): (number | null)[] {
|
||||||
|
return this.points
|
||||||
|
.map((point: GPSPoint) => point.battery ?? null)
|
||||||
|
},
|
||||||
|
|
||||||
speed(): number[] {
|
speed(): number[] {
|
||||||
if (!this.points.length) {
|
if (!this.points.length) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.points.map((point: GPSPoint, index: number) => {
|
return this.points.map((point: GPSPoint, index: number) => {
|
||||||
|
if (point.speed != null) {
|
||||||
|
return point.speed
|
||||||
|
}
|
||||||
|
|
||||||
if (index === 0) {
|
if (index === 0) {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
@ -185,6 +205,19 @@ export default {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.showMetrics.battery) {
|
||||||
|
datasets.push(
|
||||||
|
{
|
||||||
|
label: 'Battery (%)',
|
||||||
|
backgroundColor: '#0989f8',
|
||||||
|
borderColor: '#5959a8',
|
||||||
|
fill: false,
|
||||||
|
data: this.battery,
|
||||||
|
yAxisID: 'percentage',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
labels: this.points.map((point: GPSPoint) => point.timestamp),
|
labels: this.points.map((point: GPSPoint) => point.timestamp),
|
||||||
datasets: datasets,
|
datasets: datasets,
|
||||||
|
@ -242,6 +275,31 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.showMetrics.battery) {
|
||||||
|
let position = 'left'
|
||||||
|
if (yAxes.meters || yAxes.speed) {
|
||||||
|
position = 'right'
|
||||||
|
}
|
||||||
|
|
||||||
|
yAxes.percentage = {
|
||||||
|
type: 'linear',
|
||||||
|
position: position,
|
||||||
|
display: true,
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
ticks: {
|
||||||
|
beginAtZero: true,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'Percentage',
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
drawOnChartArea: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const xTicks = {} as { min?: Date, max?: Date }
|
const xTicks = {} as { min?: Date, max?: Date }
|
||||||
if (this.points.length > 1) {
|
if (this.points.length > 1) {
|
||||||
xTicks.min = this.points[0].timestamp
|
xTicks.min = this.points[0].timestamp
|
||||||
|
|
|
@ -2,13 +2,14 @@ class TimelineMetricsConfiguration {
|
||||||
public altitude: boolean = false;
|
public altitude: boolean = false;
|
||||||
public distance: boolean = true;
|
public distance: boolean = true;
|
||||||
public speed: boolean = false;
|
public speed: boolean = false;
|
||||||
|
public battery: boolean = false;
|
||||||
|
|
||||||
constructor(data: any | null = null) {
|
constructor(data: any | null = null) {
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const key of ['altitude', 'distance', 'speed']) {
|
for (const key of ['altitude', 'distance', 'speed', 'battery']) {
|
||||||
const value = String(
|
const value = String(
|
||||||
data[key] ?? data['show' + key.charAt(0).toUpperCase() + key.slice(1)]
|
data[key] ?? data['show' + key.charAt(0).toUpperCase() + key.slice(1)]
|
||||||
)
|
)
|
||||||
|
@ -39,13 +40,16 @@ class TimelineMetricsConfiguration {
|
||||||
case 'speed':
|
case 'speed':
|
||||||
this.speed = !this.speed;
|
this.speed = !this.speed;
|
||||||
break;
|
break;
|
||||||
|
case 'battery':
|
||||||
|
this.battery = !this.battery;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new TypeError(`Invalid timeline metric: ${metric}`);
|
throw new TypeError(`Invalid timeline metric: ${metric}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toQuery(): Record<string, string> {
|
toQuery(): Record<string, string> {
|
||||||
return ['altitude', 'distance', 'speed'].reduce((acc: Record<string, string>, key: string) => {
|
return ['altitude', 'distance', 'speed', 'battery'].reduce((acc: Record<string, string>, key: string) => {
|
||||||
acc['show' + key.charAt(0).toUpperCase() + key.slice(1)] = String((this as any)[key]);
|
acc['show' + key.charAt(0).toUpperCase() + key.slice(1)] = String((this as any)[key]);
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue