More new UI work

This commit is contained in:
2024-03-08 01:54:11 +00:00
parent f21c59a78a
commit 5194a903b3
12 changed files with 184 additions and 59 deletions

10
WebDisplay/.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,10 @@
{
"cSpell.words": [
"apexchart",
"pinia",
"stepline",
"Vuetify",
"xaxis",
"yaxis"
]
}

View File

@@ -1,28 +1,46 @@
<template> <template>
<v-app> <v-app>
<v-app-bar title="Home Monitor" color="primary"></v-app-bar> <v-app-bar
title="Home Monitor"
color="primary">
<template v-slot:prepend>
<v-app-bar-nav-icon
v-show="!mdAndUp"
@click="drawer = !drawer"></v-app-bar-nav-icon>
</template>
</v-app-bar>
<v-navigation-drawer expand-on-hover rail> <v-navigation-drawer
<v-list density="compact" nav> mobile-breakpoint="md"
:expand-on-hover="mdAndUp"
:rail="mdAndUp"
:model-value="mdAndUp ? true : drawer">
<v-list
density="compact"
nav>
<v-list-item <v-list-item
prepend-icon="mdi-view-dashboard" prepend-icon="mdi-view-dashboard"
title="Dashboard" title="Dashboard"
to="/"> to="/"
@click="drawer = false">
</v-list-item> </v-list-item>
<v-list-item <v-list-item
prepend-icon="mdi-sun-thermometer" prepend-icon="mdi-sun-thermometer"
title="Outdoor" title="Outdoor"
to="outdoor"> to="outdoor"
@click="drawer = false">
</v-list-item> </v-list-item>
<v-list-item <v-list-item
prepend-icon="mdi-home-analytics" prepend-icon="mdi-home-analytics"
title="Indoor" title="Indoor"
to="indoor"> to="indoor"
@click="drawer = false">
</v-list-item> </v-list-item>
<v-list-item <v-list-item
prepend-icon="mdi-home-lightning-bolt-outline" prepend-icon="mdi-home-lightning-bolt-outline"
title="Power" title="Power"
to="power"> to="power"
@click="drawer = false">
</v-list-item> </v-list-item>
</v-list> </v-list>
</v-navigation-drawer> </v-navigation-drawer>
@@ -34,5 +52,10 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
// import { ref } from 'vue';
import { useDisplay } from 'vuetify';
const drawer = ref(false);
const { mdAndUp } = useDisplay();
</script> </script>

View File

