Skip to content

Commit e5574f1

Browse files
authored
feat: elevation-limit-aware Sun Today chart + show/hide toggles (#46) (#113)
1 parent f12a8fb commit e5574f1

23 files changed

Lines changed: 617 additions & 53 deletions

dist/adaptive-cover-pro-card.js

Lines changed: 70 additions & 48 deletions
Large diffs are not rendered by default.

harness/src/card-stage.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ export class AcpHarnessCardStage extends LitElement {
136136
show_sunrise_sunset: c.show_sunrise_sunset,
137137
show_cover_fill: c.show_cover_fill,
138138
show_window_arrow: c.show_window_arrow,
139+
show_elevation_chart: c.show_elevation_chart,
139140
cover_colors: this.config.entries.map((e) => e.color),
140141
north_offset: c.north_offset,
141142
};
@@ -158,6 +159,7 @@ export class AcpHarnessCardStage extends LitElement {
158159
show_badge: t.show_badge,
159160
...(Object.keys(badges).length > 0 ? { badges } : {}),
160161
show_compass: t.show_compass,
162+
show_elevation_chart: t.show_elevation_chart,
161163
show_motion_icon: t.show_motion_icon,
162164
layout: t.layout,
163165
};

harness/src/control-panel.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,7 @@ export class AcpHarnessControlPanel extends LitElement {
754754
'show_sunrise_sunset',
755755
'show_cover_fill',
756756
'show_window_arrow',
757+
'show_elevation_chart',
757758
] as const
758759
).map((k) =>
759760
this._checkbox(k, this.config.compass[k], (v) =>
@@ -784,6 +785,7 @@ export class AcpHarnessControlPanel extends LitElement {
784785
'show_controls',
785786
'show_badge',
786787
'show_compass',
788+
'show_elevation_chart',
787789
'show_motion_icon',
788790
] as const
789791
).map((k) =>

harness/src/scenarios.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ function defaultCompass(): HarnessConfig['compass'] {
3535
show_sunrise_sunset: true,
3636
show_cover_fill: true,
3737
show_window_arrow: true,
38+
show_elevation_chart: true,
3839
north_offset: 0,
3940
};
4041
}
@@ -65,6 +66,7 @@ function defaultTile(): HarnessConfig['tile'] {
6566
show_badge: true,
6667
badges: defaultBadges(),
6768
show_compass: true,
69+
show_elevation_chart: true,
6870
show_motion_icon: true,
6971
layout: 'detailed',
7072
};
@@ -468,6 +470,7 @@ export const SCENARIOS: Scenario[] = [
468470
title: 'Living Room',
469471
window_azimuth: 180,
470472
min_elevation: 10,
473+
max_elevation: 70,
471474
color: '#ff7043',
472475
}),
473476
makeEntry({

harness/src/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ export interface SkyCompassCardOptions {
103103
show_sunrise_sunset: boolean;
104104
show_cover_fill: boolean;
105105
show_window_arrow: boolean;
106+
show_elevation_chart: boolean;
106107
north_offset: number;
107108
}
108109

@@ -127,6 +128,7 @@ export interface TileCardOptions {
127128
cloud: boolean;
128129
};
129130
show_compass: boolean;
131+
show_elevation_chart: boolean;
130132
show_motion_icon: boolean;
131133
layout: 'one-line' | 'detailed';
132134
}

src/adaptive-cover-pro-sky-compass-card-editor.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ type ToggleKey =
1919
| 'show_sun_path'
2020
| 'show_sunrise_sunset'
2121
| 'show_cover_fill'
22-
| 'show_window_arrow';
22+
| 'show_window_arrow'
23+
| 'show_elevation_chart';
2324

2425
interface ToggleRow {
2526
key: ToggleKey;
@@ -89,6 +90,12 @@ const TOGGLE_ROWS: ToggleRow[] = [
8990
descKey: 'editor.compass.toggle_window_arrow_desc',
9091
defaultOn: true,
9192
},
93+
{
94+
key: 'show_elevation_chart',
95+
labelKey: 'editor.compass.toggle_elevation_chart_label',
96+
descKey: 'editor.compass.toggle_elevation_chart_desc',
97+
defaultOn: true,
98+
},
9299
];
93100

94101
@customElement(SKY_COMPASS_CARD_EDITOR_NAME)

src/adaptive-cover-pro-sky-compass-card.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
import type { DiscoveredEntities, SkyCompassCardConfig } from './types';
1515

1616
import './components/sky-compass';
17+
import './components/elevation-chart';
1718
import './adaptive-cover-pro-sky-compass-card-editor';
1819

1920
@customElement(SKY_COMPASS_CARD_NAME)
@@ -156,6 +157,13 @@ export class AdaptiveCoverProSkyCompassCard extends LitElement {
156157
.coverColors=${cfg.cover_colors ?? []}
157158
.northOffsetDeg=${normalizeAzimuth(cfg.north_offset ?? 0)}
158159
></acp-sky-compass>
160+
${cfg.show_elevation_chart !== false
161+
? html`<acp-elevation-chart
162+
.hass=${this.hass}
163+
.discovered=${discoveredList[0]}
164+
?compact=${!!cfg.compact}
165+
></acp-elevation-chart>`
166+
: nothing}
159167
${missing.length > 0
160168
? html`<div class="warn dim">
161169
${t('root.compass_not_found', this.hass, { entries: missing.join(', ') })}

src/adaptive-cover-pro-tile-card-editor.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ const FORM_DEFAULTS = {
5757
show_controls: true,
5858
show_badge: true,
5959
show_compass: true,
60+
show_elevation_chart: true,
6061
show_motion_icon: true,
6162
layout: 'detailed',
6263
// All badges default on; only `=== false` hides.
@@ -95,6 +96,7 @@ const LABEL_KEYS: Record<string, string> = {
9596
badge_glare_zone: 'editor.tile.badge_glare_zone',
9697
badge_cloud: 'editor.tile.badge_cloud',
9798
show_compass: 'editor.tile.show_compass',
99+
show_elevation_chart: 'editor.tile.show_elevation_chart',
98100
show_motion_icon: 'editor.tile.show_motion_icon',
99101
tap_action: 'editor.tile.tap_action',
100102
hold_action: 'editor.tile.hold_action',
@@ -382,6 +384,7 @@ export class AdaptiveCoverProTileCardEditor extends LitElement implements Lovela
382384
},
383385
{ name: 'show_motion_icon', selector: { boolean: {} } },
384386
{ name: 'show_compass', selector: { boolean: {} } },
387+
{ name: 'show_elevation_chart', selector: { boolean: {} } },
385388
{ name: 'tap_action', selector: { ui_action: {} } },
386389
{ name: 'hold_action', selector: { ui_action: {} } },
387390
{ name: 'double_tap_action', selector: { ui_action: {} } },

src/adaptive-cover-pro-tile-card.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ export class AdaptiveCoverProTileCard extends LitElement {
175175
.discovered=${discovered}
176176
.open=${this._dialogOpen}
177177
.showCompass=${this._config.show_compass !== false}
178+
.showElevationChart=${this._config.show_elevation_chart !== false}
178179
.badges=${this._config.badges}
179180
@acp-dialog-close=${this._closeDialog}
180181
></acp-more-info-dialog>

src/components/elevation-chart.ts

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { HomeAssistant } from 'custom-card-helpers';
44

55
import type { DiscoveredEntities, SunPositionAttributes } from '../types';
66
import { findFovWindows, sampleDay, startOfDayInZone, type SunSample } from '../lib/sun-model';
7+
import { elevationBandFraction } from '../lib/geometry';
78
import { formatClock } from '../lib/formatters';
89
import { t } from '../lib/i18n';
910

@@ -31,7 +32,7 @@ export class ElevationChart extends LitElement {
3132
protected render(): TemplateResult | typeof nothing {
3233
if (!this.hass || !this.discovered) return nothing;
3334
const attrs = this._sunAttrs();
34-
const { latitude, longitude, time_zone } = this.hass.config as unknown as {
35+
const { latitude, longitude, time_zone } = (this.hass.config ?? {}) as unknown as {
3536
latitude?: number;
3637
longitude?: number;
3738
time_zone?: string;
@@ -72,6 +73,27 @@ export class ElevationChart extends LitElement {
7273
const currentSample = this._interpAt(samples, now);
7374
const currentY = currentSample ? yAt(currentSample.elevation) : null;
7475

76+
// Elevation limits (optional integration attrs). When present, the FOV
77+
// time-bands are clipped to the in-band elevation range and horizontal
78+
// limit gridlines are drawn. Graceful no-op when both are absent.
79+
const hasMin = typeof attrs.min_elevation === 'number';
80+
const hasMax = typeof attrs.max_elevation === 'number';
81+
const plotTop = PAD_T;
82+
const plotBottom = VIEWBOX_H - PAD_B;
83+
// loFrac → lower elevation (axisMin side, bottom of plot); hiFrac → upper.
84+
const { loFrac, hiFrac } = elevationBandFraction(
85+
attrs.min_elevation,
86+
attrs.max_elevation,
87+
minElev,
88+
maxElev,
89+
);
90+
// Map a 0..1 elevation-axis fraction to a y coordinate (0 = bottom).
91+
const yForFrac = (frac: number): number => plotBottom - frac * (plotBottom - plotTop);
92+
const bandTopY = yForFrac(hiFrac);
93+
const bandBottomY = yForFrac(loFrac);
94+
const bandY = hasMin || hasMax ? bandTopY : plotTop;
95+
const bandHeight = hasMin || hasMax ? bandBottomY - bandTopY : plotBottom - plotTop;
96+
7597
const fovBands = fovWindows.map((w) => ({
7698
x0: xAt(samples[w.startIdx].t),
7799
x1: xAt(samples[w.endIdx].t),
@@ -117,14 +139,27 @@ export class ElevationChart extends LitElement {
117139
<!-- horizon -->
118140
<line class="horizon" x1=${PAD_L} y1=${horizonY} x2=${VIEWBOX_W - PAD_R} y2=${horizonY} />
119141
120-
<!-- FOV shaded bands (each time the sun is actually in FOV + above horizon) -->
142+
<!-- elevation limit gridlines (drawn only for limits actually set) -->
143+
${
144+
hasMin
145+
? svg`<line class="limit-line" x1=${PAD_L} y1=${bandBottomY} x2=${VIEWBOX_W - PAD_R} y2=${bandBottomY} />`
146+
: nothing
147+
}
148+
${
149+
hasMax
150+
? svg`<line class="limit-line" x1=${PAD_L} y1=${bandTopY} x2=${VIEWBOX_W - PAD_R} y2=${bandTopY} />`
151+
: nothing
152+
}
153+
154+
<!-- FOV shaded bands (each time the sun is actually in FOV + above horizon),
155+
clipped to the in-band elevation range when limits are present -->
121156
${fovBands.map(
122157
(b) => svg`<rect
123158
class="fov-band"
124159
x=${b.x0}
125-
y=${PAD_T}
160+
y=${bandY}
126161
width=${b.x1 - b.x0}
127-
height=${VIEWBOX_H - PAD_T - PAD_B}
162+
height=${bandHeight}
128163
/>`,
129164
)}
130165
@@ -215,6 +250,12 @@ export class ElevationChart extends LitElement {
215250
stroke-width: 1;
216251
stroke-dasharray: 2 2;
217252
}
253+
.limit-line {
254+
stroke: var(--warning-color, gold);
255+
stroke-width: 1;
256+
stroke-dasharray: 4 3;
257+
opacity: 0.7;
258+
}
218259
.fov-band {
219260
fill: var(--warning-color, gold);
220261
fill-opacity: 0.18;

0 commit comments

Comments
 (0)