ADD: Temperatur and Weather Overview
This commit is contained in:
parent
9bcd4e2e39
commit
af273889d8
33
package-lock.json
generated
33
package-lock.json
generated
@ -16,6 +16,8 @@
|
||||
"@angular/material": "^22.0.2",
|
||||
"@angular/platform-browser": "^22.0.0",
|
||||
"@angular/router": "^22.0.0",
|
||||
"date-fns": "^4.4.0",
|
||||
"ng-apexcharts": "^2.4.0",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
@ -4533,6 +4535,13 @@
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/apexcharts": {
|
||||
"version": "5.15.2",
|
||||
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-5.15.2.tgz",
|
||||
"integrity": "sha512-qbd+ehDiRxo++wAqaGLmJcd683ktulTqoku4WYYyQ3XOgJIn4yhOQGV27KcdK7c2yhwxblyh4mgMm8ukI0wC0Q==",
|
||||
"license": "SEE LICENSE IN LICENSE",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/assertion-error": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
|
||||
@ -5308,6 +5317,16 @@
|
||||
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/date-fns": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.4.0.tgz",
|
||||
"integrity": "sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/kossnocorp"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.3",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
||||
@ -7349,6 +7368,20 @@
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ng-apexcharts": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/ng-apexcharts/-/ng-apexcharts-2.4.0.tgz",
|
||||
"integrity": "sha512-bNXIOQrux9ZXnpm4kxbW8M6AA4ENAEOpR31ZoaeBkc0SrfOMw1nxRN5ORkStOQFVQF5Wf5qivAA9KIgU7Ol26A==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.8.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/common": ">=20.0.0",
|
||||
"@angular/core": ">=20.0.0",
|
||||
"apexcharts": "^5.10.3",
|
||||
"rxjs": "^7.8.2"
|
||||
}
|
||||
},
|
||||
"node_modules/node-addon-api": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz",
|
||||
|
||||
@ -19,6 +19,8 @@
|
||||
"@angular/material": "^22.0.2",
|
||||
"@angular/platform-browser": "^22.0.0",
|
||||
"@angular/router": "^22.0.0",
|
||||
"date-fns": "^4.4.0",
|
||||
"ng-apexcharts": "^2.4.0",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
|
||||
@ -25,7 +25,10 @@
|
||||
<mat-drawer mode="side" opened>
|
||||
<app-station-list></app-station-list>
|
||||
</mat-drawer>
|
||||
<mat-drawer-content>Main Cointent</mat-drawer-content>
|
||||
<mat-drawer-content>
|
||||
<app-wettter-overview></app-wettter-overview>
|
||||
<app-temperature></app-temperature>
|
||||
</mat-drawer-content>
|
||||
</mat-drawer-container>
|
||||
|
||||
|
||||
|
||||
@ -5,10 +5,12 @@ import {MatIcon} from '@angular/material/icon';
|
||||
import {MatButton, MatIconButton} from '@angular/material/button';
|
||||
import {MatDrawer, MatDrawerContainer, MatDrawerContent} from '@angular/material/sidenav';
|
||||
import {StationList} from './station-list/station-list';
|
||||
import {WettterOverview} from './components/wettter-overview/wettter-overview';
|
||||
import {TemperatureComponent} from './components/diagrams/temperature/temperature';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
imports: [RouterOutlet, MatToolbar, MatIcon, MatIconButton, RouterLink, MatButton, MatDrawerContainer, MatDrawer, MatDrawerContent, StationList],
|
||||
imports: [RouterOutlet, MatToolbar, MatIcon, MatIconButton, RouterLink, MatButton, MatDrawerContainer, MatDrawer, MatDrawerContent, StationList, WettterOverview, TemperatureComponent],
|
||||
templateUrl: './app.html',
|
||||
styleUrl: './app.css'
|
||||
})
|
||||
|
||||
21
src/app/components/diagrams/temperature/temperature.html
Normal file
21
src/app/components/diagrams/temperature/temperature.html
Normal file
@ -0,0 +1,21 @@
|
||||
@if(measurements) {
|
||||
<mat-card>
|
||||
<mat-card-header>
|
||||
<mat-card-title>Temperaturverlauf</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<apx-chart
|
||||
[series]="chartOptions.series!"
|
||||
[chart]="chartOptions.chart!"
|
||||
[xaxis]="chartOptions.xaxis!"
|
||||
[yaxis]="chartOptions.yaxis!"
|
||||
[stroke]="chartOptions.stroke!"
|
||||
[grid]="chartOptions.grid!"
|
||||
[tooltip]="chartOptions.tooltip!"
|
||||
[legend]="chartOptions.legend!"
|
||||
[theme]="chartOptions.theme!"
|
||||
/>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
|
||||
}
|
||||
22
src/app/components/diagrams/temperature/temperature.spec.ts
Normal file
22
src/app/components/diagrams/temperature/temperature.spec.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { Temperature } from './temperature';
|
||||
|
||||
describe('Temperature', () => {
|
||||
let component: Temperature;
|
||||
let fixture: ComponentFixture<Temperature>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [Temperature],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(Temperature);
|
||||
component = fixture.componentInstance;
|
||||
await fixture.whenStable();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
120
src/app/components/diagrams/temperature/temperature.ts
Normal file
120
src/app/components/diagrams/temperature/temperature.ts
Normal file
@ -0,0 +1,120 @@
|
||||
import {ChangeDetectorRef, Component, Input, OnInit} from '@angular/core';
|
||||
import {DefaultService, MeasurementListResponse, MeasurementResolution, StationListResponse} from '../../../api';
|
||||
import {addDays, addHours, addMonths, addYears, startOfDay} from 'date-fns';
|
||||
import {ApexOptions, ChartComponent} from 'ng-apexcharts';
|
||||
import {MatCard, MatCardContent, MatCardHeader, MatCardTitle} from '@angular/material/card';
|
||||
import {MatProgressSpinner} from '@angular/material/progress-spinner';
|
||||
|
||||
export type TimeResolution = '24h' | '7d' | '30d' | '1Y' | 'All'
|
||||
|
||||
@Component({
|
||||
selector: 'app-temperature',
|
||||
imports: [
|
||||
ChartComponent,
|
||||
MatCard,
|
||||
MatCardContent,
|
||||
MatCardHeader,
|
||||
MatCardTitle,
|
||||
],
|
||||
templateUrl: './temperature.html',
|
||||
styleUrl: './temperature.css',
|
||||
})
|
||||
export class TemperatureComponent implements OnInit {
|
||||
@Input() timeResolution: TimeResolution = '7d';
|
||||
@Input() activeStations: StationListResponse[] = [];
|
||||
|
||||
resolution: MeasurementResolution = MeasurementResolution.Daily;
|
||||
measurements: MeasurementListResponse | undefined;
|
||||
chartOptions: ApexOptions = {};
|
||||
|
||||
constructor(private api: DefaultService, private cdr: ChangeDetectorRef) {}
|
||||
|
||||
ngOnInit() {
|
||||
if (this.activeStations.length === 0) {
|
||||
this.api.listStationsStationsListGet().subscribe(stations => {
|
||||
this.activeStations = stations;
|
||||
this.fetchMeasurements();
|
||||
});
|
||||
} else {
|
||||
this.fetchMeasurements();
|
||||
}
|
||||
}
|
||||
|
||||
get timeIntervalStart(): string | undefined {
|
||||
const now = new Date();
|
||||
const map: Record<TimeResolution, Date | undefined> = {
|
||||
'24h': addHours(now, -24),
|
||||
'7d': startOfDay(addDays(now, -7)),
|
||||
'30d': startOfDay(addMonths(now, -1)),
|
||||
'1Y': startOfDay(addYears(now, -1)),
|
||||
'All': undefined
|
||||
};
|
||||
return map[this.timeResolution]?.toISOString();
|
||||
}
|
||||
|
||||
fetchMeasurements() {
|
||||
const stationIds = this.activeStations.map(s => s.id);
|
||||
const now = new Date().toISOString();
|
||||
|
||||
this.api.getMeasurementsMeasurementsGet(
|
||||
stationIds, undefined, this.timeIntervalStart, now, this.resolution
|
||||
).subscribe(data => {
|
||||
this.measurements = data;
|
||||
this.buildChart();
|
||||
});
|
||||
}
|
||||
|
||||
buildChart() {
|
||||
if (!this.measurements) return;
|
||||
|
||||
this.chartOptions = {
|
||||
chart: {
|
||||
type: 'line',
|
||||
height: 280,
|
||||
toolbar: { show: false },
|
||||
animations: { enabled: true },
|
||||
background: 'transparent',
|
||||
},
|
||||
theme: { mode: 'dark' },
|
||||
stroke: {
|
||||
curve: 'smooth',
|
||||
width: 2,
|
||||
},
|
||||
series: this.measurements.stations.map(s => ({
|
||||
name: `${s.station.name} ${s.indoor ? '(innen)' : '(außen)'}`,
|
||||
data: s.measurements.map(m => ({
|
||||
x: new Date(m.timestamp).getTime(),
|
||||
y: m.temperature
|
||||
}))
|
||||
})),
|
||||
xaxis: {
|
||||
type: 'datetime',
|
||||
labels: {
|
||||
datetimeUTC: false,
|
||||
style: { colors: '#9e9e9e', fontSize: '11px' }
|
||||
},
|
||||
axisBorder: { show: false },
|
||||
axisTicks: { show: false },
|
||||
},
|
||||
yaxis: {
|
||||
labels: {
|
||||
style: { colors: '#9e9e9e', fontSize: '11px' },
|
||||
formatter: (val: number) => `${val.toFixed(1)}°`
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
borderColor: 'rgba(255,255,255,0.06)',
|
||||
strokeDashArray: 4,
|
||||
},
|
||||
tooltip: {
|
||||
theme: 'dark',
|
||||
x: { format: 'dd.MM.yyyy HH:mm' },
|
||||
y: { formatter: (val: number) => `${val.toFixed(1)} °C` }
|
||||
},
|
||||
legend: {
|
||||
labels: { colors: '#9e9e9e' }
|
||||
},
|
||||
};
|
||||
this.cdr.detectChanges()
|
||||
}
|
||||
}
|
||||
85
src/app/components/wettter-overview/wettter-overview.css
Normal file
85
src/app/components/wettter-overview/wettter-overview.css
Normal file
@ -0,0 +1,85 @@
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
||||
gap: 16px;
|
||||
margin: 2em;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background: #1e1e1e !important;
|
||||
color: #fff;
|
||||
border-radius: 14px;
|
||||
padding: 18px;
|
||||
box-shadow:
|
||||
0 4px 10px rgba(0, 0, 0, .35),
|
||||
inset 0 1px 0 rgba(255,255,255,.04);
|
||||
transition: all .2s ease;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow:
|
||||
0 8px 18px rgba(0,0,0,.45),
|
||||
inset 0 1px 0 rgba(255,255,255,.05);
|
||||
}
|
||||
}
|
||||
|
||||
.label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
|
||||
color: #9e9e9e;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.label mat-icon {
|
||||
font-size: 16px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
color: #8a8a8a;
|
||||
}
|
||||
|
||||
.value {
|
||||
margin-top: 8px;
|
||||
font-size: 2rem;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
|
||||
.change {
|
||||
margin-top: 10px;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
|
||||
font-size: .82rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.change mat-icon {
|
||||
font-size: 15px;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
}
|
||||
|
||||
.positive {
|
||||
color: #37d67a;
|
||||
}
|
||||
|
||||
.negative {
|
||||
color: #ff6b6b;
|
||||
}
|
||||
|
||||
.neutral {
|
||||
color: #9e9e9e;
|
||||
}
|
||||
|
||||
.forecast {
|
||||
margin-top: 6px;
|
||||
font-size: .78rem;
|
||||
color: #9e9e9e;
|
||||
font-style: italic;
|
||||
}
|
||||
85
src/app/components/wettter-overview/wettter-overview.html
Normal file
85
src/app/components/wettter-overview/wettter-overview.html
Normal file
@ -0,0 +1,85 @@
|
||||
<div class="stats-grid">
|
||||
<mat-card class="stat-card">
|
||||
<div class="label">
|
||||
<mat-icon>thermostat</mat-icon>
|
||||
Außen
|
||||
</div>
|
||||
|
||||
<div class="value">{{out_temperature}} °C</div>
|
||||
|
||||
@if (outer_temperature_diff) {
|
||||
@if (outer_temperature_diff > 0) {
|
||||
<div class="change negative">
|
||||
<mat-icon>arrow_upward</mat-icon>
|
||||
{{outer_temperature_diff | number : '1.1-2'}}° vs. gestern
|
||||
</div>
|
||||
} @else {
|
||||
<div class="change positive">
|
||||
<mat-icon>arrow_downward</mat-icon>
|
||||
{{outer_temperature_diff | number : '1.1-2'}}° vs. gestern
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
</mat-card>
|
||||
|
||||
<mat-card class="stat-card">
|
||||
<div class="label">
|
||||
<mat-icon>home</mat-icon>
|
||||
Innen
|
||||
</div>
|
||||
|
||||
<div class="value">{{inner_temperature | number: '1.1-2'}} °C</div>
|
||||
|
||||
@if (inner_temperature_diff) {
|
||||
@if (inner_temperature_diff > 0) {
|
||||
<div class="change negative">
|
||||
<mat-icon>arrow_upward</mat-icon>
|
||||
{{inner_temperature_diff | number : '1.1-2'}}° vs. gestern
|
||||
</div>
|
||||
} @else {
|
||||
<div class="change positive">
|
||||
<mat-icon>arrow_downward</mat-icon>
|
||||
{{inner_temperature_diff | number : '1.1-2'}}° vs. gestern
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</mat-card>
|
||||
|
||||
<mat-card class="stat-card">
|
||||
<div class="label">
|
||||
<mat-icon>water_drop</mat-icon>
|
||||
Luftfeucht.
|
||||
</div>
|
||||
|
||||
<div class="value">{{humidity | number: '1.1-2'}} %</div>
|
||||
|
||||
@if (humidity_diff) {
|
||||
@if (humidity_diff > 0) {
|
||||
<div class="change negative">
|
||||
<mat-icon>arrow_upward</mat-icon>
|
||||
{{humidity_diff | number : '1.1-2'}}% vs. gestern
|
||||
</div>
|
||||
} @else {
|
||||
<div class="change positive">
|
||||
<mat-icon>arrow_downward</mat-icon>
|
||||
{{humidity_diff | number : '1.1-2'}}% vs. gestern
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</mat-card>
|
||||
|
||||
<mat-card class="stat-card">
|
||||
<div class="label">
|
||||
<mat-icon>speed</mat-icon>
|
||||
Luftdruck
|
||||
</div>
|
||||
<div class="value">{{pressure}} hPa</div>
|
||||
@if(pressure) {
|
||||
<div class="change neutral">
|
||||
<mat-icon>{{pressureTrendIcon}}</mat-icon>
|
||||
<div class="forecast">{{weatherForecast}}</div>
|
||||
</div>
|
||||
}
|
||||
</mat-card>
|
||||
</div>
|
||||
22
src/app/components/wettter-overview/wettter-overview.spec.ts
Normal file
22
src/app/components/wettter-overview/wettter-overview.spec.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { WettterOverview } from './wettter-overview';
|
||||
|
||||
describe('WettterOverview', () => {
|
||||
let component: WettterOverview;
|
||||
let fixture: ComponentFixture<WettterOverview>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [WettterOverview],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(WettterOverview);
|
||||
component = fixture.componentInstance;
|
||||
await fixture.whenStable();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
147
src/app/components/wettter-overview/wettter-overview.ts
Normal file
147
src/app/components/wettter-overview/wettter-overview.ts
Normal file
@ -0,0 +1,147 @@
|
||||
import {ChangeDetectorRef, Component, OnInit} from '@angular/core';
|
||||
import {DefaultService, MeasurementListResponse, MeasurementResolution, StationMeasurementResponse} from '../../api';
|
||||
import {MatCard} from '@angular/material/card';
|
||||
import {MatIcon} from '@angular/material/icon';
|
||||
import {addDays, endOfDay, startOfDay} from 'date-fns';
|
||||
import {DecimalPipe} from '@angular/common';
|
||||
|
||||
@Component({
|
||||
selector: 'app-wettter-overview',
|
||||
imports: [
|
||||
MatCard,
|
||||
MatIcon,
|
||||
DecimalPipe
|
||||
],
|
||||
templateUrl: './wettter-overview.html',
|
||||
styleUrl: './wettter-overview.css',
|
||||
})
|
||||
export class WettterOverview implements OnInit {
|
||||
|
||||
last_measurement: MeasurementListResponse | undefined
|
||||
out_temperature: number | undefined
|
||||
inner_temperature: number | undefined
|
||||
humidity: number | undefined
|
||||
pressure: number | undefined
|
||||
|
||||
outer_temperature_diff: number | undefined
|
||||
inner_temperature_diff: number | undefined
|
||||
humidity_diff: number | undefined
|
||||
pressureDiff: number = 0
|
||||
|
||||
change_class(yesterday_val: number | undefined, today_val: number | undefined) {
|
||||
if (yesterday_val && today_val) {
|
||||
return yesterday_val <= today_val ? 'negative' : 'positive'
|
||||
}
|
||||
return 'neutral'
|
||||
}
|
||||
|
||||
constructor(private apiService: DefaultService, private cdr: ChangeDetectorRef) {}
|
||||
|
||||
ngOnInit() {
|
||||
const today = startOfDay(new Date());
|
||||
const yesterdayStart = addDays(today, -1);
|
||||
const yesterdayEnd = endOfDay(yesterdayStart);
|
||||
|
||||
this.apiService.getMeasurementsMeasurementsGet(undefined, undefined, undefined, undefined, MeasurementResolution.Raw, 1).subscribe(measurements => {
|
||||
const outdoor_stations = measurements.stations.filter(s => !s.indoor)
|
||||
this.out_temperature = this.computeAvgTemperature(outdoor_stations, false)
|
||||
this.inner_temperature = this.computeAvgTemperature(measurements.stations, true)
|
||||
|
||||
this.humidity = this.computeAvgHumidity(measurements.stations, true)
|
||||
this.pressure = this.computeAvgPressure(measurements.stations, false)
|
||||
|
||||
this.apiService.getMeasurementsMeasurementsGet(undefined, undefined, yesterdayStart.toISOString(), yesterdayEnd.toISOString(), MeasurementResolution.Daily, 1).subscribe(measurements => {
|
||||
const outdoor_stations = measurements.stations.filter(s => !s.indoor)
|
||||
|
||||
const yesterday_outer_temperature = this.computeAvgTemperature(outdoor_stations, false)
|
||||
this.outer_temperature_diff = yesterday_outer_temperature - this.out_temperature!
|
||||
|
||||
const yesterday_inner_temperature = this.computeAvgTemperature(measurements.stations, true)
|
||||
this.inner_temperature_diff = yesterday_inner_temperature - this.inner_temperature!
|
||||
|
||||
const yesterday_humidity = this.computeAvgHumidity(measurements.stations, true)
|
||||
this.humidity_diff = yesterday_humidity - this.humidity!
|
||||
|
||||
this.cdr.detectChanges();
|
||||
})
|
||||
})
|
||||
|
||||
this.apiService.listStationsStationsListGet().subscribe(stations => {
|
||||
const outer_stations = stations.find(s => !s.indoor_only)
|
||||
if (outer_stations) {
|
||||
const now = new Date()
|
||||
const threeHoursAgo = addDays(now, -3)
|
||||
|
||||
this.apiService.getMeasurementsMeasurementsGet([outer_stations.id], false, threeHoursAgo.toISOString(), now.toISOString(), MeasurementResolution.Hourly).subscribe(measurements => {
|
||||
const station_measurements = measurements.stations[0].measurements
|
||||
station_measurements.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime())
|
||||
|
||||
const recent = station_measurements[station_measurements.length - 1].pressure; // neueste
|
||||
const older = station_measurements[0].pressure; // älteste
|
||||
|
||||
if (recent == null || older == null) {
|
||||
this.pressureDiff = 0
|
||||
} else {
|
||||
this.pressureDiff = recent - older
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
get pressureTrendIcon(): string {
|
||||
if (this.pressureDiff > 1.5) return 'trending_up';
|
||||
if (this.pressureDiff < -1.5) return 'trending_down';
|
||||
return 'trending_flat';
|
||||
}
|
||||
|
||||
get pressureTrend(): string {
|
||||
if (this.pressureDiff > 1.5) return `+${this.pressureDiff.toFixed(1)} hPa · steigend`;
|
||||
if (this.pressureDiff < -1.5) return `${this.pressureDiff.toFixed(1)} hPa · fallend`;
|
||||
return 'stabil';
|
||||
}
|
||||
|
||||
get pressureTrendClass(): string {
|
||||
if (this.pressureDiff > 1.5) return 'positive';
|
||||
if (this.pressureDiff < -1.5) return 'negative';
|
||||
return 'neutral';
|
||||
}
|
||||
|
||||
get weatherForecast(): string {
|
||||
return this.getWeatherForecast(this.pressure!, this.pressureDiff);
|
||||
}
|
||||
|
||||
getWeatherForecast(current: number, diff: number): string {
|
||||
const absDiff = Math.abs(diff);
|
||||
const rising = diff > 0;
|
||||
|
||||
if (absDiff <= 1.5) {
|
||||
return current > 1015 ? '☀️ Beständig schön' :
|
||||
current < 1000 ? '🌧️ Beständig trüb' : '🌤️ Gleichbleibendes Wetter';
|
||||
}
|
||||
if (absDiff <= 3) {
|
||||
return rising ? '🌤️ Wetterbesserung möglich' : '🌦️ Wetterverschlechterung möglich';
|
||||
}
|
||||
if (absDiff <= 6) {
|
||||
return rising ? '☀️ Wetterbesserung wahrscheinlich' : '🌧️ Regen wahrscheinlich';
|
||||
}
|
||||
// > 6 hPa
|
||||
return rising ? '☀️ Markante Wetterbesserung' : '⛈️ Sturm möglich';
|
||||
}
|
||||
|
||||
computeAvgTemperature(stations: StationMeasurementResponse[], inner_measurements: boolean) {
|
||||
const temperatures = stations.filter(s => s.indoor === inner_measurements).map(s => s.measurements[0].temperature);
|
||||
return temperatures.reduce((sum, t) => sum + t, 0) / temperatures.length;
|
||||
}
|
||||
|
||||
computeAvgHumidity(stations: StationMeasurementResponse[], inner_measurements: boolean) {
|
||||
const humidity = stations.filter(s => s.indoor === inner_measurements).map(s => s.measurements[0].humidity ? s.measurements[0].humidity : 0);
|
||||
return humidity.reduce((sum, t) => sum + t, 0) / humidity.length;
|
||||
}
|
||||
|
||||
computeAvgPressure(stations: StationMeasurementResponse[], inner_measurements: boolean) {
|
||||
const pressure = stations.filter(s=> s.indoor === inner_measurements).map(s => s.measurements[0].pressure ? s.measurements[0].pressure : 0)
|
||||
return pressure.reduce((sum, t) => sum + t, 0) / pressure.length;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user