mirror of
https://github.com/ckaczor/HomeMonitor.git
synced 2026-01-16 09:35:38 -05:00
Bunch more new UI
This commit is contained in:
@@ -35,7 +35,7 @@
|
||||
</DashboardItem>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
<style scoped>
|
||||
.power-current {
|
||||
font-size: 14px;
|
||||
padding: 6px 12px;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { useWeatherStore } from '@/stores/weatherStore';
|
||||
import { ConvertPascalToInchesOfMercury } from '@/pressureConverter';
|
||||
|
||||
const weatherStore = useWeatherStore();
|
||||
weatherStore.start();
|
||||
@@ -31,62 +32,33 @@
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className="weather-current-header">Temperature</td>
|
||||
<td>
|
||||
{{
|
||||
weatherStore.current?.Temperature?.toFixed(2)
|
||||
}}°F
|
||||
</td>
|
||||
<td>{{ weatherStore.current?.Temperature?.toFixed(2) }}°F</td>
|
||||
</tr>
|
||||
<tr v-if="weatherStore.current?.HeatIndex">
|
||||
<td className="weather-current-header">Heat index</td>
|
||||
<td>
|
||||
{{ weatherStore.current?.HeatIndex?.toFixed(2) }}°F
|
||||
</td>
|
||||
<td>{{ weatherStore.current?.HeatIndex?.toFixed(2) }}°F</td>
|
||||
</tr>
|
||||
|
||||
<tr v-if="weatherStore.current?.WindChill">
|
||||
<td className="weather-current-header">Wind chill</td>
|
||||
<td>
|
||||
{{ weatherStore.current?.WindChill?.toFixed(2) }}°F
|
||||
</td>
|
||||
<td>{{ weatherStore.current?.WindChill?.toFixed(2) }}°F</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="weather-current-header">Humidity</td>
|
||||
<td>
|
||||
{{ weatherStore.current?.Humidity?.toFixed(2) }}%
|
||||
</td>
|
||||
<td>{{ weatherStore.current?.Humidity?.toFixed(2) }}%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="weather-current-header">Dew point</td>
|
||||
<td>
|
||||
{{ weatherStore.current?.DewPoint?.toFixed(2) }}°F
|
||||
</td>
|
||||
<td>{{ weatherStore.current?.DewPoint?.toFixed(2) }}°F</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="weather-current-header">Pressure</td>
|
||||
<td>
|
||||
{{
|
||||
weatherStore.current?.Pressure &&
|
||||
(
|
||||
weatherStore.current?.Pressure /
|
||||
33.864 /
|
||||
100
|
||||
)?.toFixed(2)
|
||||
}}"
|
||||
{{ weatherStore.current?.Pressure && ConvertPascalToInchesOfMercury(weatherStore.current?.Pressure)?.toFixed(2) }}"
|
||||
<span
|
||||
class="pressure-trend-arrow"
|
||||
:class="
|
||||
rotationClass(
|
||||
weatherStore.current
|
||||
?.PressureDifferenceThreeHour
|
||||
)
|
||||
"
|
||||
:title="
|
||||
'3 Hour Change: ' +
|
||||
weatherStore.current?.PressureDifferenceThreeHour?.toFixed(
|
||||
1
|
||||
)
|
||||
">
|
||||
:class="rotationClass(weatherStore.current?.PressureDifferenceThreeHour)"
|
||||
:title="'3 Hour Change: ' + weatherStore.current?.PressureDifferenceThreeHour?.toFixed(1)">
|
||||
➜
|
||||
</span>
|
||||
</td>
|
||||
@@ -100,11 +72,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="weather-current-header">Rain</td>
|
||||
<td>
|
||||
{{
|
||||
weatherStore.current?.RainLastHour?.toFixed(2)
|
||||
}}" (last hour)
|
||||
</td>
|
||||
<td>{{ weatherStore.current?.RainLastHour?.toFixed(2) }}" (last hour)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="weather-current-header">Light</td>
|
||||
|
||||
120
WebDisplay/src/components/Indoor.vue
Normal file
120
WebDisplay/src/components/Indoor.vue
Normal file
@@ -0,0 +1,120 @@
|
||||
<script setup lang="ts">
|
||||
import { createIndoorStore } from '@/stores/indoorStore';
|
||||
import { ConvertCToF } from '@/temperatureConverter';
|
||||
import { ConvertMillibarToInchesOfMercury } from '@/pressureConverter';
|
||||
|
||||
const props = defineProps({
|
||||
title: { type: String, required: true },
|
||||
deviceName: { type: String, required: true }
|
||||
});
|
||||
|
||||
const indoorStore = createIndoorStore(props.deviceName);
|
||||
indoorStore.start();
|
||||
|
||||
const airQualityDescription = (airQualityIndex: number | undefined) => {
|
||||
if (!airQualityIndex) {
|
||||
return '';
|
||||
} else if (airQualityIndex >= 0 && airQualityIndex <= 50) {
|
||||
return 'Good';
|
||||
} else if (airQualityIndex >= 51 && airQualityIndex <= 100) {
|
||||
return 'Moderate';
|
||||
} else if (airQualityIndex >= 101 && airQualityIndex <= 150) {
|
||||
return 'Unhealthy for Sensitive Groups';
|
||||
} else if (airQualityIndex >= 151 && airQualityIndex <= 200) {
|
||||
return 'Unhealthy';
|
||||
} else if (airQualityIndex >= 201 && airQualityIndex <= 300) {
|
||||
return 'Very Unhealthy';
|
||||
} else if (airQualityIndex >= 301) {
|
||||
return 'Hazardous';
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
||||
|
||||
const airQualityClass = (airQualityIndex: number | undefined) => {
|
||||
if (!airQualityIndex) {
|
||||
return 'aqi-none';
|
||||
} else if (airQualityIndex >= 0 && airQualityIndex <= 50) {
|
||||
return 'aqi-green';
|
||||
} else if (airQualityIndex >= 51 && airQualityIndex <= 100) {
|
||||
return 'aqi-yellow';
|
||||
} else if (airQualityIndex >= 101 && airQualityIndex <= 150) {
|
||||
return 'aqi-orange';
|
||||
} else if (airQualityIndex >= 151 && airQualityIndex <= 200) {
|
||||
return 'aqi-red';
|
||||
} else if (airQualityIndex >= 201 && airQualityIndex <= 300) {
|
||||
return 'aqi-purple';
|
||||
} else if (airQualityIndex >= 301) {
|
||||
return 'aqi-maroon';
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DashboardItem :title="title">
|
||||
<div className="current">
|
||||
<div v-if="!indoorStore.current">Loading...</div>
|
||||
<table v-else>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className="header">Temperature</td>
|
||||
<td>{{ ConvertCToF(indoorStore.current.temperature).toFixed(2) }}°F</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="header">Humidity</td>
|
||||
<td>{{ indoorStore.current.humidity.toFixed(2) }}%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="header">Pressure</td>
|
||||
<td>{{ ConvertMillibarToInchesOfMercury(indoorStore.current.pressure).toFixed(2) }}"</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="header">Air Quality</td>
|
||||
<td :class="airQualityClass(indoorStore.current.airQualityIndex)" :title="indoorStore.current.airQualityIndex.toString()">
|
||||
{{ airQualityDescription(indoorStore.current.airQualityIndex) }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</DashboardItem>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.current {
|
||||
font-size: 14px;
|
||||
padding: 6px 12px;
|
||||
}
|
||||
|
||||
.header {
|
||||
font-weight: 500;
|
||||
text-align: right;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.aqi-green {
|
||||
color: green;
|
||||
}
|
||||
|
||||
.aqi-yellow {
|
||||
color: goldenrod;
|
||||
}
|
||||
|
||||
.aqi-orange {
|
||||
color: orange;
|
||||
}
|
||||
|
||||
.aqi-red {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.aqi-purple {
|
||||
color: purple;
|
||||
}
|
||||
|
||||
.aqi-maroon {
|
||||
color: maroon;
|
||||
}
|
||||
</style>
|
||||
109
WebDisplay/src/components/ValueChart.vue
Normal file
109
WebDisplay/src/components/ValueChart.vue
Normal file
@@ -0,0 +1,109 @@
|
||||
<script setup lang="ts">
|
||||
import { ApexOptions } from 'apexcharts';
|
||||
|
||||
const props = defineProps({
|
||||
type: { type: String, required: true },
|
||||
title: { type: String, required: true },
|
||||
unit: { type: String, required: true },
|
||||
categories: { type: Array<number>, required: true },
|
||||
series: { type: Array<{ name: string; data: Array<number> }>, required: true },
|
||||
yAxisDecimalPoints: { type: Number, required: false, default: 0 },
|
||||
valueDecimalPoints: { type: Number, required: false, default: 2 },
|
||||
group: { type: String, required: false, default: undefined },
|
||||
stepline: { type: Boolean, required: false, default: false },
|
||||
ready: { type: Boolean, required: true }
|
||||
});
|
||||
|
||||
var chartOptions: ApexOptions = {
|
||||
chart: {
|
||||
id: props.title,
|
||||
animations: {
|
||||
enabled: false
|
||||
},
|
||||
group: props.group,
|
||||
toolbar: {
|
||||
tools: {
|
||||
pan: false
|
||||
}
|
||||
}
|
||||
},
|
||||
title: {
|
||||
text: props.title,
|
||||
align: 'center'
|
||||
},
|
||||
legend: {
|
||||
itemMargin: {
|
||||
vertical: 5
|
||||
},
|
||||
showForSingleSeries: true
|
||||
},
|
||||
tooltip: {
|
||||
x: {
|
||||
format: 'MMMM d h:mm TT'
|
||||
},
|
||||
y: {
|
||||
formatter: (value) => {
|
||||
return `${value.toFixed(props.valueDecimalPoints)}${props.unit}`;
|
||||
}
|
||||
}
|
||||
},
|
||||
xaxis: {
|
||||
type: 'datetime',
|
||||
categories: props.categories,
|
||||
tooltip: {
|
||||
enabled: false
|
||||
},
|
||||
labels: {
|
||||
datetimeUTC: false,
|
||||
datetimeFormatter: {
|
||||
day: 'MMM d',
|
||||
hour: 'h:mm TT'
|
||||
}
|
||||
}
|
||||
},
|
||||
yaxis: {
|
||||
labels: {
|
||||
formatter: (value) => {
|
||||
return `${value.toFixed(props.yAxisDecimalPoints)}${props.unit}`;
|
||||
}
|
||||
}
|
||||
},
|
||||
stroke: {
|
||||
width: 2,
|
||||
curve: props.stepline ? 'stepline' : 'smooth'
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false
|
||||
}
|
||||
};
|
||||
|
||||
var chartSeries: ApexAxisChartSeries = props.series;
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-container
|
||||
v-if="!props.ready"
|
||||
class="fill-height loading">
|
||||
<v-responsive class="align-center text-center fill-height">
|
||||
<v-progress-circular
|
||||
:size="50"
|
||||
:width="5"
|
||||
color="primary"
|
||||
indeterminate></v-progress-circular>
|
||||
</v-responsive>
|
||||
</v-container>
|
||||
<apexchart
|
||||
v-else
|
||||
width="100%"
|
||||
height="300"
|
||||
:type="props.type"
|
||||
:options="chartOptions"
|
||||
:series="chartSeries"></apexchart>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.loading {
|
||||
width: 100%;
|
||||
min-height: 300px;
|
||||
}
|
||||
</style>
|
||||
@@ -3,6 +3,7 @@
|
||||
import { useWeatherStore } from '@/stores/weatherStore';
|
||||
import { subHours } from 'date-fns';
|
||||
import { WeatherAggregates } from '@/models/weather/weather-aggregates';
|
||||
import { ConvertPascalToInchesOfMercury } from '@/pressureConverter';
|
||||
|
||||
const weatherAggregates = ref<WeatherAggregates | undefined>();
|
||||
|
||||
@@ -11,11 +12,9 @@
|
||||
const end = new Date();
|
||||
const start = subHours(end, 24);
|
||||
|
||||
weatherStore
|
||||
.getReadingAggregate(start, end)
|
||||
.then((newWeatherAggregates) => {
|
||||
weatherAggregates.value = newWeatherAggregates;
|
||||
});
|
||||
weatherStore.getReadingAggregate(start, end).then((newWeatherAggregates) => {
|
||||
weatherAggregates.value = newWeatherAggregates;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -32,78 +31,35 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="weather-summary-header">Temperature</td>
|
||||
<td>
|
||||
{{
|
||||
weatherAggregates!.temperature.min.toFixed(2)
|
||||
}}°F
|
||||
</td>
|
||||
<td>
|
||||
{{
|
||||
weatherAggregates!.temperature.average.toFixed(
|
||||
2
|
||||
)
|
||||
}}°F
|
||||
</td>
|
||||
<td>
|
||||
{{
|
||||
weatherAggregates!.temperature.max.toFixed(2)
|
||||
}}°F
|
||||
</td>
|
||||
<td>{{ weatherAggregates!.temperature.min.toFixed(2) }}°F</td>
|
||||
<td>{{ weatherAggregates!.temperature.average.toFixed(2) }}°F</td>
|
||||
<td>{{ weatherAggregates!.temperature.max.toFixed(2) }}°F</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="weather-summary-header">Humidity</td>
|
||||
<td>
|
||||
{{ weatherAggregates!.humidity.min.toFixed(2) }}%
|
||||
</td>
|
||||
<td>
|
||||
{{
|
||||
weatherAggregates!.humidity.average.toFixed(2)
|
||||
}}%
|
||||
</td>
|
||||
<td>
|
||||
{{ weatherAggregates!.humidity.max.toFixed(2) }}%
|
||||
</td>
|
||||
<td>{{ weatherAggregates!.humidity.min.toFixed(2) }}%</td>
|
||||
<td>{{ weatherAggregates!.humidity.average.toFixed(2) }}%</td>
|
||||
<td>{{ weatherAggregates!.humidity.max.toFixed(2) }}%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="weather-summary-header">Pressure</td>
|
||||
<td>
|
||||
{{
|
||||
(
|
||||
weatherAggregates!.pressure.min /
|
||||
33.864 /
|
||||
100
|
||||
).toFixed(2)
|
||||
}}"
|
||||
</td>
|
||||
<td>
|
||||
{{
|
||||
(
|
||||
weatherAggregates!.pressure.average /
|
||||
33.864 /
|
||||
100
|
||||
).toFixed(2)
|
||||
}}"
|
||||
</td>
|
||||
<td>
|
||||
{{
|
||||
(
|
||||
weatherAggregates!.pressure.max /
|
||||
33.864 /
|
||||
100
|
||||
).toFixed(2)
|
||||
}}"
|
||||
</td>
|
||||
<td>{{ ConvertPascalToInchesOfMercury(weatherAggregates!.pressure.min).toFixed(2) }}"</td>
|
||||
<td>{{ ConvertPascalToInchesOfMercury(weatherAggregates!.pressure.average).toFixed(2) }}"</td>
|
||||
<td>{{ ConvertPascalToInchesOfMercury(weatherAggregates!.pressure.max).toFixed(2) }}"</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="weather-summary-header">Light</td>
|
||||
<td>
|
||||
{{ weatherAggregates!.light.min.toFixed(2) }} lx
|
||||
{{ weatherAggregates!.light.min.toFixed(2) }}
|
||||
lx
|
||||
</td>
|
||||
<td>
|
||||
{{ weatherAggregates!.light.average.toFixed(2) }} lx
|
||||
{{ weatherAggregates!.light.average.toFixed(2) }}
|
||||
lx
|
||||
</td>
|
||||
<td>
|
||||
{{ weatherAggregates!.light.max.toFixed(2) }} lx
|
||||
{{ weatherAggregates!.light.max.toFixed(2) }}
|
||||
lx
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -113,9 +69,7 @@
|
||||
mph
|
||||
</td>
|
||||
<td>
|
||||
{{
|
||||
weatherAggregates!.windSpeed.average.toFixed(2)
|
||||
}}
|
||||
{{ weatherAggregates!.windSpeed.average.toFixed(2) }}
|
||||
mph
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Reference in New Issue
Block a user