Compare commits

..

No commits in common. "4feb38c14cab728b4b7fa2d49681adec037cfea0" and "04be7692c1f1b3e35e88edb49df8c631b7bb71b7" have entirely different histories.

15 changed files with 52 additions and 223 deletions

View File

@ -4,20 +4,6 @@ import {Value} from '../series/Value';
export class Location { export class Location {
powerSelf: Value = Value.NULL;
powerPurchasePercentConsume: Value = Value.NULL;
powerProducePercentConsume: Value = Value.NULL;
powerDeliveryPercentConsume: Value = Value.NULL;
powerDeliveryPercentProduce: Value = Value.NULL;
powerSelfPercentConsume: Value = Value.NULL;
powerSelfPercentProduce: Value = Value.NULL;
constructor( constructor(
readonly id: number, readonly id: number,
readonly name: string, readonly name: string,
@ -60,13 +46,6 @@ export class Location {
private updateConsume() { private updateConsume() {
this._powerConsume = Value.ZERO.plus(this._powerPurchase?.value, true).plus(this._powerProduce?.value, true).minus(this._powerDeliver?.value, true); this._powerConsume = Value.ZERO.plus(this._powerPurchase?.value, true).plus(this._powerProduce?.value, true).minus(this._powerDeliver?.value, true);
this.powerSelf = Value.ZERO.plus(this.powerProduce?.value.minus(this.powerDeliver?.value, true), true);
this.powerPurchasePercentConsume = Value.ZERO.plus(this.powerPurchase?.value.percent(this.powerConsume, "%", 0), true);
this.powerProducePercentConsume = Value.ZERO.plus(this.powerProduce?.value.percent(this.powerConsume, "%", 0), true);
this.powerDeliveryPercentConsume = Value.ZERO.plus(this.powerDeliver?.value.percent(this.powerConsume, "%", 0), true);
this.powerDeliveryPercentProduce = Value.ZERO.plus(this.powerDeliver?.value.percent(this.powerProduce?.value, "%", 0), true);
this.powerSelfPercentConsume = Value.ZERO.plus(this.powerSelf.percent(this.powerConsume, "%", 0), true);
this.powerSelfPercentProduce = Value.ZERO.plus(this.powerSelf.percent(this.powerProduce?.value, "%", 0), true);
} }
static fromJson(json: any): Location { static fromJson(json: any): Location {

View File

@ -2,32 +2,21 @@
<app-location-power [location]="location"></app-location-power> <app-location-power [location]="location"></app-location-power>
<app-location-energy [location]="location" [interval]="Interval.DAY" [offset]="offsetDay"> <app-location-energy [location]="location" [interval]="Interval.DAY" heading="Heute"></app-location-energy>
<app-location-energy [location]="location" [interval]="Interval.DAY" [offset]="offset">
<ng-content #SeriesHistoryHeading> <ng-content #SeriesHistoryHeading>
<div style="display: flex; width: 100%"> <div style="display: flex; width: 100%">
&nbsp; &nbsp;
<div (click)="offsetDay += 1">&larr;</div> <div (click)="offset += 1">&larr;</div>
&nbsp; &nbsp;
<div (click)="offsetDay = Math.max(0, offsetDay -1)">&rarr;</div> <div (click)="offset = Math.max(1, offset -1)">&rarr;</div>
&nbsp; &nbsp;
<div style="flex: 1">{{ offsetDayTitle() }}</div> <div style="flex: 1">{{ offsetDayTitle() }}</div>
</div> </div>
</ng-content> </ng-content>
</app-location-energy> </app-location-energy>
<app-location-energy [location]="location" [interval]="Interval.MONTH" [offset]="offsetMonth">
<ng-content #SeriesHistoryHeading>
<div style="display: flex; width: 100%">
&nbsp;
<div (click)="offsetMonth += 1">&larr;</div>
&nbsp;
<div (click)="offsetMonth = Math.max(0, offsetMonth -1)">&rarr;</div>
&nbsp;
<div style="flex: 1">{{ offsetMonthTitle() }}</div>
</div>
</ng-content>
</app-location-energy>
<div class="Section"> <div class="Section">
<div class="SectionHeading"> <div class="SectionHeading">
<div class="SectionHeadingText"> <div class="SectionHeadingText">

View File

@ -41,9 +41,7 @@ export class LocationDetail implements OnInit, OnDestroy {
private readonly subs: Subscription [] = []; private readonly subs: Subscription [] = [];
protected offsetDay: number = 0; protected offset: number = 1;
protected offsetMonth: number = 0;
private readonly datePipe: DatePipe; private readonly datePipe: DatePipe;
@ -78,21 +76,16 @@ export class LocationDetail implements OnInit, OnDestroy {
} }
protected offsetDayTitle(): string { protected offsetDayTitle(): string {
if (this.offsetDay === 0) { if (this.offset === 1) {
return 'Heute';
} else if (this.offsetDay === 1) {
return 'Gestern'; return 'Gestern';
} else { } else {
const d = new Date(this.dateService.now); if (this.offset < 7) {
d.setDate(d.getDate() - this.offsetDay); const d = new Date(this.dateService.now);
return this.datePipe.transform(d, 'dd.MM.yyyy EEEE') || ''; d.setDate(d.getDate() - this.offset);
return this.datePipe.transform(d, 'EEEE') || '';
}
return `Vor ${this.offset} Tagen`;
} }
} }
protected offsetMonthTitle(): string {
const d = new Date(this.dateService.now);
d.setMonth(d.getMonth() - this.offsetMonth);
return this.datePipe.transform(d, 'yyyy MMMM') || '';
}
} }

View File

@ -14,10 +14,6 @@
<div class="SectionBody COLOR_FONT_PURCHASE"> <div class="SectionBody COLOR_FONT_PURCHASE">
{{ purchase.toValueString(null) }} {{ purchase.toValueString(null) }}
</div> </div>
<div class="SectionBody COLOR_FONT_PURCHASE percent">
{{ purchasePercentConsume.toValueString(null) }}
<sub class="subscript">Verbrauch</sub>
</div>
</div> </div>
<div class="Section4"> <div class="Section4">
@ -27,27 +23,6 @@
<div class="SectionBody COLOR_FONT_PRODUCE"> <div class="SectionBody COLOR_FONT_PRODUCE">
{{ produce.toValueString(null) }} {{ produce.toValueString(null) }}
</div> </div>
<div class="SectionBody COLOR_FONT_PRODUCE percent">
{{ producePercentConsume.toValueString(null) }}
<sub class="subscript">Verbrauch</sub>
</div>
</div>
<div class="Section4">
<div class="SectionHeadingText">
Selbst
</div>
<div class="SectionBody COLOR_FONT_SELF">
{{ self.toValueString(null) }}
</div>
<div class="SectionBody COLOR_FONT_SELF percent">
{{ selfPercentConsume.toValueString(null) }}
<sub class="subscript">Verbrauch</sub>
</div>
<div class="SectionBody COLOR_FONT_SELF percent">
{{ selfPercentProduce.toValueString(null) }}
<sub class="subscript">Produktion</sub>
</div>
</div> </div>
<div class="Section4"> <div class="Section4">
@ -66,14 +41,6 @@
<div class="SectionBody COLOR_FONT_DELIVER"> <div class="SectionBody COLOR_FONT_DELIVER">
{{ deliver.toValueString(null) }} {{ deliver.toValueString(null) }}
</div> </div>
<div class="SectionBody COLOR_FONT_DELIVER percent">
{{ deliveryPercentConsume.toValueString(null) }}
<sub class="subscript">Verbrauch</sub>
</div>
<div class="SectionBody COLOR_FONT_DELIVER percent">
{{ deliveryPercentProduce.toValueString(null) }}
<sub class="subscript">Produktion</sub>
</div>
</div> </div>
</div> </div>

View File

@ -31,20 +31,6 @@ export class LocationEnergy implements OnInit, AfterViewInit, OnDestroy {
protected consume: Value = Value.NULL; protected consume: Value = Value.NULL;
protected self: Value = Value.NULL;
protected purchasePercentConsume: Value = Value.NULL;
protected producePercentConsume: Value = Value.NULL;
protected deliveryPercentConsume: Value = Value.NULL;
protected deliveryPercentProduce: Value = Value.NULL;
protected selfPercentConsume: Value = Value.NULL;
protected selfPercentProduce: Value = Value.NULL;
@Input() @Input()
heading!: string; heading!: string;
@ -103,13 +89,6 @@ export class LocationEnergy implements OnInit, AfterViewInit, OnDestroy {
const callNextAndUpdateConsume = (value: Value) => { const callNextAndUpdateConsume = (value: Value) => {
next(value); next(value);
this.consume = this.purchase.plus(this.produce, true).minus(this.deliver, true); this.consume = this.purchase.plus(this.produce, true).minus(this.deliver, true);
this.self = this.produce.minus(this.deliver, true);
this.purchasePercentConsume = this.purchase.percent(this.consume, "%", 0);
this.producePercentConsume = this.produce.percent(this.consume, "%", 0);
this.deliveryPercentConsume = this.deliver.percent(this.consume, "%", 0);
this.deliveryPercentProduce = this.deliver.percent(this.produce, "%", 0);
this.selfPercentConsume = this.self.percent(this.consume, "%", 0);
this.selfPercentProduce = this.self.percent(this.produce, "%", 0);
}; };
if (fresh !== null && fresh !== undefined) { if (fresh !== null && fresh !== undefined) {
if (fresh.id !== series?.id) { if (fresh.id !== series?.id) {

View File

@ -2,8 +2,6 @@ export class EnergyPoint {
readonly epochSeconds: number; readonly epochSeconds: number;
readonly date: Date;
private _purchase: number | null = null; private _purchase: number | null = null;
private _produce: number | null = null; private _produce: number | null = null;
@ -53,7 +51,6 @@ export class EnergyPoint {
point: number[], point: number[],
) { ) {
this.epochSeconds = point[0]; this.epochSeconds = point[0];
this.date = new Date(this.epochSeconds * 1000);
} }
set deliver(value: number | null) { set deliver(value: number | null) {

View File

@ -1,42 +1,33 @@
<svg [attr.viewBox]="`0 0 ${widthPx} ${heightPx+1}`" [style.background-color]="'#eee'"> <svg [attr.viewBox]="`0 0 ${widthPx} ${heightPx}`" [style.background-color]="'#eee'">
@for (point of points; track point.epochSeconds) { @for (point of points; track point.epochSeconds) {
<g> <rect
<title> [attr.x]="(point.epochSeconds - xMin) * xFactor"
Bezug: {{ location.energyPurchase?.toValue(point.purchase, point.date)?.toValueString(null) }} [attr.y]="heightPx - 1 + yMinPx - point.getPurchaseY(yFactor)"
Solar: {{ location.energyProduce?.toValue(point.produce, point.date)?.toValueString(null) }} [attr.width]="xWidthPx"
Selbst: {{ location.energyPurchase?.toValue(point.self, point.date)?.toValueString(null) }} [attr.height]="point.getPurchaseH(yFactor)"
Einsp.: {{ location.energyDeliver?.toValue(point.deliver, point.date)?.toValueString(null) }} class="COLOR_BACK_PURCHASE"
Verbrauch: {{ location.energyPurchase?.toValue(point.consume, point.date)?.toValueString(null) }} ></rect>
</title> <rect
<rect [attr.x]="(point.epochSeconds - xMin) * xFactor"
[attr.x]="(point.epochSeconds - xMin) * xFactor" [attr.y]="heightPx - 1 + yMinPx - point.getSelfY(yFactor)"
[attr.y]="heightPx + yMinPx - point.getPurchaseY(yFactor)" [attr.width]="xWidthPx"
[attr.width]="xWidthPx" [attr.height]="point.getSelfH(yFactor)"
[attr.height]="point.getPurchaseH(yFactor)" class="COLOR_BACK_SELF"
class="COLOR_BACK_PURCHASE" ></rect>
></rect> <rect
<rect [attr.x]="(point.epochSeconds - xMin) * xFactor"
[attr.x]="(point.epochSeconds - xMin) * xFactor" [attr.y]="heightPx + yMinPx"
[attr.y]="heightPx + yMinPx - point.getSelfY(yFactor)" [attr.width]="xWidthPx"
[attr.width]="xWidthPx" [attr.height]="point.getDeliverH(yFactor)"
[attr.height]="point.getSelfH(yFactor)" class="COLOR_BACK_DELIVER"
class="COLOR_BACK_SELF" ></rect>
></rect> <line
<rect x1="0"
[attr.x]="(point.epochSeconds - xMin) * xFactor" [attr.y1]="heightPx - 1 + yMinPx"
[attr.y]="heightPx+1 + yMinPx" [attr.x2]="widthPx"
[attr.width]="xWidthPx" [attr.y2]="heightPx - 1 + yMinPx"
[attr.height]="point.getDeliverH(yFactor)" stroke="#aaaaaa"
class="COLOR_BACK_DELIVER" stroke-width="1"
></rect> ></line>
</g>
} }
<line
x1="0"
[attr.y1]="heightPx +0.5 + yMinPx"
[attr.x2]="widthPx"
[attr.y2]="heightPx +0.5 + yMinPx"
stroke="#aaaaaa"
stroke-width="1"
></line>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -1,5 +1 @@
@import "../../../../colors"; @import "../../../../colors";
g:hover {
stroke: black;
}

View File

@ -126,10 +126,6 @@ export class EnergyPlot implements OnInit, OnDestroy, AfterViewInit {
this.ngAfterViewInit(); this.ngAfterViewInit();
} }
get location(): Location {
return this._location;
}
@Input() @Input()
set interval(value: Interval) { set interval(value: Interval) {
this._interval = value; this._interval = value;

View File

@ -13,10 +13,6 @@
<div class="SectionBody COLOR_FONT_PURCHASE"> <div class="SectionBody COLOR_FONT_PURCHASE">
{{ location.powerPurchase?.value?.toValueString(dateService.now) }} {{ location.powerPurchase?.value?.toValueString(dateService.now) }}
</div> </div>
<div class="SectionBody COLOR_FONT_PURCHASE percent">
{{ location.powerPurchasePercentConsume.toValueString(dateService.now) }}
<sub class="subscript">Verbrauch</sub>
</div>
</div> </div>
<div class="Section4"> <div class="Section4">
@ -26,27 +22,6 @@
<div class="SectionBody COLOR_FONT_PRODUCE"> <div class="SectionBody COLOR_FONT_PRODUCE">
{{ location.powerProduce?.value?.toValueString(dateService.now) }} {{ location.powerProduce?.value?.toValueString(dateService.now) }}
</div> </div>
<div class="SectionBody COLOR_FONT_PRODUCE percent">
{{ location.powerProducePercentConsume.toValueString(dateService.now) }}
<sub class="subscript">Verbrauch</sub>
</div>
</div>
<div class="Section4">
<div class="SectionHeadingText">
Selbst
</div>
<div class="SectionBody COLOR_FONT_SELF">
{{ location.powerSelf?.toValueString(dateService.now) }}
</div>
<div class="SectionBody COLOR_FONT_SELF percent">
{{ location.powerSelfPercentConsume.toValueString(dateService.now) }}
<sub class="subscript">Verbrauch</sub>
</div>
<div class="SectionBody COLOR_FONT_SELF percent">
{{ location.powerSelfPercentProduce.toValueString(dateService.now) }}
<sub class="subscript">Produktion</sub>
</div>
</div> </div>
<div class="Section4"> <div class="Section4">
@ -65,14 +40,6 @@
<div class="SectionBody COLOR_FONT_DELIVER"> <div class="SectionBody COLOR_FONT_DELIVER">
{{ location.powerDeliver?.value?.toValueString(dateService.now) }} {{ location.powerDeliver?.value?.toValueString(dateService.now) }}
</div> </div>
<div class="SectionBody COLOR_FONT_DELIVER percent">
{{ location.powerDeliveryPercentConsume.toValueString(dateService.now) }}
<sub class="subscript">Verbrauch</sub>
</div>
<div class="SectionBody COLOR_FONT_DELIVER percent">
{{ location.powerDeliveryPercentProduce.toValueString(dateService.now) }}
<sub class="subscript">Produktion</sub>
</div>
</div> </div>
</div> </div>

View File

@ -8,7 +8,7 @@ export class Interval {
static readonly WEEK = new Interval('WEEK', this.HOUR); static readonly WEEK = new Interval('WEEK', this.HOUR);
static readonly MONTH = new Interval('MONTH', this.DAY); static readonly MONTH = new Interval('MONTH', this.HOUR);
static readonly YEAR = new Interval('YEAR', this.DAY); static readonly YEAR = new Interval('YEAR', this.DAY);

View File

@ -37,11 +37,4 @@ export class Series {
return this.id === other?.id; return this.id === other?.id;
} }
toValue(value: number | null | undefined, date: Date | null | undefined): Value {
if (value === null || value === undefined || date === null || date === undefined) {
return Value.NULL;
}
return Value.of(this, value, date);
}
} }

View File

@ -83,11 +83,11 @@ export class Value {
return ageSeconds > this.seconds * 2.1; return ageSeconds > this.seconds * 2.1;
} }
percent(other: Value | null | undefined, unit: string | null = null, precision: number | null = null): Value { onlyPositive(): Value {
if (other === null || other === undefined) { if (this.value < 0) {
return Value.NULL; return Value.ZERO;
} }
return new BiValue(this, other, (a, b) => a / b * 100, unit, precision); return this;
} }
} }
@ -98,18 +98,12 @@ export class BiValue extends Value {
readonly a: Value, readonly a: Value,
readonly b: Value, readonly b: Value,
readonly operation: (a: number, b: number) => number, readonly operation: (a: number, b: number) => number,
unit: string | null = null,
precision: number | null = null,
) { ) {
if (a.unit !== "" && b.unit !== "" && a.unit !== b.unit) { if (a.unit !== "" && b.unit !== "" && a.unit !== b.unit) {
throw new Error(`Operation needs units to be equal or empty: this=${a}, other=${b}`); throw new Error(`Operation needs units to be equal or empty: this=${a}, other=${b}`);
} }
if (precision === null) { const precision = Math.max(a.precision, b.precision);
precision = Math.max(a.precision, b.precision); const unit = a.unit || b.unit;
}
if (unit === null) {
unit = a.unit || b.unit;
}
const date = a.date.getTime() < b.date.getTime() ? a.date : b.date; const date = a.date.getTime() < b.date.getTime() ? a.date : b.date;
super(operation(a.value, b.value), precision, 0, unit, date); super(operation(a.value, b.value), precision, 0, unit, date);
} }

View File

@ -3,7 +3,7 @@
@COLOR_FONT_PURCHASE: red; @COLOR_FONT_PURCHASE: red;
@COLOR_FONT_DELIVER: magenta; @COLOR_FONT_DELIVER: magenta;
@COLOR_FONT_PRODUCE: #0095ff; @COLOR_FONT_PRODUCE: #0095ff;
@COLOR_FONT_SELF: #00b34a; @COLOR_FONT_SELF: #0095ff;
@COLOR_FONT_CONSUME: #ff8800; @COLOR_FONT_CONSUME: #ff8800;
@COLOR_BACK_PURCHASE: #ffa7a7; @COLOR_BACK_PURCHASE: #ffa7a7;
@ -28,10 +28,6 @@
color: @COLOR_FONT_SELF; color: @COLOR_FONT_SELF;
} }
.COLOR_FONT_SELF {
color: @COLOR_FONT_SELF;
}
.COLOR_FONT_CONSUME { .COLOR_FONT_CONSUME {
color: @COLOR_FONT_CONSUME; color: @COLOR_FONT_CONSUME;
} }

View File

@ -3,7 +3,7 @@ body {
height: 100%; height: 100%;
margin: 0; margin: 0;
font-family: sans-serif; font-family: sans-serif;
font-size: 3.5vw; font-size: 4vw;
user-select: none; user-select: none;
} }
@ -99,11 +99,3 @@ div {
} }
} }
.percent {
font-size: 70%;
.subscript {
font-size: 60%;
}
}