mirror of
https://github.com/ckaczor/HomeMonitor.git
synced 2026-02-16 10:58:32 -05:00
More UI work and start preparing for deployment
This commit is contained in:
14
WebDisplay/Dockerfile
Normal file
14
WebDisplay/Dockerfile
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# build stage
|
||||||
|
FROM node:lts-alpine as build-stage
|
||||||
|
RUN corepack enable && corepack prepare pnpm@latest --activate
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN pnpm install
|
||||||
|
COPY . .
|
||||||
|
RUN pnpm run build
|
||||||
|
|
||||||
|
# production stage
|
||||||
|
FROM nginx:stable-alpine as production-stage
|
||||||
|
COPY --from=build-stage /app/dist /usr/share/nginx/html
|
||||||
|
EXPOSE 80
|
||||||
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
1
WebDisplay/components.d.ts
vendored
1
WebDisplay/components.d.ts
vendored
@@ -12,7 +12,6 @@ declare module 'vue' {
|
|||||||
CurrentPower: typeof import('./src/components/CurrentPower.vue')['default']
|
CurrentPower: typeof import('./src/components/CurrentPower.vue')['default']
|
||||||
CurrentWeather: typeof import('./src/components/CurrentWeather.vue')['default']
|
CurrentWeather: typeof import('./src/components/CurrentWeather.vue')['default']
|
||||||
DashboardItem: typeof import('./src/components/DashboardItem.vue')['default']
|
DashboardItem: typeof import('./src/components/DashboardItem.vue')['default']
|
||||||
HelloWorld: typeof import('./src/components/HelloWorld.vue')['default']
|
|
||||||
Indoor: typeof import('./src/components/Indoor.vue')['default']
|
Indoor: typeof import('./src/components/Indoor.vue')['default']
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
RouterView: typeof import('vue-router')['RouterView']
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
|
|||||||
68
WebDisplay/deploy/azure-pipelines.yml
Normal file
68
WebDisplay/deploy/azure-pipelines.yml
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
name: $(Rev:r)
|
||||||
|
|
||||||
|
pr: none
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
batch: 'true'
|
||||||
|
branches:
|
||||||
|
include:
|
||||||
|
- master
|
||||||
|
paths:
|
||||||
|
include:
|
||||||
|
- WebDisplay
|
||||||
|
|
||||||
|
stages:
|
||||||
|
- stage: Build
|
||||||
|
jobs:
|
||||||
|
- job: Build
|
||||||
|
pool:
|
||||||
|
vmImage: 'ubuntu-latest'
|
||||||
|
steps:
|
||||||
|
- task: Docker@0
|
||||||
|
displayName: 'Build an image'
|
||||||
|
inputs:
|
||||||
|
containerregistrytype: 'Container Registry'
|
||||||
|
dockerRegistryConnection: 'Docker Hub'
|
||||||
|
dockerFile: 'WebDisplay/Dockerfile'
|
||||||
|
imageName: 'ckaczor/home-monitor-web-display:$(Build.BuildNumber)'
|
||||||
|
includeLatestTag: true
|
||||||
|
- task: Docker@0
|
||||||
|
displayName: 'Push an image'
|
||||||
|
inputs:
|
||||||
|
containerregistrytype: 'Container Registry'
|
||||||
|
dockerRegistryConnection: 'Docker Hub'
|
||||||
|
action: 'Push an image'
|
||||||
|
imageName: 'ckaczor/home-monitor-web-display:$(Build.BuildNumber)'
|
||||||
|
includeLatestTag: true
|
||||||
|
- task: Bash@3
|
||||||
|
inputs:
|
||||||
|
targetType: 'inline'
|
||||||
|
script: 'sed -i s/#BUILD_BUILDNUMBER#/$BUILD_BUILDNUMBER/ WebDisplay/deploy/manifest.yaml'
|
||||||
|
- task: PublishBuildArtifacts@1
|
||||||
|
inputs:
|
||||||
|
PathtoPublish: 'WebDisplay/deploy/manifest.yaml'
|
||||||
|
ArtifactName: 'Manifest'
|
||||||
|
publishLocation: 'Container'
|
||||||
|
|
||||||
|
- stage: Deploy
|
||||||
|
jobs:
|
||||||
|
- job: Deploy
|
||||||
|
pool:
|
||||||
|
vmImage: 'ubuntu-latest'
|
||||||
|
steps:
|
||||||
|
- task: DownloadBuildArtifacts@0
|
||||||
|
inputs:
|
||||||
|
artifactName: 'Manifest'
|
||||||
|
buildType: 'current'
|
||||||
|
downloadType: 'single'
|
||||||
|
downloadPath: '$(System.ArtifactsDirectory)'
|
||||||
|
- task: Kubernetes@1
|
||||||
|
inputs:
|
||||||
|
connectionType: 'Kubernetes Service Connection'
|
||||||
|
kubernetesServiceEndpoint: 'Kubernetes'
|
||||||
|
namespace: 'home-monitor'
|
||||||
|
command: 'apply'
|
||||||
|
useConfigurationFile: true
|
||||||
|
configuration: '$(System.ArtifactsDirectory)/Manifest/manifest.yaml'
|
||||||
|
secretType: 'dockerRegistry'
|
||||||
|
containerRegistryType: 'Container Registry'
|
||||||
76
WebDisplay/deploy/manifest.yaml
Normal file
76
WebDisplay/deploy/manifest.yaml
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
---
|
||||||
|
kind: Deployment
|
||||||
|
apiVersion: apps/v1
|
||||||
|
metadata:
|
||||||
|
name: display
|
||||||
|
namespace: home-monitor
|
||||||
|
labels:
|
||||||
|
app: display
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: display
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: display
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: display
|
||||||
|
image: ckaczor/home-monitor-web-display:#BUILD_BUILDNUMBER#
|
||||||
|
terminationMessagePath: "/dev/termination-log"
|
||||||
|
terminationMessagePolicy: File
|
||||||
|
imagePullPolicy: Always
|
||||||
|
securityContext:
|
||||||
|
privileged: true
|
||||||
|
restartPolicy: Always
|
||||||
|
terminationGracePeriodSeconds: 30
|
||||||
|
dnsPolicy: ClusterFirst
|
||||||
|
nodeSelector:
|
||||||
|
kubernetes.io/hostname: kubernetes
|
||||||
|
schedulerName: default-scheduler
|
||||||
|
---
|
||||||
|
kind: Service
|
||||||
|
apiVersion: v1
|
||||||
|
metadata:
|
||||||
|
name: display
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- name: client
|
||||||
|
port: 80
|
||||||
|
selector:
|
||||||
|
app: display
|
||||||
|
type: ClusterIP
|
||||||
|
---
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: IngressRoute
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
kubernetes.io/ingress.class: traefik
|
||||||
|
creationTimestamp: null
|
||||||
|
name: display
|
||||||
|
namespace: home-monitor
|
||||||
|
spec:
|
||||||
|
routes:
|
||||||
|
- kind: Rule
|
||||||
|
match: PathPrefix(`/`)
|
||||||
|
middlewares:
|
||||||
|
- name: display
|
||||||
|
namespace: home-monitor
|
||||||
|
services:
|
||||||
|
- kind: Service
|
||||||
|
name: display
|
||||||
|
namespace: home-monitor
|
||||||
|
port: 80
|
||||||
|
---
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: Middleware
|
||||||
|
metadata:
|
||||||
|
creationTimestamp: null
|
||||||
|
name: display
|
||||||
|
namespace: home-monitor
|
||||||
|
spec:
|
||||||
|
stripPrefix:
|
||||||
|
prefixes:
|
||||||
|
- /
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "WebDisplay",
|
"name": "web-display",
|
||||||
"version": "0.0.0",
|
"version": "1.0.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "cross-env NODE_OPTIONS='--no-warnings' vite",
|
"dev": "cross-env NODE_OPTIONS='--no-warnings' vite",
|
||||||
"build": "vue-tsc --noEmit && vite build",
|
"build": "vue-tsc --noEmit && vite build",
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
<td>
|
<td>
|
||||||
<div
|
<div
|
||||||
v-if="weatherStore.current?.HeatIndex || weatherStore.current?.WindChill"
|
v-if="weatherStore.current?.HeatIndex || weatherStore.current?.WindChill"
|
||||||
className="weather-current-header">
|
className="weather-current-header ml-8">
|
||||||
Feels like
|
Feels like
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
@@ -14,15 +14,9 @@
|
|||||||
<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-top: 0px;
|
|
||||||
border-bottom-left-radius: 3px;
|
|
||||||
border-bottom-right-radius: 3px;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -16,7 +16,9 @@
|
|||||||
yAxisMaximum: { type: Number, required: false, default: undefined },
|
yAxisMaximum: { type: Number, required: false, default: undefined },
|
||||||
tickAmount: { type: Number, required: false, default: undefined },
|
tickAmount: { type: Number, required: false, default: undefined },
|
||||||
lineSize: { type: Number, required: false, default: 2 },
|
lineSize: { type: Number, required: false, default: 2 },
|
||||||
markerSize: { type: Number, required: false, default: 0 }
|
markerSize: { type: Number, required: false, default: 0 },
|
||||||
|
yAxisLabelFormatter: { type: Function, required: false, default: undefined },
|
||||||
|
yAxisValueFormatter: { type: Function, required: false, default: undefined }
|
||||||
});
|
});
|
||||||
|
|
||||||
var chartOptions: ApexOptions = {
|
var chartOptions: ApexOptions = {
|
||||||
@@ -48,6 +50,10 @@
|
|||||||
},
|
},
|
||||||
y: {
|
y: {
|
||||||
formatter: (value) => {
|
formatter: (value) => {
|
||||||
|
if (props.yAxisValueFormatter) {
|
||||||
|
return props.yAxisValueFormatter(value);
|
||||||
|
}
|
||||||
|
|
||||||
return `${value.toFixed(props.valueDecimalPoints)}${props.unit}`;
|
return `${value.toFixed(props.valueDecimalPoints)}${props.unit}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -69,6 +75,10 @@
|
|||||||
yaxis: {
|
yaxis: {
|
||||||
labels: {
|
labels: {
|
||||||
formatter: (value) => {
|
formatter: (value) => {
|
||||||
|
if (props.yAxisLabelFormatter) {
|
||||||
|
return props.yAxisLabelFormatter(value);
|
||||||
|
}
|
||||||
|
|
||||||
return `${value.toFixed(props.yAxisDecimalPoints)}${props.unit}`;
|
return `${value.toFixed(props.yAxisDecimalPoints)}${props.unit}`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -94,7 +104,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="chart">
|
<div class="chart">
|
||||||
<v-container
|
<v-container
|
||||||
v-if="!props.ready"
|
v-show="!props.ready"
|
||||||
class="fill-height loading">
|
class="fill-height loading">
|
||||||
<v-responsive class="align-center text-center fill-height">
|
<v-responsive class="align-center text-center fill-height">
|
||||||
<v-progress-circular
|
<v-progress-circular
|
||||||
@@ -105,7 +115,6 @@
|
|||||||
</v-responsive>
|
</v-responsive>
|
||||||
</v-container>
|
</v-container>
|
||||||
<apexchart
|
<apexchart
|
||||||
v-else
|
|
||||||
width="100%"
|
width="100%"
|
||||||
height="250"
|
height="250"
|
||||||
:type="props.type"
|
:type="props.type"
|
||||||
@@ -118,9 +127,17 @@
|
|||||||
.loading {
|
.loading {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 250px;
|
min-height: 250px;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 99;
|
||||||
|
background-color: #eeeeee;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chart {
|
.chart {
|
||||||
|
position: relative;
|
||||||
border: 1px solid lightgray;
|
border: 1px solid lightgray;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
|
import WindDirectionNumber from './wind-direction-number';
|
||||||
|
|
||||||
export default interface WindHistoryGrouped {
|
export default interface WindHistoryGrouped {
|
||||||
bucket: string;
|
bucket: string;
|
||||||
minimumSpeed: number;
|
minimumSpeed: number;
|
||||||
averageSpeed: number;
|
averageSpeed: number;
|
||||||
maximumSpeed: number;
|
maximumSpeed: number;
|
||||||
averageDirection: number;
|
averageDirection: WindDirectionNumber;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,30 +4,34 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<v-container fluid class="container">
|
<v-container
|
||||||
<v-row dense align="start">
|
fluid
|
||||||
<v-col cols="3">
|
class="container">
|
||||||
|
<v-card class="current-weather">
|
||||||
<CurrentWeather></CurrentWeather>
|
<CurrentWeather></CurrentWeather>
|
||||||
</v-col>
|
</v-card>
|
||||||
<v-col cols="3">
|
<v-card class="almanac">
|
||||||
<Almanac></Almanac>
|
<Almanac></Almanac>
|
||||||
</v-col>
|
</v-card>
|
||||||
<v-col cols="2">
|
<v-card class="current-power">
|
||||||
<CurrentPower></CurrentPower>
|
<CurrentPower></CurrentPower>
|
||||||
</v-col>
|
</v-card>
|
||||||
<v-col cols="2">
|
<v-card class="current-laundry-status">
|
||||||
<CurrentLaundryStatus></CurrentLaundryStatus>
|
<CurrentLaundryStatus></CurrentLaundryStatus>
|
||||||
</v-col>
|
</v-card>
|
||||||
<v-col cols="4">
|
<v-card class="weather-summary">
|
||||||
<WeatherSummary></WeatherSummary>
|
<WeatherSummary></WeatherSummary>
|
||||||
</v-col>
|
</v-card>
|
||||||
<v-col cols="2">
|
<v-card class="upstairs">
|
||||||
<Indoor title="Upstairs" deviceName="main"></Indoor>
|
<Indoor
|
||||||
</v-col>
|
title="Upstairs"
|
||||||
<v-col cols="2">
|
deviceName="main"></Indoor>
|
||||||
<Indoor title="Downstairs" deviceName="basement"></Indoor>
|
</v-card>
|
||||||
</v-col>
|
<v-card class="downstairs">
|
||||||
</v-row>
|
<Indoor
|
||||||
|
title="Downstairs"
|
||||||
|
deviceName="basement"></Indoor>
|
||||||
|
</v-card>
|
||||||
</v-container>
|
</v-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -36,4 +40,66 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: #fafafa;
|
background-color: #fafafa;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (min-width: 700px) {
|
||||||
|
.container {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(5, max-content);
|
||||||
|
grid-template-rows: repeat(5, max-content);
|
||||||
|
gap: 15px 15px;
|
||||||
|
grid-auto-flow: row;
|
||||||
|
grid-template-areas:
|
||||||
|
'current-weather current-weather almanac almanac current-power'
|
||||||
|
'current-weather current-weather almanac almanac current-laundry-status'
|
||||||
|
'weather-summary weather-summary weather-summary upstairs downstairs'
|
||||||
|
'weather-summary weather-summary weather-summary . .'
|
||||||
|
'. . . . .';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 700px) {
|
||||||
|
.container {
|
||||||
|
padding: 7px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(1, max-content);
|
||||||
|
grid-template-rows: repeat(7, max-content);
|
||||||
|
gap: 10px 0px;
|
||||||
|
grid-template-areas:
|
||||||
|
'current-weather'
|
||||||
|
'almanac'
|
||||||
|
'current-power'
|
||||||
|
'current-laundry-status'
|
||||||
|
'weather-summary'
|
||||||
|
'upstairs'
|
||||||
|
'downstairs';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.current-weather {
|
||||||
|
grid-area: current-weather;
|
||||||
|
}
|
||||||
|
|
||||||
|
.almanac {
|
||||||
|
grid-area: almanac;
|
||||||
|
}
|
||||||
|
|
||||||
|
.current-power {
|
||||||
|
grid-area: current-power;
|
||||||
|
}
|
||||||
|
|
||||||
|
.current-laundry-status {
|
||||||
|
grid-area: current-laundry-status;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weather-summary {
|
||||||
|
grid-area: weather-summary;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upstairs {
|
||||||
|
grid-area: upstairs;
|
||||||
|
}
|
||||||
|
|
||||||
|
.downstairs {
|
||||||
|
grid-area: downstairs;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -16,43 +16,43 @@
|
|||||||
const start = ref(subHours(end.value, 24));
|
const start = ref(subHours(end.value, 24));
|
||||||
const timeSpan = ref(TimeSpan.Last24Hours);
|
const timeSpan = ref(TimeSpan.Last24Hours);
|
||||||
|
|
||||||
const categories: number[] = [];
|
const categories = ref<number[]>([]);
|
||||||
|
|
||||||
const mainTemperatureSeries = { name: 'Upstairs', data: [] as number[] };
|
const mainTemperatureSeries = ref({ name: 'Upstairs', data: [] as number[] });
|
||||||
const basementTemperatureSeries = { name: 'Downstairs', data: [] as number[] };
|
const basementTemperatureSeries = ref({ name: 'Downstairs', data: [] as number[] });
|
||||||
|
|
||||||
const mainHumiditySeries = { name: 'Upstairs', data: [] as number[] };
|
const mainHumiditySeries = ref({ name: 'Upstairs', data: [] as number[] });
|
||||||
const basementHumiditySeries = { name: 'Downstairs', data: [] as number[] };
|
const basementHumiditySeries = ref({ name: 'Downstairs', data: [] as number[] });
|
||||||
|
|
||||||
const mainPressureSeries = { name: 'Upstairs', data: [] as number[] };
|
const mainPressureSeries = ref({ name: 'Upstairs', data: [] as number[] });
|
||||||
const basementPressureSeries = { name: 'Downstairs', data: [] as number[] };
|
const basementPressureSeries = ref({ name: 'Downstairs', data: [] as number[] });
|
||||||
|
|
||||||
const load = () => {
|
const load = () => {
|
||||||
ready.value = false;
|
ready.value = false;
|
||||||
|
|
||||||
categories.length = 0;
|
|
||||||
|
|
||||||
mainTemperatureSeries.data.length = 0;
|
|
||||||
basementTemperatureSeries.data.length = 0;
|
|
||||||
|
|
||||||
mainHumiditySeries.data.length = 0;
|
|
||||||
basementHumiditySeries.data.length = 0;
|
|
||||||
|
|
||||||
mainPressureSeries.data.length = 0;
|
|
||||||
basementPressureSeries.data.length = 0;
|
|
||||||
|
|
||||||
indoorStore.getReadingValueHistoryGrouped(start.value, end.value, 15).then((groupedReadingsList) => {
|
indoorStore.getReadingValueHistoryGrouped(start.value, end.value, 15).then((groupedReadingsList) => {
|
||||||
|
categories.value.length = 0;
|
||||||
|
|
||||||
|
mainTemperatureSeries.value.data.length = 0;
|
||||||
|
basementTemperatureSeries.value.data.length = 0;
|
||||||
|
|
||||||
|
mainHumiditySeries.value.data.length = 0;
|
||||||
|
basementHumiditySeries.value.data.length = 0;
|
||||||
|
|
||||||
|
mainPressureSeries.value.data.length = 0;
|
||||||
|
basementPressureSeries.value.data.length = 0;
|
||||||
|
|
||||||
groupedReadingsList.forEach((groupedReadings) => {
|
groupedReadingsList.forEach((groupedReadings) => {
|
||||||
if (groupedReadings.name === 'main') {
|
if (groupedReadings.name === 'main') {
|
||||||
categories.push(new Date(groupedReadings.bucket).getTime());
|
categories.value.push(new Date(groupedReadings.bucket).getTime());
|
||||||
|
|
||||||
mainTemperatureSeries.data.push(ConvertCToF(groupedReadings.averageTemperature));
|
mainTemperatureSeries.value.data.push(ConvertCToF(groupedReadings.averageTemperature));
|
||||||
mainHumiditySeries.data.push(groupedReadings.averageHumidity);
|
mainHumiditySeries.value.data.push(groupedReadings.averageHumidity);
|
||||||
mainPressureSeries.data.push(ConvertMillibarToInchesOfMercury(groupedReadings.averagePressure));
|
mainPressureSeries.value.data.push(ConvertMillibarToInchesOfMercury(groupedReadings.averagePressure));
|
||||||
} else if (groupedReadings.name === 'basement') {
|
} else if (groupedReadings.name === 'basement') {
|
||||||
basementTemperatureSeries.data.push(ConvertCToF(groupedReadings.averageTemperature));
|
basementTemperatureSeries.value.data.push(ConvertCToF(groupedReadings.averageTemperature));
|
||||||
basementHumiditySeries.data.push(groupedReadings.averageHumidity);
|
basementHumiditySeries.value.data.push(groupedReadings.averageHumidity);
|
||||||
basementPressureSeries.data.push(ConvertMillibarToInchesOfMercury(groupedReadings.averagePressure));
|
basementPressureSeries.value.data.push(ConvertMillibarToInchesOfMercury(groupedReadings.averagePressure));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -4,28 +4,28 @@
|
|||||||
import { useWeatherStore } from '@/stores/weatherStore';
|
import { useWeatherStore } from '@/stores/weatherStore';
|
||||||
import { ConvertPascalToInchesOfMercury } from '@/pressureConverter';
|
import { ConvertPascalToInchesOfMercury } from '@/pressureConverter';
|
||||||
import ValueChart from '../components/ValueChart.vue';
|
import ValueChart from '../components/ValueChart.vue';
|
||||||
import WindDirectionNumber from '@/models/weather/wind-direction-number';
|
|
||||||
import TimeRange from '@/components/TimeRange.vue';
|
import TimeRange from '@/components/TimeRange.vue';
|
||||||
import TimeSpan from '@/models/time-span';
|
import TimeSpan from '@/models/time-span';
|
||||||
|
import { ConvertDegreesToShortLabel, ConvertWindDirectionToDegrees } from '@/windConverter';
|
||||||
|
|
||||||
const weatherStore = useWeatherStore();
|
const weatherStore = useWeatherStore();
|
||||||
|
|
||||||
const readingsReady = ref(false);
|
const readingsReady = ref(false);
|
||||||
const windReady = ref(false);
|
const windReady = ref(false);
|
||||||
|
|
||||||
const readingsCategories: number[] = [];
|
const readingsCategories = ref<number[]>([]);
|
||||||
const windCategories: number[] = [];
|
const windCategories = ref<number[]>([]);
|
||||||
|
|
||||||
const temperatureSeries = { name: 'Average Temperature', data: [] as number[] };
|
const temperatureSeries = ref({ name: 'Average Temperature', data: [] as number[] });
|
||||||
const humiditySeries = { name: 'Average Humidity', data: [] as number[] };
|
const humiditySeries = ref({ name: 'Average Humidity', data: [] as number[] });
|
||||||
const pressureSeries = { name: 'Average Pressure', data: [] as number[] };
|
const pressureSeries = ref({ name: 'Average Pressure', data: [] as number[] });
|
||||||
const lightSeries = { name: 'Average Light', data: [] as number[] };
|
const lightSeries = ref({ name: 'Average Light', data: [] as number[] });
|
||||||
const rainSeries = { name: 'Total Rain', data: [] as number[] };
|
const rainSeries = ref({ name: 'Total Rain', data: [] as number[] });
|
||||||
|
|
||||||
const windMinimumSeries = { name: 'Minimum', data: [] as number[] };
|
const windMinimumSeries = ref({ name: 'Minimum', data: [] as number[] });
|
||||||
const windAverageSeries = { name: 'Average', data: [] as number[] };
|
const windAverageSeries = ref({ name: 'Average', data: [] as number[] });
|
||||||
const windMaximumSeries = { name: 'Maximum', data: [] as number[] };
|
const windMaximumSeries = ref({ name: 'Maximum', data: [] as number[] });
|
||||||
const windDirectionSeries = { name: 'Average Direction', data: [] as number[] };
|
const windDirectionSeries = ref({ name: 'Average Direction', data: [] as number[] });
|
||||||
|
|
||||||
const end = ref(new Date());
|
const end = ref(new Date());
|
||||||
const start = ref(subHours(end.value, 24));
|
const start = ref(subHours(end.value, 24));
|
||||||
@@ -33,43 +33,44 @@
|
|||||||
|
|
||||||
const load = () => {
|
const load = () => {
|
||||||
readingsReady.value = false;
|
readingsReady.value = false;
|
||||||
readingsCategories.length = 0;
|
|
||||||
temperatureSeries.data.length = 0;
|
|
||||||
humiditySeries.data.length = 0;
|
|
||||||
pressureSeries.data.length = 0;
|
|
||||||
lightSeries.data.length = 0;
|
|
||||||
rainSeries.data.length = 0;
|
|
||||||
|
|
||||||
windReady.value = false;
|
windReady.value = false;
|
||||||
windCategories.length = 0;
|
|
||||||
windMinimumSeries.data.length = 0;
|
|
||||||
windAverageSeries.data.length = 0;
|
|
||||||
windMaximumSeries.data.length = 0;
|
|
||||||
windDirectionSeries.data.length = 0;
|
|
||||||
|
|
||||||
weatherStore.getReadingHistoryGrouped(start.value, end.value, 15).then((groupedReadingsList) => {
|
weatherStore.getReadingHistoryGrouped(start.value, end.value, 15).then((groupedReadingsList) => {
|
||||||
groupedReadingsList.forEach((groupedReadings) => {
|
readingsCategories.value.length = 0;
|
||||||
readingsCategories.push(new Date(groupedReadings.bucket).getTime());
|
temperatureSeries.value.data.length = 0;
|
||||||
|
humiditySeries.value.data.length = 0;
|
||||||
|
pressureSeries.value.data.length = 0;
|
||||||
|
lightSeries.value.data.length = 0;
|
||||||
|
rainSeries.value.data.length = 0;
|
||||||
|
|
||||||
temperatureSeries.data.push(groupedReadings.averageTemperature);
|
groupedReadingsList.forEach((groupedReadings) => {
|
||||||
humiditySeries.data.push(groupedReadings.averageHumidity);
|
readingsCategories.value.push(new Date(groupedReadings.bucket).getTime());
|
||||||
pressureSeries.data.push(ConvertPascalToInchesOfMercury(groupedReadings.averagePressure));
|
|
||||||
lightSeries.data.push(groupedReadings.averageLightLevel);
|
temperatureSeries.value.data.push(groupedReadings.averageTemperature);
|
||||||
rainSeries.data.push(groupedReadings.rainTotal);
|
humiditySeries.value.data.push(groupedReadings.averageHumidity);
|
||||||
|
pressureSeries.value.data.push(ConvertPascalToInchesOfMercury(groupedReadings.averagePressure));
|
||||||
|
lightSeries.value.data.push(groupedReadings.averageLightLevel);
|
||||||
|
rainSeries.value.data.push(groupedReadings.rainTotal);
|
||||||
});
|
});
|
||||||
|
|
||||||
readingsReady.value = true;
|
readingsReady.value = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
weatherStore.getWindHistoryGrouped(start.value, end.value, 15).then((groupedReadingsList) => {
|
weatherStore.getWindHistoryGrouped(start.value, end.value, 15).then((groupedReadingsList) => {
|
||||||
|
windCategories.value.length = 0;
|
||||||
|
windMinimumSeries.value.data.length = 0;
|
||||||
|
windAverageSeries.value.data.length = 0;
|
||||||
|
windMaximumSeries.value.data.length = 0;
|
||||||
|
windDirectionSeries.value.data.length = 0;
|
||||||
|
|
||||||
groupedReadingsList.forEach((groupedReadings) => {
|
groupedReadingsList.forEach((groupedReadings) => {
|
||||||
windCategories.push(new Date(groupedReadings.bucket).getTime());
|
windCategories.value.push(new Date(groupedReadings.bucket).getTime());
|
||||||
|
|
||||||
windMinimumSeries.data.push(groupedReadings.minimumSpeed);
|
windMinimumSeries.value.data.push(groupedReadings.minimumSpeed);
|
||||||
windAverageSeries.data.push(groupedReadings.averageSpeed);
|
windAverageSeries.value.data.push(groupedReadings.averageSpeed);
|
||||||
windMaximumSeries.data.push(groupedReadings.maximumSpeed);
|
windMaximumSeries.value.data.push(groupedReadings.maximumSpeed);
|
||||||
|
|
||||||
windDirectionSeries.data.push(groupedReadings.averageDirection);
|
windDirectionSeries.value.data.push(ConvertWindDirectionToDegrees(groupedReadings.averageDirection));
|
||||||
});
|
});
|
||||||
|
|
||||||
windReady.value = true;
|
windReady.value = true;
|
||||||
@@ -200,11 +201,11 @@
|
|||||||
title="Wind Direction"
|
title="Wind Direction"
|
||||||
unit=""
|
unit=""
|
||||||
group="outdoor"
|
group="outdoor"
|
||||||
:tick-amount="3"
|
:tick-amount="4"
|
||||||
:marker-size="3"
|
:y-axis-minimum="0"
|
||||||
:line-size="0"
|
:y-axis-maximum="360"
|
||||||
:y-axis-minimum="WindDirectionNumber.Min"
|
:y-axis-label-formatter="ConvertDegreesToShortLabel"
|
||||||
:y-axis-maximum="WindDirectionNumber.Max"
|
:y-axis-value-formatter="ConvertDegreesToShortLabel"
|
||||||
:categories="windCategories"
|
:categories="windCategories"
|
||||||
:series="[windDirectionSeries]">
|
:series="[windDirectionSeries]">
|
||||||
</ValueChart>
|
</ValueChart>
|
||||||
|
|||||||
@@ -14,13 +14,13 @@
|
|||||||
const powerReady = ref(false);
|
const powerReady = ref(false);
|
||||||
const lightReady = ref(false);
|
const lightReady = ref(false);
|
||||||
|
|
||||||
const powerCategories: number[] = [];
|
const powerCategories = ref<number[]>([]);
|
||||||
const lightCategories: number[] = [];
|
const lightCategories = ref<number[]>([]);
|
||||||
|
|
||||||
const generationSeries = { name: 'Generation', data: [] as number[] };
|
const generationSeries = ref({ name: 'Generation', data: [] as number[] });
|
||||||
const consumptionSeries = { name: 'Consumption', data: [] as number[] };
|
const consumptionSeries = ref({ name: 'Consumption', data: [] as number[] });
|
||||||
|
|
||||||
const lightSeries = { name: 'Average Light', data: [] as number[] };
|
const lightSeries = ref({ name: 'Average Light', data: [] as number[] });
|
||||||
|
|
||||||
const end = ref(new Date());
|
const end = ref(new Date());
|
||||||
const start = ref(subHours(end.value, 24));
|
const start = ref(subHours(end.value, 24));
|
||||||
@@ -28,30 +28,31 @@
|
|||||||
|
|
||||||
const load = () => {
|
const load = () => {
|
||||||
powerReady.value = false;
|
powerReady.value = false;
|
||||||
powerCategories.length = 0;
|
|
||||||
generationSeries.data.length = 0;
|
|
||||||
consumptionSeries.data.length = 0;
|
|
||||||
|
|
||||||
lightReady.value = false;
|
lightReady.value = false;
|
||||||
lightCategories.length = 0;
|
|
||||||
lightSeries.data.length = 0;
|
|
||||||
|
|
||||||
powerStore.getReadingHistoryGrouped(start.value, end.value, 15).then((groupedReadingsList) => {
|
powerStore.getReadingHistoryGrouped(start.value, end.value, 15).then((groupedReadingsList) => {
|
||||||
groupedReadingsList.forEach((groupedReadings) => {
|
powerCategories.value.length = 0;
|
||||||
powerCategories.push(new Date(groupedReadings.bucket).getTime());
|
generationSeries.value.data.length = 0;
|
||||||
|
consumptionSeries.value.data.length = 0;
|
||||||
|
|
||||||
generationSeries.data.push(groupedReadings.averageGeneration);
|
groupedReadingsList.forEach((groupedReadings) => {
|
||||||
consumptionSeries.data.push(groupedReadings.averageConsumption);
|
powerCategories.value.push(new Date(groupedReadings.bucket).getTime());
|
||||||
|
|
||||||
|
generationSeries.value.data.push(groupedReadings.averageGeneration);
|
||||||
|
consumptionSeries.value.data.push(groupedReadings.averageConsumption);
|
||||||
});
|
});
|
||||||
|
|
||||||
powerReady.value = true;
|
powerReady.value = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
weatherStore.getReadingValueHistoryGrouped(WeatherValueType.Light, start.value, end.value, 15).then((groupedReadingsList) => {
|
weatherStore.getReadingValueHistoryGrouped(WeatherValueType.Light, start.value, end.value, 15).then((groupedReadingsList) => {
|
||||||
groupedReadingsList.forEach((groupedReadings) => {
|
lightCategories.value.length = 0;
|
||||||
lightCategories.push(new Date(groupedReadings.bucket).getTime());
|
lightSeries.value.data.length = 0;
|
||||||
|
|
||||||
lightSeries.data.push(groupedReadings.averageValue);
|
groupedReadingsList.forEach((groupedReadings) => {
|
||||||
|
lightCategories.value.push(new Date(groupedReadings.bucket).getTime());
|
||||||
|
|
||||||
|
lightSeries.value.data.push(groupedReadings.averageValue);
|
||||||
});
|
});
|
||||||
|
|
||||||
lightReady.value = true;
|
lightReady.value = true;
|
||||||
|
|||||||
81
WebDisplay/src/windConverter.ts
Normal file
81
WebDisplay/src/windConverter.ts
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
import WindDirectionNumber from './models/weather/wind-direction-number';
|
||||||
|
|
||||||
|
export function ConvertWindDirectionToDegrees(windDirection: WindDirectionNumber): number {
|
||||||
|
switch (windDirection) {
|
||||||
|
case WindDirectionNumber.None:
|
||||||
|
return -1;
|
||||||
|
case WindDirectionNumber.North:
|
||||||
|
return 0;
|
||||||
|
case WindDirectionNumber.East:
|
||||||
|
return 90;
|
||||||
|
case WindDirectionNumber.South:
|
||||||
|
return 180;
|
||||||
|
case WindDirectionNumber.West:
|
||||||
|
return 270;
|
||||||
|
case WindDirectionNumber.NorthEast:
|
||||||
|
return 45;
|
||||||
|
case WindDirectionNumber.SouthEast:
|
||||||
|
return 135;
|
||||||
|
case WindDirectionNumber.SouthWest:
|
||||||
|
return 225;
|
||||||
|
case WindDirectionNumber.NorthWest:
|
||||||
|
return 315;
|
||||||
|
case WindDirectionNumber.NorthNorthEast:
|
||||||
|
return 22.5;
|
||||||
|
case WindDirectionNumber.EastNorthEast:
|
||||||
|
return 67.5;
|
||||||
|
case WindDirectionNumber.EastSouthEast:
|
||||||
|
return 112.5;
|
||||||
|
case WindDirectionNumber.SouthSouthEast:
|
||||||
|
return 157.5;
|
||||||
|
case WindDirectionNumber.SouthSouthWest:
|
||||||
|
return 202.5;
|
||||||
|
case WindDirectionNumber.WestSouthWest:
|
||||||
|
return 247.5;
|
||||||
|
case WindDirectionNumber.WestNorthWest:
|
||||||
|
return 292.5;
|
||||||
|
case WindDirectionNumber.NorthNorthWest:
|
||||||
|
return 337.5;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ConvertDegreesToShortLabel(degrees: number): string {
|
||||||
|
switch (degrees) {
|
||||||
|
case 0:
|
||||||
|
case 360:
|
||||||
|
return 'N';
|
||||||
|
case 90:
|
||||||
|
return 'E';
|
||||||
|
case 180:
|
||||||
|
return 'S';
|
||||||
|
case 270:
|
||||||
|
return 'W';
|
||||||
|
case 45:
|
||||||
|
return 'NE';
|
||||||
|
case 135:
|
||||||
|
return 'SE';
|
||||||
|
case 225:
|
||||||
|
return 'SW';
|
||||||
|
case 315:
|
||||||
|
return 'NW';
|
||||||
|
case 22.5:
|
||||||
|
return 'NNE';
|
||||||
|
case 67.5:
|
||||||
|
return 'ENE';
|
||||||
|
case 112.5:
|
||||||
|
return 'ESE';
|
||||||
|
case 157.5:
|
||||||
|
return 'SSE';
|
||||||
|
case 202.5:
|
||||||
|
return 'SSW';
|
||||||
|
case 247.5:
|
||||||
|
return 'WSW';
|
||||||
|
case 292.5:
|
||||||
|
return 'WNW';
|
||||||
|
case 337.5:
|
||||||
|
return 'NNW';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
"@/*": ["src/*"]
|
"@/*": ["src/*"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
|
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue", "typed-router.d.ts"],
|
||||||
"references": [{ "path": "./tsconfig.node.json" }],
|
"references": [{ "path": "./tsconfig.node.json" }],
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user