@@ -1,6 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { useWeatherStore } from '@/stores/weatherStore'; import { useWeatherStore } from '@/stores/weatherStore';
import { ConvertPascalToInchesOfMercury } from '@/pressureConverter'; import { ConvertPascalToInchesOfMercury } from '@/pressureConverter';
import { ShortenWindDirection } from '@/windFormatter';
const weatherStore = useWeatherStore(); const weatherStore = useWeatherStore();
weatherStore.start(); weatherStore.start();
@@ -28,32 +29,42 @@
<DashboardItem title="Weather"> <DashboardItem title="Weather">
<div className="weather-current"> <div className="weather-current">
<div v-if="!weatherStore.current">Loading...</div> <div v-if="!weatherStore.current">Loading...</div>
<table v-else> <table
v-else
class="weather-table">
<tbody> <tbody>
<tr> <tr>
<td className="weather-current-header">Temperature</td> <td
<td>{{ weatherStore.current?.Temperature?.toFixed(2) }}°F</td> className="weather-current-header"
</tr> width="1">
<tr v-if="weatherStore.current?.HeatIndex"> Temperature
<td className="weather-current-header">Heat index</td> </td>
<td>{{ weatherStore.current?.HeatIndex?.toFixed(2) }}°F</td> <td :colspan="weatherStore.current?.HeatIndex || weatherStore.current?.WindChill ? 1 : 3">
</tr> {{ weatherStore.current?.Temperature?.toFixed(2) }}°F
</td>
<tr v-if="weatherStore.current?.WindChill"> <td>
<td className="weather-current-header">Wind chill</td> <div
<td>{{ weatherStore.current?.WindChill?.toFixed(2) }}°F</td> v-if="weatherStore.current?.HeatIndex || weatherStore.current?.WindChill"
className="weather-current-header">
Feels like
</div>
</td>
<td>
<div v-if="weatherStore.current?.HeatIndex">{{ weatherStore.current?.HeatIndex?.toFixed(2) }}°F</div>
<div v-if="weatherStore.current?.WindChill">{{ weatherStore.current?.WindChill?.toFixed(2) }}°F</div>
</td>
</tr> </tr>
<tr> <tr>
<td className="weather-current-header">Humidity</td> <td className="weather-current-header">Humidity</td>
<td>{{ weatherStore.current?.Humidity?.toFixed(2) }}%</td> <td colspan="3">{{ weatherStore.current?.Humidity?.toFixed(2) }}%</td>
</tr> </tr>
<tr> <tr>
<td className="weather-current-header">Dew point</td> <td className="weather-current-header">Dew point</td>
<td>{{ weatherStore.current?.DewPoint?.toFixed(2) }}°F</td> <td colspan="3">{{ weatherStore.current?.DewPoint?.toFixed(2) }}°F</td>
</tr> </tr>
<tr> <tr>
<td className="weather-current-header">Pressure</td> <td className="weather-current-header">Pressure</td>
<td> <td colspan="3">
{{ weatherStore.current?.Pressure && ConvertPascalToInchesOfMercury(weatherStore.current?.Pressure)?.toFixed(2) }}" {{ weatherStore.current?.Pressure && ConvertPascalToInchesOfMercury(weatherStore.current?.Pressure)?.toFixed(2) }}"
<span <span
class="pressure-trend-arrow" class="pressure-trend-arrow"
@@ -65,18 +76,18 @@
</tr> </tr>
<tr> <tr>
<td className="weather-current-header">Wind</td> <td className="weather-current-header">Wind</td>
<td> <td colspan="3">
{{ weatherStore.current?.WindSpeed?.toFixed(2) }} {{ weatherStore.current?.WindSpeed?.toFixed(2) }}
mph {{ weatherStore.current?.WindDirection }} mph {{ ShortenWindDirection(weatherStore.current?.WindDirection) }}
</td> </td>
</tr> </tr>
<tr> <tr>
<td className="weather-current-header">Rain</td> <td className="weather-current-header">Rain</td>
<td>{{ weatherStore.current?.RainLastHour?.toFixed(2) }}" (last hour)</td> <td colspan="3">{{ weatherStore.current?.RainLastHour?.toFixed(2) }}" (last hour)</td>
</tr> </tr>
<tr> <tr>
<td className="weather-current-header">Light</td> <td className="weather-current-header">Light</td>
<td> <td colspan="3">
{{ weatherStore.current?.LightLevel?.toFixed(2) }} {{ weatherStore.current?.LightLevel?.toFixed(2) }}
lx lx
</td> </td>
@@ -90,7 +101,7 @@
<style> <style>
.weather-current { .weather-current {
font-size: 14px; font-size: 14px;
padding: 6px 12px; padding: 4px 12px;
} }
.weather-current-header { .weather-current-header {
@@ -99,6 +110,10 @@
padding-right: 10px; padding-right: 10px;
} }
.weather-table {
width: 100%;
}
.pressure-trend-arrow { .pressure-trend-arrow {
display: inline-block; display: inline-block;
position: relative; position: relative;

View File

@@ -14,10 +14,15 @@
<style scoped> <style scoped>
.dashboard-item-header { .dashboard-item-header {
padding: 2px 10px; padding: 2px 10px;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
} }
.dashboard-item-content { .dashboard-item-content {
background-color: white; background-color: white;
border: 1px solid lightgray; border: 1px solid lightgray;
border-top: 0px;
border-bottom-left-radius: 3px;
border-bottom-right-radius: 3px;
} }
</style> </style>

View File

@@ -29,7 +29,7 @@
}, },
title: { title: {
text: props.title, text: props.title,
align: 'center' align: 'left'
}, },
legend: { legend: {
itemMargin: { itemMargin: {
@@ -81,24 +81,26 @@
</script> </script>
<template> <template>
<v-container <div class="chart">
v-if="!props.ready" <v-container
class="fill-height loading"> v-if="!props.ready"
<v-responsive class="align-center text-center fill-height"> class="fill-height loading">
<v-progress-circular <v-responsive class="align-center text-center fill-height">
:size="50" <v-progress-circular
:width="5" :size="50"
color="primary" :width="5"
indeterminate></v-progress-circular> color="primary"
</v-responsive> indeterminate></v-progress-circular>
</v-container> </v-responsive>
<apexchart </v-container>
v-else <apexchart
width="100%" v-else
height="300" width="100%"
:type="props.type" height="300"
:options="chartOptions" :type="props.type"
:series="chartSeries"></apexchart> :options="chartOptions"
:series="chartSeries"></apexchart>
</div>
</template> </template>
<style scoped> <style scoped>
@@ -106,4 +108,12 @@
width: 100%; width: 100%;
min-height: 300px; min-height: 300px;
} }
.chart {
border: 1px solid lightgray;
background-color: white;
padding-top: 10px;
padding-right: 10px;
border-radius: 3px;
}
</style> </style>

View File

@@ -2,7 +2,7 @@
import { ref } from 'vue'; import { ref } from 'vue';
import { useWeatherStore } from '@/stores/weatherStore'; import { useWeatherStore } from '@/stores/weatherStore';
import { subHours } from 'date-fns'; import { subHours } from 'date-fns';
import { WeatherAggregates } from '@/models/weather/weather-aggregates'; import WeatherAggregates from '@/models/weather/weather-aggregates';
import { ConvertPascalToInchesOfMercury } from '@/pressureConverter'; import { ConvertPascalToInchesOfMercury } from '@/pressureConverter';
const weatherAggregates = ref<WeatherAggregates | undefined>(); const weatherAggregates = ref<WeatherAggregates | undefined>();
@@ -63,7 +63,7 @@
</td> </td>
</tr> </tr>
<tr> <tr>
<td class="weather-summary-header">Wind Speed</td> <td class="weather-summary-header">Wind speed</td>
<td> <td>
{{ weatherAggregates!.windSpeed.min.toFixed(2) }} {{ weatherAggregates!.windSpeed.min.toFixed(2) }}
mph mph
@@ -78,7 +78,7 @@
</td> </td>
</tr> </tr>
<tr> <tr>
<td class="weather-summary-header">Wind Direction</td> <td class="weather-summary-header">Wind direction</td>
<td></td> <td></td>
<td> <td>
{{ weatherAggregates!.windDirectionAverage }} {{ weatherAggregates!.windDirectionAverage }}

View File

@@ -1,8 +1,10 @@
import WindDirection from './wind-direction';
export default class WeatherRecent { export default class WeatherRecent {
type: string | undefined; type: string | undefined;
message: null | undefined; message: null | undefined;
timestamp: Date | undefined; timestamp: Date | undefined;
windDirection: string | undefined; windDirection: WindDirection | undefined;
windSpeed: number | undefined; windSpeed: number | undefined;
humidity: number | undefined; humidity: number | undefined;
rain: number | undefined; rain: number | undefined;

View File

@@ -1,8 +1,10 @@
import WindDirection from './wind-direction';
export default class WeatherUpdate { export default class WeatherUpdate {
Type: string | undefined; Type: string | undefined;
Message: null | undefined; Message: null | undefined;
Timestamp: Date | undefined; Timestamp: Date | undefined;
WindDirection: string | undefined; WindDirection: WindDirection | undefined;
WindSpeed: number | undefined; WindSpeed: number | undefined;
Humidity: number | undefined; Humidity: number | undefined;
Rain: number | undefined; Rain: number | undefined;

View File

@@ -0,0 +1,21 @@
export enum WindDirection {
None = 'None',
North = 'North',
East = 'East',
South = 'South',
West = 'West',
NorthEast = 'NorthEast',
SouthEast = 'SouthEast',
SouthWest = 'SouthWest',
NorthWest = 'NorthWest',
NorthNorthEast = 'NorthNorthEast',
EastNorthEast = 'EastNorthEast',
EastSouthEast = 'EastSouthEast',
SouthSouthEast = 'SouthSouthEast',
SouthSouthWest = 'SouthSouthWest',
WestSouthWest = 'WestSouthWest',
WestNorthWest = 'WestNorthWest',
NorthNorthWest = 'NorthNorthWest'
}
export default WindDirection;

View File

@@ -5,7 +5,7 @@
<template> <template>
<v-container fluid class="container"> <v-container fluid class="container">
<v-row align="start"> <v-row dense align="start">
<v-col cols="3"> <v-col cols="3">
<CurrentWeather></CurrentWeather> <CurrentWeather></CurrentWeather>
</v-col> </v-col>
@@ -18,8 +18,6 @@
<v-col cols="2"> <v-col cols="2">
<CurrentLaundryStatus></CurrentLaundryStatus> <CurrentLaundryStatus></CurrentLaundryStatus>
</v-col> </v-col>
</v-row>
<v-row>
<v-col cols="4"> <v-col cols="4">
<WeatherSummary></WeatherSummary> <WeatherSummary></WeatherSummary>
</v-col> </v-col>

View File

@@ -53,8 +53,7 @@
dense dense
align="start"> align="start">
<v-col <v-col
sm="4" sm="6"
md="6"
cols="12"> cols="12">
<ValueChart <ValueChart
:ready="powerReady" :ready="powerReady"
@@ -68,8 +67,7 @@
:series="[generationSeries, consumptionSeries]"></ValueChart> :series="[generationSeries, consumptionSeries]"></ValueChart>
</v-col> </v-col>
<v-col <v-col
sm="4" sm="6"
md="6"
cols="12"> cols="12">
<ValueChart <ValueChart
:ready="lightReady" :ready="lightReady"

View File

@@ -0,0 +1,41 @@
import WindDirection from './models/weather/wind-direction';
export function ShortenWindDirection(windDirection: WindDirection | undefined): string {
switch (windDirection) {
case WindDirection.None:
return '';
case WindDirection.North:
return 'N';
case WindDirection.East:
return 'E';
case WindDirection.South:
return 'S';
case WindDirection.West:
return 'W';
case WindDirection.NorthEast:
return 'NE';
case WindDirection.SouthEast:
return 'SE';
case WindDirection.SouthWest:
return 'SW';
case WindDirection.NorthWest:
return 'NW';
case WindDirection.NorthNorthEast:
return 'NNE';
case WindDirection.EastNorthEast:
return 'ENE';
case WindDirection.EastSouthEast:
return 'ESE';
case WindDirection.SouthSouthEast:
return 'SSE';
case WindDirection.SouthSouthWest:
return 'SSW';
case WindDirection.WestSouthWest:
return 'WSW';
case WindDirection.WestNorthWest:
return 'WNW';
case WindDirection.NorthNorthWest:
return 'NNW';
}
return windDirection!.toString();
}