mirror of
https://github.com/ckaczor/HomeMonitor.git
synced 2026-02-15 10:58:34 -05:00
More new UI work
This commit is contained in:
10
WebDisplay/.vscode/settings.json
vendored
Normal file
10
WebDisplay/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"cSpell.words": [
|
||||||
|
"apexchart",
|
||||||
|
"pinia",
|
||||||
|
"stepline",
|
||||||
|
"Vuetify",
|
||||||
|
"xaxis",
|
||||||
|
"yaxis"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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 }}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
21
WebDisplay/src/models/weather/wind-direction.ts
Normal file
21
WebDisplay/src/models/weather/wind-direction.ts
Normal 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;
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
41
WebDisplay/src/windFormatter.ts
Normal file
41
WebDisplay/src/windFormatter.ts
Normal 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();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user